MythTV  0.26-pre
mtv_api.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # -*- coding: UTF-8 -*-
00003 # ----------------------
00004 # Name: mtv_api - Simple-to-use Python interface to the MTV API (http://www.mtv.com/)
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 #           metadata, video and image URLs from MTV. These routines are based on the api. Specifications
00009 #           for this api are published at http://developer.mtvnservices.com/docs
00010 #
00011 # License:Creative Commons GNU GPL v2
00012 # (http://creativecommons.org/licenses/GPL/2.0/)
00013 #-------------------------------------
00014 __title__ ="mtv_api - Simple-to-use Python interface to the MTV API (http://developer.mtvnservices.com/docs)"
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 metadata, video and image URLs from MTV. These routines are based on the api. Specifications
00019 for this api are published at http://developer.mtvnservices.com/docs
00020 '''
00021 
00022 __version__="v0.2.5"
00023 # 0.1.0 Initial development
00024 # 0.1.1 Added Tree View Processing
00025 # 0.1.2 Modified Reee view code and structure to be standandized across all grabbers
00026 # 0.1.3 Added directory image access and display
00027 # 0.1.4 Documentation review
00028 # 0.2.0 Public release
00029 # 0.2.1 New python bindings conversion
00030 #       Better exception error reporting
00031 #       Better handling of invalid unicode data from source
00032 # 0.2.2 Complete abort error message display improvements
00033 #       Removed the import and use of the feedparser library
00034 # 0.2.3 Fixed an exception message output code error in two places
00035 # 0.2.4 Removed the need for python MythTV bindings and added "%SHAREDIR%" to icon directory path
00036 # 0.2.5 Use MTV web page as API not returning valid URLs
00037 
00038 import os, struct, sys, re, time
00039 from datetime import datetime, timedelta
00040 import urllib, urllib2
00041 import logging
00042 
00043 try:
00044     import xml.etree.cElementTree as ElementTree
00045 except ImportError:
00046     import xml.etree.ElementTree as ElementTree
00047 
00048 from mtv_exceptions import (MtvUrlError, MtvHttpError, MtvRssError, MtvVideoNotFound, MtvInvalidSearchType, MtvXmlError, MtvVideoDetailError)
00049 
00050 class OutStreamEncoder(object):
00051     """Wraps a stream with an encoder"""
00052     def __init__(self, outstream, encoding=None):
00053         self.out = outstream
00054         if not encoding:
00055             self.encoding = sys.getfilesystemencoding()
00056         else:
00057             self.encoding = encoding
00058 
00059     def write(self, obj):
00060         """Wraps the output stream, encoding Unicode strings with the specified encoding"""
00061         if isinstance(obj, unicode):
00062             try:
00063                 self.out.write(obj.encode(self.encoding))
00064             except IOError:
00065                 pass
00066         else:
00067             try:
00068                 self.out.write(obj)
00069             except IOError:
00070                 pass
00071 
00072     def __getattr__(self, attr):
00073         """Delegate everything but write to the stream"""
00074         return getattr(self.out, attr)
00075 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
00076 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
00077 
00078 
00079 class XmlHandler:
00080     """Deals with retrieval of XML files from API
00081     """
00082     def __init__(self, url):
00083         self.url = url
00084 
00085     def _grabUrl(self, url):
00086         try:
00087             urlhandle = urllib.urlopen(url)
00088         except IOError, errormsg:
00089             raise MtvHttpError(errormsg)
00090         return urlhandle.read()
00091 
00092     def getEt(self):
00093         xml = self._grabUrl(self.url)
00094         try:
00095             et = ElementTree.fromstring(xml)
00096         except SyntaxError, errormsg:
00097             raise MtvXmlError(errormsg)
00098         return et
00099 
00100 
00101 class Videos(object):
00102     """Main interface to http://www.mtv.com/
00103     This is done to support a common naming framework for all python Netvision plugins no matter their site
00104     target.
00105 
00106     Supports search methods
00107     The apikey is a not required to access http://www.mtv.com/
00108     """
00109     def __init__(self,
00110                 apikey,
00111                 mythtv = True,
00112                 interactive = False,
00113                 select_first = False,
00114                 debug = False,
00115                 custom_ui = None,
00116                 language = None,
00117                 search_all_languages = False,
00118                 ):
00119         """apikey (str/unicode):
00120             Specify the target site API key. Applications need their own key in some cases
00121 
00122         mythtv (True/False):
00123             When True, the returned meta data is being returned has the key and values massaged to match MythTV
00124             When False, the returned meta data  is being returned matches what target site returned
00125 
00126         interactive (True/False): (This option is not supported by all target site apis)
00127             When True, uses built-in console UI is used to select the correct show.
00128             When False, the first search result is used.
00129 
00130         select_first (True/False): (This option is not supported currently implemented in any grabbers)
00131             Automatically selects the first series search result (rather
00132             than showing the user a list of more than one series).
00133             Is overridden by interactive = False, or specifying a custom_ui
00134 
00135         debug (True/False):
00136              shows verbose debugging information
00137 
00138         custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
00139             A callable subclass of interactive class (overrides interactive option)
00140 
00141         language (2 character language abbreviation): (This option is not supported by all target site apis)
00142             The language of the returned data. Is also the language search
00143             uses. Default is "en" (English). For full list, run..
00144 
00145         search_all_languages (True/False): (This option is not supported by all target site apis)
00146             By default, a Netvision grabber will only search in the language specified using
00147             the language option. When this is True, it will search for the
00148             show in any language
00149 
00150         """
00151         self.config = {}
00152 
00153         if apikey is not None:
00154             self.config['apikey'] = apikey
00155         else:
00156             pass    # MTV does not require an apikey
00157 
00158         self.config['debug_enabled'] = debug # show debugging messages
00159 
00160         self.log_name = "MTV"
00161         self.log = self._initLogger() # Setups the logger (self.log.debug() etc)
00162 
00163         self.config['custom_ui'] = custom_ui
00164 
00165         self.config['interactive'] = interactive # prompt for correct series?
00166 
00167         self.config['select_first'] = select_first
00168 
00169         self.config['search_all_languages'] = search_all_languages
00170 
00171         # Defaulting to ENGISH but the MTV apis do not support specifying a language
00172         self.config['language'] = "en"
00173 
00174         self.error_messages = {'MtvUrlError': u"! Error: The URL (%s) cause the exception error (%s)\n", 'MtvHttpError': u"! Error: An HTTP communications error with MTV was raised (%s)\n", 'MtvRssError': u"! Error: Invalid RSS metadata\nwas received from MTV error (%s). Skipping item.\n", 'MtvVideoNotFound': u"! Error: Video search with MTV did not return any results (%s)\n", 'MtvVideoDetailError': u"! Error: Invalid Video metadata detail\nwas received from MTV error (%s). Skipping item.\n", }
00175 
00176         # This is an example that must be customized for each target site
00177         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', 'media_credit': 'item_author', 'published_parsed': 'item_pubdate', 'media_description': 'item_description', 'video': 'item_link', 'thumbnail': 'item_thumbnail', 'link': 'item_url', 'duration': 'item_duration', 'item_rating': 'item_rating', 'item_width': 'item_width', 'item_height': 'item_height', 'language': 'item_lang'}]
00178 
00179         self.config[u'urls'] = {}
00180 
00181         self.config[u'image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
00182         self.config[u'urls'] = {}
00183 
00184         # Functions that parse video data from RSS data
00185         self.config['item_parser'] = {}
00186         self.config['item_parser']['main'] = self.getVideosForURL
00187 
00188         # v2 api calls - An example that must be customized for each target site
00189         self.config[u'urls'][u'tree.view'] = {
00190             'new_genres': {
00191                 '__all__': ['http://api.mtvnservices.com/1/genre/%s/videos/?', 'main'],
00192                 },
00193             'genres': {
00194                 '__all__': ['http://api.mtvnservices.com/1/genre/%s/videos/?', 'main'],
00195                 },
00196             }
00197 
00198         self.tree_order = ['new_genres', 'genres', ]
00199         self.tree_org = {
00200             'new_genres': [['New over the last 3 months', ['pop', 'rock', 'metal', 'randb', 'jazz', 'blues_folk', 'country', 'latin', 'hip_hop', 'world_reggae', 'electronic_dance', 'easy_listening', 'classical', 'soundtracks_musicals', 'alternative', 'environmental', ]],
00201             ],
00202             'genres': [['All Genres', ['pop', 'rock', 'metal', 'randb', 'jazz', 'blues_folk', 'country', 'latin', 'hip_hop', 'world_reggae', 'electronic_dance', 'easy_listening', 'classical', 'soundtracks_musicals', 'alternative', 'environmental', ]],
00203             ],
00204             }
00205 
00206         # Time periods of videos e.g, &date=01011980-12311989 or MMDDYYYY-MMDDYYYY
00207         d1 = datetime.now()
00208         yr = d1 - timedelta(weeks=52)
00209         mts = d1 - timedelta(days=93)
00210         last_3_months = u'%s-%s' % (mts.strftime('%m%d%Y'), d1.strftime('%m%d%Y'))
00211         last_year = u'%s-%s' % (yr.strftime('%m%d%Y'), d1.strftime('%m%d%Y'))
00212 
00213         # http://api.mtvnservices.com/1/genre/rock/videos/?&max-results=20&start-index=1
00214         self.tree_customize = {
00215             'new_genres': { # ?term=%s&start-index=%s&max-results=%s
00216                 '__default__': {'max-results': '20', 'start-index': '1', 'date': last_3_months, 'sort': 'date_descending'},
00217                 #'cat name': {'max-results': '', 'start-index': '', 'date': '', 'sort': ''},
00218             },
00219             'genres': { # ?term=%s&start-index=%s&max-results=%s
00220                 '__default__': {'max-results': '20', 'start-index': '1', 'sort': 'date_descending'},
00221                 #'cat name': {'max-results': '', 'start-index': '', 'date': '', 'sort': ''},
00222                 'rock': {'date': last_year, },
00223                 'R&B': {'date': last_year, },
00224                 'country': {'date': last_year, },
00225                 'hip_hop': {'date': last_year, },
00226             },
00227             }
00228 
00229         self.feed_names = {
00230             'new_genres': {'world_reggae': 'World/Reggae', 'pop': 'Pop', 'metal': 'Metal', 'environmental': 'Environmental', 'latin': 'Latin', 'randb': 'R&B', 'rock': 'Rock', 'easy_listening': 'Easy Listening', 'jazz': 'Jazz', 'country': 'Country', 'hip_hop': 'Hip-Hop', 'classical': 'Classical', 'electronic_dance': 'Electro / Dance', 'blues_folk': 'Blues / Folk', 'alternative': 'Alternative', 'soundtracks_musicals': 'Soundtracks / Musicals', 'New over the last 3 months': 'directories/topics/month'
00231             },
00232             'genres': {'world_reggae': 'World/Reggae', 'pop': 'Pop', 'metal': 'Metal', 'environmental': 'Environmental', 'latin': 'Latin', 'randb': 'R&B', 'rock': 'Rock', 'easy_listening': 'Easy Listening', 'jazz': 'Jazz', 'country': 'Country', 'hip_hop': 'Hip-Hop', 'classical': 'Classical', 'electronic_dance': 'Electro / Dance', 'blues_folk': 'Blues / Folk', 'alternative': 'Alternative', 'soundtracks_musicals': 'Soundtracks / Musicals',
00233             },
00234             }
00235 
00236         self.feed_icons = {
00237             'new_genres': {'New over the last 3 months': 'directories/topics/recent', 'world_reggae': 'directories/music_genres/world_reggae', 'pop': 'directories/music_genres/pop', 'metal': 'directories/music_genres/metal', 'environmental': 'directories/music_genres/environmental', 'latin': 'directories/music_genres/latino', 'randb': 'directories/music_genres/rnb', 'rock': 'directories/music_genres/rock', 'easy_listening': 'directories/music_genres/easy_listening', 'jazz': 'directories/music_genres/jazz', 'country': 'directories/music_genres/country', 'hip_hop': 'directories/music_genres/hiphop', 'classical': 'directories/music_genres/classical', 'electronic_dance': 'directories/music_genres/electronic_dance', 'blues_folk': 'directories/music_genres/blues_folk', 'alternative': 'directories/music_genres/alternative', 'soundtracks_musicals': 'directories/music_genres/soundtracks_musicals',
00238             },
00239             'genres': {'Genres': 'directories/topics/music','world_reggae': 'directories/music_genres/world_reggae', 'pop': 'directories/music_genres/pop', 'metal': 'directories/music_genres/metal', 'environmental': 'directories/music_genres/environmental', 'latin': 'directories/music_genres/latino', 'randb': 'directories/music_genres/rnb', 'rock': 'directories/music_genres/rock', 'easy_listening': 'directories/music_genres/easy_listening', 'jazz': 'directories/music_genres/jazz', 'country': 'directories/music_genres/country', 'hip_hop': 'directories/music_genres/hiphop', 'classical': 'directories/music_genres/classical', 'electronic_dance': 'directories/music_genres/electronic_dance', 'blues_folk': 'directories/music_genres/blues_folk', 'alternative': 'directories/music_genres/alternative', 'soundtracks_musicals': 'directories/music_genres/soundtracks_musicals',
00240             },
00241             }
00242         # Get the absolute path to the mtv.html file
00243         self.mtvHtmlPath = u'file://'+os.path.dirname( os.path.realpath( __file__ )).replace(u'/nv_python_libs/mtv', u'/nv_python_libs/configs/HTML/mtv.html?title=%s&videocode=%s')
00244 
00245         # Initialize the tree view flag so that the item parsing code can be used for multiple purposes
00246         self.treeview = False
00247         self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/mtv.png'
00248     # end __init__()
00249 
00250 ###########################################################################################################
00251 #
00252 # Start - Utility functions
00253 #
00254 ###########################################################################################################
00255 
00256     def massageDescription(self, text):
00257         '''Removes HTML markup from a text string.
00258         @param text The HTML source.
00259         @return The plain text.  If the HTML source contains non-ASCII
00260         entities or character references, this is a Unicode string.
00261         '''
00262         def fixup(m):
00263             text = m.group(0)
00264             if text[:1] == "<":
00265                 return "" # ignore tags
00266             if text[:2] == "&#":
00267                 try:
00268                     if text[:3] == "&#x":
00269                         return unichr(int(text[3:-1], 16))
00270                     else:
00271                         return unichr(int(text[2:-1]))
00272                 except ValueError:
00273                     pass
00274             elif text[:1] == "&":
00275                 import htmlentitydefs
00276                 entity = htmlentitydefs.entitydefs.get(text[1:-1])
00277                 if entity:
00278                     if entity[:2] == "&#":
00279                         try:
00280                             return unichr(int(entity[2:-1]))
00281                         except ValueError:
00282                             pass
00283                     else:
00284                         return unicode(entity, "iso-8859-1")
00285             return text # leave as is
00286         return self.ampReplace(re.sub(u"(?s)<[^>]*>|&#?\w+;", fixup, self.textUtf8(text))).replace(u'\n',u' ')
00287     # end massageDescription()
00288 
00289 
00290     def _initLogger(self):
00291         """Setups a logger using the logging module, returns a log object
00292         """
00293         logger = logging.getLogger(self.log_name)
00294         formatter = logging.Formatter('%(asctime)s) %(levelname)s %(message)s')
00295 
00296         hdlr = logging.StreamHandler(sys.stdout)
00297 
00298         hdlr.setFormatter(formatter)
00299         logger.addHandler(hdlr)
00300 
00301         if self.config['debug_enabled']:
00302             logger.setLevel(logging.DEBUG)
00303         else:
00304             logger.setLevel(logging.WARNING)
00305         return logger
00306     #end initLogger
00307 
00308 
00309     def textUtf8(self, text):
00310         if text == None:
00311             return text
00312         try:
00313             return unicode(text, 'utf8')
00314         except UnicodeDecodeError:
00315             return u''
00316         except (UnicodeEncodeError, TypeError):
00317             return text
00318     # end textUtf8()
00319 
00320 
00321     def ampReplace(self, text):
00322         '''Replace all "&" characters with "&amp;"
00323         '''
00324         text = self.textUtf8(text)
00325         return text.replace(u'&amp;',u'~~~~~').replace(u'&',u'&amp;').replace(u'~~~~~', u'&amp;')
00326     # end ampReplace()
00327 
00328 
00329     def setTreeViewIcon(self, dir_icon=None):
00330         '''Check if there is a specific generic tree view icon. If not default to the channel icon.
00331         return self.tree_dir_icon
00332         '''
00333         self.tree_dir_icon = self.channel_icon
00334         if not dir_icon:
00335             if not self.feed_icons.has_key(self.tree_key):
00336                 return self.tree_dir_icon
00337             if not self.feed_icons[self.tree_key].has_key(self.feed):
00338                 return self.tree_dir_icon
00339             dir_icon = self.feed_icons[self.tree_key][self.feed]
00340             if not dir_icon:
00341                 return self.tree_dir_icon
00342         self.tree_dir_icon = u'%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, )
00343         return self.tree_dir_icon
00344     # end setTreeViewIcon()
00345 
00346 ###########################################################################################################
00347 #
00348 # End of Utility functions
00349 #
00350 ###########################################################################################################
00351 
00352 
00353     def searchTitle(self, title, pagenumber, pagelen):
00354         '''Key word video search of the MTV web site
00355         return an array of matching item dictionaries
00356         return
00357         '''
00358         url = self.config[u'urls'][u'video.search'] % (urllib.quote_plus(title.encode("utf-8")), pagenumber , pagelen,)
00359         if self.config['debug_enabled']:
00360             print url
00361             print
00362 
00363         try:
00364             etree = XmlHandler(url).getEt()
00365         except Exception, errormsg:
00366             raise MtvUrlError(self.error_messages['MtvUrlError'] % (url, errormsg))
00367 
00368         if etree is None:
00369             raise MtvVideoNotFound(u"No MTV Video matches found for search value (%s)" % title)
00370 
00371         data = []
00372         for entry in etree:
00373             if not entry.tag.endswith('entry'):
00374                 continue
00375             item = {}
00376             for parts in entry:
00377                 if parts.tag.endswith('id'):
00378                     item['id'] = parts.text
00379                 if parts.tag.endswith('title'):
00380                     item['title'] = parts.text
00381                 if parts.tag.endswith('author'):
00382                     for e in parts:
00383                         if e.tag.endswith('name'):
00384                             item['media_credit'] = e.text
00385                             break
00386                 if parts.tag.endswith('published'):
00387                     item['published_parsed'] = parts.text
00388                 if parts.tag.endswith('description'):
00389                     item['media_description'] = parts.text
00390             data.append(item)
00391 
00392         # Make sure there are no item elements that are None
00393         for item in data:
00394             for key in item.keys():
00395                 if item[key] == None:
00396                     item[key] = u''
00397 
00398         # Massage each field and eliminate any item without a URL
00399         elements_final = []
00400         for item in data:
00401             if not 'id' in item.keys():
00402                 continue
00403 
00404             video_details = None
00405             try:
00406                 video_details = self.videoDetails(item['id'], urllib.quote(item['title'].encode("utf-8")))
00407             except MtvUrlError, msg:
00408                 sys.stderr.write(self.error_messages['MtvUrlError'] % msg)
00409             except MtvVideoDetailError, msg:
00410                 sys.stderr.write(self.error_messages['MtvVideoDetailError'] % msg)
00411             except Exception, e:
00412                 sys.stderr.write(u"! Error: Unknown error while retrieving a Video's meta data. Skipping video.' (%s)\nError(%s)\n" % (title, e))
00413 
00414             if video_details:
00415                 for key in video_details.keys():
00416                     item[key] = video_details[key]
00417 
00418             item['language'] = u''
00419             for key in item.keys():
00420                 if key == 'content':
00421                     if len(item[key]):
00422                         if item[key][0].has_key('language'):
00423                             if item[key][0]['language'] != None:
00424                                 item['language'] = item[key][0]['language']
00425                 if key == 'published_parsed': # '2009-12-21T00:00:00Z'
00426                     if item[key]:
00427                         pub_time = time.strptime(item[key].strip(), "%Y-%m-%dT%H:%M:%SZ")
00428                         item[key] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', pub_time)
00429                     continue
00430                 if key == 'media_description' or key == 'title':
00431                     # Strip the HTML tags
00432                     if item[key]:
00433                         item[key] = self.massageDescription(item[key].strip())
00434                         item[key] = item[key].replace(u'|', u'-')
00435                     continue
00436                 if type(item[key]) == type(u''):
00437                     if item[key]:
00438                         item[key] = item[key].replace('"\n',' ').strip()
00439             elements_final.append(item)
00440 
00441         if not len(elements_final):
00442             raise MtvVideoNotFound(u"No MTV Video matches found for search value (%s)" % title)
00443 
00444         return elements_final
00445         # end searchTitle()
00446 
00447 
00448     def videoDetails(self, url, title=u''):
00449         '''Using the passed URL retrieve the video meta data details
00450         return a dictionary of video metadata details
00451         return
00452         '''
00453         if self.config['debug_enabled']:
00454             print url
00455             print
00456 
00457         try:
00458             etree = XmlHandler(url).getEt()
00459         except Exception, errormsg:
00460             raise MtvUrlError(self.error_messages['MtvUrlError'] % (url, errormsg))
00461 
00462         if etree is None:
00463             raise MtvVideoDetailError(u'1-No Video meta data for (%s)' % url)
00464 
00465         metadata = {}
00466         cur_size = True
00467         for e in etree:
00468             if e.tag.endswith(u'content') and e.text == None:
00469                 index = e.get('url').rindex(u':')
00470                 metadata['video'] = self.mtvHtmlPath % (title,  e.get('url')[index+1:])
00471                 # !! This tag will need to be added at a later date
00472 #                metadata['customhtml'] = u'true'
00473                 metadata['duration'] =  e.get('duration')
00474             if e.tag.endswith(u'player'):
00475                 metadata['link'] = e.get('url')
00476             if e.tag.endswith(u'thumbnail'):
00477                 if cur_size == False:
00478                     continue
00479                 height = e.get('height')
00480                 width = e.get('width')
00481                 if int(width) > cur_size:
00482                     metadata['thumbnail'] = e.get('url')
00483                     cur_size = int(width)
00484                 if int(width) >= 200:
00485                     cur_size = False
00486                     break
00487 
00488         if not len(metadata):
00489             raise MtvVideoDetailError(u'2-No Video meta data for (%s)' % url)
00490 
00491         if not metadata.has_key('video'):
00492             metadata['video'] =  metadata['link']
00493             metadata['duration'] =  u''
00494         else:
00495             metadata['link'] =  metadata['video']
00496 
00497         return metadata
00498         # end videoDetails()
00499 
00500 
00501     def searchForVideos(self, title, pagenumber):
00502         """Common name for a video search. Used to interface with MythTV plugin NetVision
00503         """
00504         # v2 api calls - An example that must be customized for each target site
00505         if self.grabber_title == 'MTV':
00506             self.config[u'urls'][u'video.search'] = "http://api.mtvnservices.com/1/video/search/?term=%s&start-index=%s&max-results=%s"
00507         elif self.grabber_title == 'MTV Artists': # This search type is not currently implemented
00508             self.config[u'urls'][u'video.search'] = "http://api.mtvnservices.com/1/artist/search/?term=%s&start-index=%s&max-results=%s"
00509         else:
00510             sys.stderr.write(u"! Error: MtvInvalidSearchType - The grabber name (%s) is invalid \n" % self.grabber_title)
00511             sys.exit(1)
00512 
00513 
00514         # Easier for debugging
00515 #        print self.searchTitle(title, pagenumber, self.page_limit)
00516 #        print
00517 #        sys.exit()
00518 
00519 
00520         startindex = (int(pagenumber) -1) * self.page_limit + 1
00521         try:
00522             data = self.searchTitle(title, startindex, self.page_limit)
00523         except MtvVideoNotFound, msg:
00524             sys.stderr.write(u"%s\n" % msg)
00525             return None
00526         except MtvUrlError, msg:
00527             sys.stderr.write(u'%s\n' % msg)
00528             sys.exit(1)
00529         except MtvHttpError, msg:
00530             sys.stderr.write(self.error_messages['MtvHttpError'] % msg)
00531             sys.exit(1)
00532         except MtvRssError, msg:
00533             sys.stderr.write(self.error_messages['MtvRssError'] % msg)
00534             sys.exit(1)
00535         except Exception, e:
00536             sys.stderr.write(u"! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e))
00537             sys.exit(1)
00538 
00539         if data == None:
00540             return None
00541         if not len(data):
00542             return None
00543 
00544         items = []
00545         for match in data:
00546             item_data = {}
00547             for key in self.key_translation[1].keys():
00548                 if key in match.keys():
00549                     item_data[self.key_translation[1][key]] = match[key]
00550                 else:
00551                     item_data[self.key_translation[1][key]] = u''
00552             items.append(item_data)
00553 
00554         # Channel details and search results
00555         channel = {'channel_title': u'MTV', 'channel_link': u'http://www.mtv.com', 'channel_description': u"Visit MTV (Music Television) for TV shows, music videos, celebrity photos, news.", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0}
00556 
00557         if len(items) == self.page_limit:
00558             channel['channel_numresults'] = self.page_limit * int(pagenumber) + 1
00559         elif len(items) < self.page_limit:
00560             channel['channel_numresults'] = self.page_limit * (int(pagenumber)-1) + len(items)
00561         else:
00562             channel['channel_numresults'] = self.page_limit * int(pagenumber)
00563         channel['channel_startindex'] = self.page_limit * int(pagenumber)
00564         channel['channel_returned'] = len(items)
00565 
00566         if len(items):
00567             return [[channel, items]]
00568         return None
00569     # end searchForVideos()
00570 
00571 
00572     def displayTreeView(self):
00573         '''Gather the MTV Genres/Artists/...etc then get a max page of videos meta data in each of them
00574         return array of directories and their video metadata
00575         '''
00576         # Channel details and search results
00577         self.channel = {'channel_title': u'MTV', 'channel_link': u'http://www.mtv.com', 'channel_description': u"Visit MTV (Music Television) for TV shows, music videos, celebrity photos, news.", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0}
00578 
00579         if self.config['debug_enabled']:
00580             print self.config[u'urls']
00581             print
00582 
00583         # Set the default videos per page limit for all feeds/categories/... etc
00584         for key in self.tree_customize.keys():
00585             if '__default__' in self.tree_customize[key].keys():
00586                 if 'max-results' in self.tree_customize[key]['__default__'].keys():
00587                     self.tree_customize[key]['__default__']['max-results'] = unicode(self.page_limit)
00588 
00589         # Get videos within each category
00590         dictionaries = []
00591 
00592         # Process the various video feeds/categories/... etc
00593         for key in self.tree_order:
00594             self.tree_key = key
00595             dictionaries = self.getVideos(self.tree_org[key], dictionaries)
00596 
00597         return [[self.channel, dictionaries]]
00598     # end displayTreeView()
00599 
00600     def makeURL(self, URL):
00601         '''Form a URL to search for videos
00602         return a URL
00603         '''
00604         additions = dict(self.tree_customize[self.tree_key]['__default__']) # Set defaults
00605 
00606         # Add customizations
00607         if self.feed in self.tree_customize[self.tree_key].keys():
00608             for element in self.tree_customize[self.tree_key][self.feed].keys():
00609                 additions[element] = self.tree_customize[self.tree_key][self.feed][element]
00610 
00611         # Make the search extension string that is added to the URL
00612         addition = u''
00613         for ky in additions.keys():
00614             if ky.startswith('add_'):
00615                 addition+=u'/%s' %  additions[ky]
00616             else:
00617                 addition+=u'&%s=%s' %  (ky, additions[ky])
00618         index = URL.find('%')
00619         if index == -1:
00620             return (URL+addition)
00621         else:
00622             return (URL+addition) % self.feed
00623     # end makeURL()
00624 
00625 
00626     def getVideos(self, dir_dict, dictionaries):
00627         '''Parse a list made of genres/artists ... etc lists and retrieve video meta data
00628         return a dictionary of directory names and categories video metadata
00629         '''
00630         for sets in dir_dict:
00631             if not isinstance(sets[1], list):
00632                 if sets[0] != '': # Add the nested dictionaries display name
00633                     try:
00634                         dictionaries.append([self.massageDescription(sets[0]), self.setTreeViewIcon(self.feed_icons[self.tree_key][sets[0]])])
00635                     except KeyError:
00636                         dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
00637                 else:
00638                     dictionaries.append(['', u'']) # Add the nested dictionary indicator
00639                 continue
00640             temp_dictionary = []
00641             for self.feed in sets[1]:
00642                 if self.config[u'urls'][u'tree.view'][self.tree_key].has_key('__all__'):
00643                     URL = self.config[u'urls'][u'tree.view'][self.tree_key]['__all__']
00644                 else:
00645                     URL = self.config[u'urls'][u'tree.view'][self.tree_key][self.feed]
00646                 temp_dictionary = self.config['item_parser'][URL[1]](self.makeURL(URL[0]), temp_dictionary)
00647             if len(temp_dictionary):
00648                 if len(sets[0]): # Add the nested dictionaries display name
00649                     try:
00650                         dictionaries.append([self.massageDescription(sets[0]), self.setTreeViewIcon(self.feed_icons[self.tree_key][sets[0]])])
00651                     except KeyError:
00652                         dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
00653                 for element in temp_dictionary:
00654                     dictionaries.append(element)
00655                 if len(sets[0]):
00656                     dictionaries.append(['', u'']) # Add the nested dictionary indicator
00657         return dictionaries
00658     # end getVideos()
00659 
00660 
00661     def getVideosForURL(self, url, dictionaries):
00662         '''Get the video metadata for url search
00663         return the video dictionary of directories and their video mata data
00664         '''
00665         initial_length = len(dictionaries)
00666 
00667         if self.config['debug_enabled']:
00668             print "Category URL:"
00669             print url
00670             print
00671 
00672         try:
00673             etree = XmlHandler(url).getEt()
00674         except Exception, errormsg:
00675             sys.stderr.write(self.error_messages['MtvUrlError'] % (url, errormsg))
00676             return dictionaries
00677 
00678         if etree is None:
00679             sys.stderr.write(u'1-No Videos for (%s)\n' % self.feed)
00680             return dictionaries
00681 
00682         dictionary_first = False
00683         for elements in etree:
00684             if elements.tag.endswith(u'totalResults'):
00685                 self.channel['channel_numresults'] += int(elements.text)
00686                 self.channel['channel_startindex'] = self.page_limit
00687                 self.channel['channel_returned'] = self.page_limit # False value CHANGE later
00688                 continue
00689 
00690             if not elements.tag.endswith(u'entry'):
00691                 continue
00692 
00693             metadata = {}
00694             cur_size = True
00695             flash = False
00696             metadata['language'] = self.config['language']
00697             for e in elements:
00698                 if e.tag.endswith(u'title'):
00699                     if e.text != None:
00700                         metadata['title'] = self.massageDescription(e.text.strip())
00701                     else:
00702                         metadata['title'] = u''
00703                     continue
00704                 if e.tag == u'content':
00705                     if e.text != None:
00706                         metadata['media_description'] = self.massageDescription(e.text.strip())
00707                     else:
00708                         metadata['media_description'] = u''
00709                     continue
00710                 if e.tag.endswith(u'published'): # '2007-03-06T00:00:00Z'
00711                     if e.text != None:
00712                         pub_time = time.strptime(e.text.strip(), "%Y-%m-%dT%H:%M:%SZ")
00713                         metadata['published_parsed'] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', pub_time)
00714                     else:
00715                         metadata['published_parsed'] = u''
00716                     continue
00717                 if e.tag.endswith(u'content') and e.text == None:
00718                     metadata['video'] =  self.ampReplace(e.get('url'))
00719                     metadata['duration'] =  e.get('duration')
00720                     continue
00721                 if e.tag.endswith(u'player'):
00722                     metadata['link'] = self.ampReplace(e.get('url'))
00723                     continue
00724                 if e.tag.endswith(u'thumbnail'):
00725                     if cur_size == False:
00726                         continue
00727                     height = e.get('height')
00728                     width = e.get('width')
00729                     if int(width) > cur_size:
00730                         metadata['thumbnail'] = self.ampReplace(e.get('url'))
00731                         cur_size = int(width)
00732                     if int(width) >= 200:
00733                         cur_size = False
00734                     continue
00735                 if e.tag.endswith(u'author'):
00736                     for a in e:
00737                         if a.tag.endswith(u'name'):
00738                             if a.text:
00739                                 metadata['media_credit'] = self.massageDescription(a.text.strip())
00740                             else:
00741                                 metadata['media_credit'] = u''
00742                             break
00743                     continue
00744 
00745             if not len(metadata):
00746                 raise MtvVideoDetailError(u'2-No Video meta data for (%s)' % url)
00747 
00748             if not metadata.has_key('video') and not metadata.has_key('link'):
00749                 continue
00750 
00751             if not metadata.has_key('video'):
00752                 metadata['video'] = metadata['link']
00753             else:
00754                 index = metadata['video'].rindex(u':')
00755                 metadata['video'] = self.mtvHtmlPath % (urllib.quote(metadata['title'].encode("utf-8")), metadata['video'][index+1:])
00756                 metadata['link'] =  metadata['video']
00757                 # !! This tag will need to be added at a later date
00758 #                metadata['customhtml'] = u'true'
00759 
00760             if not dictionary_first:  # Add the dictionaries display name
00761                 dictionaries.append([self.massageDescription(self.feed_names[self.tree_key][self.feed]), self.setTreeViewIcon()])
00762                 dictionary_first = True
00763 
00764             final_item = {}
00765             for key in self.key_translation[1].keys():
00766                 if not metadata.has_key(key):
00767                     final_item[self.key_translation[1][key]] = u''
00768                 else:
00769                     final_item[self.key_translation[1][key]] = metadata[key]
00770             dictionaries.append(final_item)
00771 
00772         if initial_length < len(dictionaries): # Need to check if there was any items for this Category
00773             dictionaries.append(['', u'']) # Add the nested dictionary indicator
00774         return dictionaries
00775     # end getVideosForURL()
00776 # end Videos() class
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends