00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
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
00157
00158 self.config['debug_enabled'] = debug
00159
00160 self.log_name = "MTV"
00161 self.log = self._initLogger()
00162
00163 self.config['custom_ui'] = custom_ui
00164
00165 self.config['interactive'] = interactive
00166
00167 self.config['select_first'] = select_first
00168
00169 self.config['search_all_languages'] = search_all_languages
00170
00171
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
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"]
00182 self.config[u'urls'] = {}
00183
00184
00185 self.config['item_parser'] = {}
00186 self.config['item_parser']['main'] = self.getVideosForURL
00187
00188
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
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
00214 self.tree_customize = {
00215 'new_genres': {
00216 '__default__': {'max-results': '20', 'start-index': '1', 'date': last_3_months, 'sort': 'date_descending'},
00217
00218 },
00219 'genres': {
00220 '__default__': {'max-results': '20', 'start-index': '1', 'sort': 'date_descending'},
00221
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
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
00246 self.treeview = False
00247 self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/mtv.png'
00248
00249
00250
00251
00252
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 ""
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
00286 return self.ampReplace(re.sub(u"(?s)<[^>]*>|&#?\w+;", fixup, self.textUtf8(text))).replace(u'\n',u' ')
00287
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
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
00319
00320
00321 def ampReplace(self, text):
00322 '''Replace all "&" characters with "&"
00323 '''
00324 text = self.textUtf8(text)
00325 return text.replace(u'&',u'~~~~~').replace(u'&',u'&').replace(u'~~~~~', u'&')
00326
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
00345
00346
00347
00348
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
00393 for item in data:
00394 for key in item.keys():
00395 if item[key] == None:
00396 item[key] = u''
00397
00398
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':
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
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
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
00472
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
00499
00500
00501 def searchForVideos(self, title, pagenumber):
00502 """Common name for a video search. Used to interface with MythTV plugin NetVision
00503 """
00504
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':
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
00515
00516
00517
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
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
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
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
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
00590 dictionaries = []
00591
00592
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
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__'])
00605
00606
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
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
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] != '':
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''])
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]):
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''])
00657 return dictionaries
00658
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
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'):
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
00758
00759
00760 if not dictionary_first:
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):
00773 dictionaries.append(['', u''])
00774 return dictionaries
00775
00776