|
MythTV
0.26-pre
|
00001 #!/usr/bin/env python 00002 # -*- coding: utf-8 -*- 00003 # Copyright (C) 2009 Marc Poulhiès 00004 # 00005 # Python module for Vimeo 00006 # originaly part of 'plopifier' 00007 # 00008 # Plopifier is free software: you can redistribute it and/or modify 00009 # it under the terms of the GNU General Public License as published by 00010 # the Free Software Foundation, either version 3 of the License, or 00011 # (at your option) any later version. 00012 # 00013 # Plopifier is distributed in the hope that it will be useful, 00014 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 # GNU General Public License for more details. 00017 # 00018 # You should have received a copy of the GNU General Public License 00019 # along with Plopifier. If not, see <http://www.gnu.org/licenses/>. 00020 # ------------------------------------------------------------------ 00021 # Name: vimeo_api - Simple-to-use Python interface to the vimeo API (http://vimeo.com) 00022 # Python Script 00023 # Author: Marc Poulhiès and modified by R.D. Vaughan 00024 # Purpose: This python script is intended to perform a variety of utility functions to search and access text 00025 # metadata and video/image URLs from vimeo. These routines are based on the v2 api. Specifications 00026 # for this api are published at http://vimeo.com/api/docs/advanced-api 00027 # 00028 # License:Creative Commons GNU GPL v2 00029 # (http://creativecommons.org/licenses/GPL/2.0/) 00030 #------------------------------------- 00031 __title__ ="vimeo_api - Simple-to-use Python interface to the vimeo API (http://vimeo.com)" 00032 __author__="Marc Poulhiès and modified by R.D. Vaughan" 00033 __purpose__=''' 00034 This python script is intended to perform a variety of utility functions to search and access text 00035 metadata and video/image URLs from vimeo. These routines are based on the v2 api. Specifications 00036 for this api are published at http://vimeo.com/api/docs/advanced-api 00037 ''' 00038 00039 __version__="v0.2.5" 00040 # 0.1.0 Initial development 00041 # 0.1.1 Added Tree view processing 00042 # 0.1.2 Documentation review 00043 # 0.2.0 Public release 00044 # 0.2.1 Fixed bug where some videos cannot be embedded (played fullscreen automatically) 00045 # 0.2.2 New python bindings conversion 00046 # Better exception error reporting 00047 # Better handling of invalid unicode data from source 00048 # 0.2.3 Completed the exception error reporting improvements 00049 # Fixed an exception message error when vimeo returns poorly formed XML. 00050 # For example search term "flute" returns bad XML while "Flute music" returns proper XML 00051 # 0.2.4 Fixed an exception message output code error 00052 # 0.2.5 Removed the need for python MythTV bindings and added "%SHAREDIR%" to icon directory path 00053 00054 """ 00055 Python module to interact with Vimeo through its API (version 2) 00056 """ 00057 import os, struct, sys, re, time, datetime 00058 import urllib, urllib2 00059 import logging 00060 import pycurl 00061 import xml.etree.ElementTree as ET 00062 import inspect 00063 import oauth.oauth_api as oauth 00064 from MythTV import MythXML 00065 00066 from vimeo_exceptions import (VimeoUrlError, VimeoHttpError, VimeoResponseError, VimeoVideoNotFound, VimeoRequestTokenError, VimeoAuthorizeTokenError, VimeoVideosSearchError, VimeoAllChannelError, __errmsgs__) 00067 00068 from vimeo_data import getData 00069 00070 00071 REQUEST_TOKEN_URL = 'http://vimeo.com/oauth/request_token' 00072 ACCESS_TOKEN_URL = 'http://vimeo.com/oauth/access_token' 00073 AUTHORIZATION_URL = 'http://vimeo.com/oauth/authorize' 00074 00075 API_REST_URL = 'http://vimeo.com/api/rest/v2/' 00076 API_V2_CALL_URL = 'http://vimeo.com/api/v2/' 00077 00078 USER_AGENT = 'python-vimeo http://github.com/dkm/python-vimeo' 00079 00080 PORT=80 00081 00082 HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1() 00083 00084 00085 class VimeoException(Exception): 00086 def __init__(self, msg): 00087 Exception.__init__(self) 00088 self.msg = msg 00089 00090 def __str__(self): 00091 return self.msg 00092 00093 class CurlyRestException(Exception): 00094 def __init__(self, code, msg, full): 00095 Exception.__init__(self) 00096 self.code = code 00097 self.msg = msg 00098 self.full = full 00099 00100 def __str__(self): 00101 return "Error code: %s, message: %s\nFull message: %s" % (self.code, 00102 self.msg, 00103 self.full) 00104 00105 00106 class CurlyRequest: 00107 """ 00108 A CurlyRequest object is used to send HTTP requests. 00109 It's a simple wrapper around basic curl methods. 00110 In particular, it can upload files and display a progress bar. 00111 """ 00112 def __init__(self, pbarsize=19): 00113 self.buf = None 00114 self.pbar_size = pbarsize 00115 self.pidx = 0 00116 self.debug = False 00117 00118 def do_rest_call(self, url): 00119 """ 00120 Send a simple GET request and interpret the answer as a REST reply. 00121 """ 00122 00123 res = self.do_request(url) 00124 try: 00125 t = ET.fromstring(res) 00126 00127 if t.attrib['stat'] == 'fail': 00128 err_code = t.find('err').attrib['code'] 00129 err_msg = t.find('err').attrib['msg'] 00130 raise Exception(err_code, err_msg, ET.tostring(t)) 00131 return t 00132 except Exception,e: 00133 raise Exception(u'%s' % (e)) 00134 00135 def _body_callback(self, buf): 00136 self.buf += buf 00137 00138 def do_request(self, url): 00139 """ 00140 Send a simple GET request 00141 """ 00142 if self.debug: 00143 print "Request URL:" 00144 print url 00145 print 00146 00147 self.buf = "" 00148 curl = pycurl.Curl() 00149 curl.setopt(pycurl.USERAGENT, USER_AGENT) 00150 curl.setopt(curl.URL, url) 00151 curl.setopt(curl.WRITEFUNCTION, self._body_callback) 00152 curl.perform() 00153 curl.close() 00154 p = self.buf 00155 self.buf = "" 00156 00157 if self.debug: 00158 print "Raw response:" 00159 print p 00160 print 00161 00162 return p 00163 00164 def _upload_progress(self, download_t, download_d, upload_t, upload_d): 00165 # this is only for upload progress bar 00166 if upload_t == 0: 00167 return 0 00168 00169 self.pidx = (self.pidx + 1) % len(TURNING_BAR) 00170 00171 done = int(self.pbar_size * upload_d / upload_t) 00172 00173 if done != self.pbar_size: 00174 pstr = '#'*done +'>' + ' '*(self.pbar_size - done - 1) 00175 else: 00176 pstr = '#'*done 00177 00178 print "\r%s[%s] " %(TURNING_BAR[self.pidx], pstr), 00179 return 0 00180 00181 def do_post_call(self, url, args, use_progress=False): 00182 """ 00183 Send a simple POST request 00184 """ 00185 c = pycurl.Curl() 00186 c.setopt(c.POST, 1) 00187 c.setopt(c.URL, url) 00188 c.setopt(c.HTTPPOST, args) 00189 c.setopt(c.WRITEFUNCTION, self.body_callback) 00190 #c.setopt(c.VERBOSE, 1) 00191 self.buf = "" 00192 00193 c.setopt(c.NOPROGRESS, 0) 00194 00195 if use_progress: 00196 c.setopt(c.PROGRESSFUNCTION, self._upload_progress) 00197 00198 c.perform() 00199 c.close() 00200 res = self.buf 00201 self.buf = "" 00202 return res 00203 00204 class SimpleOAuthClient(oauth.OAuthClient): 00205 """ 00206 Class used for handling authenticated call to the API. 00207 """ 00208 00209 def __init__(self, key, secret, 00210 server="vimeo.com", port=PORT, 00211 request_token_url=REQUEST_TOKEN_URL, 00212 access_token_url=ACCESS_TOKEN_URL, 00213 authorization_url=AUTHORIZATION_URL, 00214 token=None, 00215 token_secret=None): 00216 """ 00217 You need to give both key (consumer key) and secret (consumer secret). 00218 If you already have an access token (token+secret), you can use it 00219 by giving it through token and token_secret parameters. 00220 If not, then you need to call both get_request_token(), get_authorize_token_url() and 00221 finally get_access_token(). 00222 """ 00223 00224 self.curly = CurlyRequest() 00225 self.key = key 00226 self.secret = secret 00227 self.server = server 00228 self.port = PORT 00229 self.request_token_url = request_token_url 00230 self.access_token_url = access_token_url 00231 self.authorization_url = authorization_url 00232 self.consumer = oauth.OAuthConsumer(self.key, self.secret) 00233 00234 if token != None and token_secret != None: 00235 self.token = oauth.OAuthToken(token, token_secret) 00236 else: 00237 self.token = None 00238 00239 def get_request_token(self): 00240 """ 00241 Requests a request token and return it on success. 00242 """ 00243 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, 00244 http_url=self.request_token_url) 00245 oauth_request.sign_request(HMAC_SHA1, self.consumer, None) 00246 self.token = self._fetch_token(oauth_request) 00247 00248 00249 def get_authorize_token_url(self): 00250 """ 00251 Returns a URL used to verify and authorize the application to access 00252 user's account. The pointed page should contain a simple 'password' that 00253 acts as the 'verifier' in oauth. 00254 """ 00255 00256 oauth_request = oauth.OAuthRequest.from_token_and_callback(token=self.token, 00257 http_url=self.authorization_url) 00258 return oauth_request.to_url() 00259 00260 00261 def get_access_token(self, verifier): 00262 """ 00263 Should be called after having received the 'verifier' from the authorization page. 00264 See 'get_authorize_token_url()' method. 00265 """ 00266 00267 self.token.set_verifier(verifier) 00268 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, 00269 token=self.token, 00270 verifier=verifier, 00271 http_url=self.access_token_url) 00272 oauth_request.sign_request(HMAC_SHA1, self.consumer, self.token) 00273 self.token = self._fetch_token(oauth_request) 00274 00275 def _fetch_token(self, oauth_request): 00276 """ 00277 Sends a requests and interprets the result as a token string. 00278 """ 00279 ans = self.curly.do_request(oauth_request.to_url()) 00280 return oauth.OAuthToken.from_string(ans) 00281 00282 def vimeo_oauth_checkAccessToken(self, auth_token): 00283 pass 00284 00285 00286 def _do_vimeo_authenticated_call(self, method, parameters={}): 00287 """ 00288 Wrapper to send an authenticated call to vimeo. You first need to have 00289 an access token. 00290 """ 00291 00292 parameters['method'] = method 00293 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, 00294 token=self.token, 00295 http_method='GET', 00296 http_url=API_REST_URL, 00297 parameters=parameters) 00298 oauth_request.sign_request(HMAC_SHA1, self.consumer, self.token) 00299 return self.curly.do_rest_call(oauth_request.to_url()) 00300 00301 def _do_vimeo_unauthenticated_call(self, method, parameters={}): 00302 """ 00303 Wrapper to send an unauthenticated call to vimeo. You don't need to have 00304 an access token. 00305 """ 00306 parameters['method'] = method 00307 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, 00308 http_method='GET', 00309 http_url=API_REST_URL, 00310 parameters=parameters) 00311 oauth_request.sign_request(HMAC_SHA1, self.consumer, None) 00312 return self.curly.do_rest_call(oauth_request.to_url()) 00313 00314 ### 00315 ### Album section 00316 ### 00317 def vimeo_albums_getAll(self, user_id, sort=None, 00318 per_page=None, 00319 page=None): 00320 """ 00321 Get a list of a user's albums. 00322 This method does not require authentication. 00323 """ 00324 params = {'user_id': user_id} 00325 if sort in ('newest', 'oldest', 'alphabetical'): 00326 params['sort'] = sort 00327 if per_page != None: 00328 params['per_page'] = per_page 00329 if page != None: 00330 params['page'] = page 00331 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'), 00332 parameters=params) 00333 00334 ### 00335 ### Video section 00336 ### 00337 def vimeo_videos_search(self, query, sort=None, 00338 per_page=None, 00339 page=None): 00340 """ 00341 Search for matching Videos. 00342 This method does not require authentication. 00343 """ 00344 params = {} 00345 if sort in ('newest', 'most_played', 'relevant', 'most_liked', 'oldest'): 00346 params['sort'] = sort 00347 else: 00348 params['sort'] = 'most_liked' 00349 if per_page != None: 00350 params['per_page'] = per_page 00351 if page != None: 00352 params['page'] = page 00353 params['full_response'] = '1' 00354 #params['query'] = query.replace(u' ', u'_') 00355 params['query'] = query 00356 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'), 00357 parameters=params) 00358 00359 ### 00360 ### Channel section 00361 ### 00362 def vimeo_channels_getAll(self, sort=None, 00363 per_page=None, 00364 page=None): 00365 """ 00366 Get a list of all public channels. 00367 This method does not require authentication. 00368 """ 00369 params = {} 00370 if sort in ('newest', 'oldest', 'alphabetical', 00371 'most_videos', 'most_subscribed', 'most_recently_updated'): 00372 params['sort'] = sort 00373 if per_page != None: 00374 params['per_page'] = per_page 00375 if page != None: 00376 params['page'] = page 00377 00378 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'), 00379 parameters=params) 00380 00381 def vimeo_channels_getVideos(self, channel_id=None, full_response=None, 00382 per_page=None, 00383 page=None): 00384 """ 00385 Get a list of Videos for a specific channels. 00386 This method does not require authentication. 00387 """ 00388 # full_response channel_id 00389 params = {} 00390 if channel_id != None: 00391 params['channel_id'] = channel_id 00392 if full_response != None: 00393 params['full_response'] = 1 00394 if per_page != None: 00395 params['per_page'] = per_page 00396 if page != None: 00397 params['page'] = page 00398 00399 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'), 00400 parameters=params) 00401 00402 00403 ### 00404 ### Contacts section 00405 ### 00406 00407 00408 ### 00409 ### Groups section 00410 ### 00411 00412 ### 00413 ### Groups Events section 00414 ### 00415 00416 ### 00417 ### Groups forums section 00418 ### 00419 00420 ### 00421 ### OAuth section 00422 ### 00423 00424 ### 00425 ### People section 00426 ### 00427 00428 ### 00429 ### Test section 00430 ### 00431 def vimeo_test_echo(self, params={}): 00432 """ 00433 This will just repeat back any parameters that you send. 00434 No auth required 00435 """ 00436 ## for simplicity, I'm using a signed call, but it's 00437 ## useless. Tokens & stuff will simply get echoed as the 00438 ## others parameters are. 00439 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'), 00440 parameters=params) 00441 00442 00443 def vimeo_test_login(self): 00444 """ 00445 Is the user logged in? 00446 """ 00447 return self._do_vimeo_authenticated_call(inspect.stack()[0][3].replace('_', '.')) 00448 00449 00450 def vimeo_test_null(self): 00451 """ 00452 This is just a simple null/ping test. 00453 00454 You can use this method to make sure that you are properly 00455 contacting to the Vimeo API. 00456 """ 00457 return self._do_vimeo_authenticated_call(inspect.stack()[0][3].replace('_', '.')) 00458 00459 00460 ### 00461 ### Videos section 00462 ### 00463 00464 ### 00465 ### Videos comments section 00466 ### 00467 00468 ### 00469 ### Videos embed section 00470 ### 00471 00472 00473 ### 00474 ### Videos Upload section 00475 ### 00476 00477 def vimeo_videos_upload_getQuota(self): 00478 """ 00479 (from vimeo API documentation) 00480 Get the space and number of HD uploads left for a user. 00481 00482 Numbers are provided in bytes. It's a good idea to check this 00483 method before you upload a video to let the user know if their 00484 video will be converted to HD. hd_quota will have a value of 0 00485 if the user reached the max number of uploads, 1 00486 otherwise. Resets is the number of the day of the week, 00487 starting with Sunday. 00488 """ 00489 return self._do_vimeo_authenticated_call(inspect.stack()[0][3].replace('_', '.')) 00490 00491 00492 00493 00494 def _simple_request(url, format): 00495 if format != 'xml': 00496 raise VimeoException("Sorry, only 'xml' supported. '%s' was requested." %format) 00497 00498 curly = CurlyRequest() 00499 url = url %(format) 00500 ans = curly.do_request(url) 00501 00502 if format == 'xml': 00503 return ET.fromstring(ans) 00504 00505 ## 00506 ## User related call from the "Simple API". 00507 ## See : http://vimeo.com/api/docs/simple-api 00508 ## 00509 00510 def _user_request(user, info, format): 00511 url = API_V2_CALL_URL + '%s/%s.%%s' %(user,info) 00512 return _simple_request(url, format) 00513 00514 def user_info(user, format="xml"): 00515 """ 00516 User info for the specified user 00517 """ 00518 return _user_request(user, inspect.stack()[0][3][5:], format) 00519 00520 00521 def user_videos(user, format="xml"): 00522 """ 00523 Videos created by user 00524 """ 00525 return _user_request(user, inspect.stack()[0][3][5:], format) 00526 00527 def user_likes(user, format="xml"): 00528 """ 00529 Videos the user likes 00530 """ 00531 return _user_request(user, inspect.stack()[0][3][5:], format) 00532 00533 def user_appears_in(user, format="xml"): 00534 """ 00535 Videos that the user appears in 00536 """ 00537 return _user_request(user, inspect.stack()[0][3][5:], format) 00538 00539 def user_all_videos(user, format="xml"): 00540 """ 00541 Videos that the user appears in and created 00542 """ 00543 return _user_request(user, inspect.stack()[0][3][5:], format) 00544 00545 def user_subscriptions(user, format="xml"): 00546 """ 00547 Videos the user is subscribed to 00548 """ 00549 return _user_request(user, inspect.stack()[0][3][5:], format) 00550 00551 def user_albums(user, format="xml"): 00552 """ 00553 Albums the user has created 00554 """ 00555 return _user_request(user, inspect.stack()[0][3][5:], format) 00556 00557 def user_channels(user, format="xml"): 00558 """ 00559 Channels the user has created and subscribed to 00560 """ 00561 return _user_request(user, inspect.stack()[0][3][5:], format) 00562 00563 def user_groups(user, format="xml"): 00564 """ 00565 Groups the user has created and joined 00566 """ 00567 return _user_request(user, inspect.stack()[0][3][5:], format) 00568 00569 def user_contacts_videos(user, format="xml"): 00570 """ 00571 Videos that the user's contacts created 00572 """ 00573 return _user_request(user, inspect.stack()[0][3][5:], format) 00574 00575 def user_contacts_like(user, format="xml"): 00576 """ 00577 Videos that the user's contacts like 00578 """ 00579 return _user_request(user, inspect.stack()[0][3][5:], format) 00580 00581 00582 ## 00583 ## get a specific video 00584 ## 00585 def video_request(video, format): 00586 url = API_V2_CALL_URL + 'video/%s.%%s' %(video) 00587 return _simple_request(url) 00588 00589 ################################################################################################# 00590 # MythTV Netvideo specific classes start here 00591 ################################################################################################# 00592 00593 class OutStreamEncoder(object): 00594 """Wraps a stream with an encoder""" 00595 def __init__(self, outstream, encoding=None): 00596 self.out = outstream 00597 if not encoding: 00598 self.encoding = sys.getfilesystemencoding() 00599 else: 00600 self.encoding = encoding 00601 00602 def write(self, obj): 00603 """Wraps the output stream, encoding Unicode strings with the specified encoding""" 00604 if isinstance(obj, unicode): 00605 try: 00606 self.out.write(obj.encode(self.encoding)) 00607 except IOError: 00608 pass 00609 else: 00610 try: 00611 self.out.write(obj) 00612 except IOError: 00613 pass 00614 00615 def __getattr__(self, attr): 00616 """Delegate everything but write to the stream""" 00617 return getattr(self.out, attr) 00618 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8') 00619 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8') 00620 00621 00622 class Videos(object): 00623 """Main interface to http://vimeo.com/ 00624 This is done to support a common naming framework for all python Netvision plugins no matter their site 00625 target. 00626 00627 Supports search and tree view methods 00628 The apikey is a not passed but is created as a session token to access http://vimeo.com/ 00629 """ 00630 def __init__(self, 00631 apikey, 00632 mythtv = True, 00633 interactive = False, 00634 select_first = False, 00635 debug = False, 00636 custom_ui = None, 00637 language = None, 00638 search_all_languages = False, 00639 ): 00640 """apikey (str/unicode): 00641 Specify the target site API key. Applications need their own key in some cases 00642 00643 mythtv (True/False): 00644 When True, the returned meta data is being returned has the key and values massaged to match MythTV 00645 When False, the returned meta data is being returned matches what target site returned 00646 00647 interactive (True/False): (This option is not supported by all target site apis) 00648 When True, uses built-in console UI is used to select the correct show. 00649 When False, the first search result is used. 00650 00651 select_first (True/False): (This option is not supported currently implemented in any grabbers) 00652 Automatically selects the first series search result (rather 00653 than showing the user a list of more than one series). 00654 Is overridden by interactive = False, or specifying a custom_ui 00655 00656 debug (True/False): 00657 shows verbose debugging information 00658 00659 custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers) 00660 A callable subclass of interactive class (overrides interactive option) 00661 00662 language (2 character language abbreviation): (This option is not supported by all target site apis) 00663 The language of the returned data. Is also the language search 00664 uses. Default is "en" (English). For full list, run.. 00665 00666 search_all_languages (True/False): (This option is not supported by all target site apis) 00667 By default, a Netvision grabber will only search in the language specified using 00668 the language option. When this is True, it will search for the 00669 show in any language 00670 00671 """ 00672 self.config = {} 00673 self.mythxml = MythXML() 00674 00675 self.config['debug_enabled'] = debug # show debugging messages 00676 00677 self.log_name = "vimeo" 00678 self.log = self._initLogger() # Setups the logger (self.log.debug() etc) 00679 00680 self.config['custom_ui'] = custom_ui 00681 00682 self.config['interactive'] = interactive # prompt for correct series? 00683 00684 self.config['select_first'] = select_first 00685 00686 self.config['search_all_languages'] = search_all_languages 00687 00688 # Defaulting to ENGISH but the vimeo.com apis do not support specifying a language 00689 self.config['language'] = "en" 00690 00691 self.error_messages = {'VimeoUrlError': u"! Error: The URL (%s) cause the exception error (%s)\n", 'VimeoHttpError': u"! Error: An HTTP communications error with vimeo.com was raised (%s)\n", 'VimeoResponseError': u"! Error: Invalid XML metadata\nwas received from vimeo.com error (%s). Skipping item.\n", 'VimeoVideoNotFound': u"! Error: Video search with vimeo.com did not return any results for (%s)\n", 'VimeoRequestTokenError': u"! Error: Vimeo get request token failed (%s)\n", 'VimeoAuthorizeTokenError': u"! Error: Video get authorise token failed (%s)\n", 'VimeoVideosSearchError': u"! Error: Video search failed (%s)\n", 'VimeoException': u"! Error: VimeoException (%s)\n", 'VimeoAllChannelError': u"! Error: Access Video Channel information failed (%s)\n", } 00692 00693 # This is an example that must be customized for each target site 00694 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', 'display_name': 'item_author', 'upload_date': 'item_pubdate', 'description': 'item_description', 'url': 'item_link', 'thumbnail': 'item_thumbnail', 'url': 'item_url', 'duration': 'item_duration', 'number_of_likes': 'item_rating', 'width': 'item_width', 'height': 'item_height', 'language': 'item_lang'}] 00695 00696 # The following url_ configs are based of the 00697 # http://vimeo.com/api/docs/advanced-api 00698 self.config[u'methods'] = {} 00699 # Methods are actually set in initializeVimeo() 00700 self.config[u'methods']['channels'] = None 00701 self.config[u'methods']['channels_videos'] = None 00702 00703 # Functions that parse video data from RSS data 00704 self.config['item_parser'] = {} 00705 self.config['item_parser']['channels'] = self.getVideosForChannels 00706 00707 # Tree view url and the function that parses that urls meta data 00708 self.config[u'methods'][u'tree.view'] = { 00709 'N_R_S': { 00710 '__all__': ['channels_videos', 'channels'], 00711 }, 00712 'Everything HD': { 00713 '__all__': ['channels_videos', 'channels'], 00714 }, 00715 } 00716 00717 self.config[u'image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions 00718 00719 self.tree_key_list = ['newest', 'most_recently_updated', 'most_subscribed'] 00720 00721 self.tree_order = ['N_R_S', 'Everything HD', ] 00722 00723 self.tree_org = { 00724 'N_R_S': [ 00725 ['Newest Channels/Most ...', u''], 00726 ['', self.tree_key_list], 00727 [u'',u''] 00728 ], 00729 'Everything HD': [ 00730 ['Everything HD: Newest Channels/Most ...', self.tree_key_list ] 00731 ], 00732 } 00733 00734 self.tree_customize = { 00735 'N_R_S': { 00736 '__default__': { }, 00737 #'cat name': {}, 00738 }, 00739 'Everything HD': { 00740 '__default__': { }, 00741 #'cat name': {}, 00742 }, 00743 } 00744 00745 self.feed_names = { 00746 'N_R_S': {'newest': 'Most Recent Channels', 'most_recently_updated': "This Month's Channels'", 'most_subscribed': 'Most Subscribed Channels', 00747 }, 00748 'Everything HD': {'newest': 'Most Recent Channels', 'most_recently_updated': "This Month's Channels'", 'most_subscribed': 'Most Subscribed Channels', 00749 }, 00750 00751 } 00752 00753 self.feed_icons = { 00754 'N_R_S': {'newest': 'directories/topics/most_recent', 'most_recently_updated': 'directories/topics/month', 'most_subscribed': 'directories/topics/most_subscribed', 00755 }, 00756 'Everything HD': {'newest': 'directories/topics/most_recent', 'most_recently_updated': 'directories/topics/month', 'most_subscribed': 'directories/topics/most_subscribed', 'Everything HD': 'directories/topics/hd' 00757 }, 00758 00759 } 00760 00761 # Initialize the tree view flag so that the item parsing code can be used for multiple purposes 00762 self.treeview = False 00763 self.channel_icon = u'%SHAREDIR%/mythnetvision/icons/vimeo.jpg' 00764 # end __init__() 00765 00766 ########################################################################################################### 00767 # 00768 # Start - Utility functions 00769 # 00770 ########################################################################################################### 00771 00772 def massageDescription(self, text): 00773 '''Removes HTML markup from a text string. 00774 @param text The HTML source. 00775 @return The plain text. If the HTML source contains non-ASCII 00776 entities or character references, this is a Unicode string. 00777 ''' 00778 def fixup(m): 00779 text = m.group(0) 00780 if text[:1] == "<": 00781 return "" # ignore tags 00782 if text[:2] == "&#": 00783 try: 00784 if text[:3] == "&#x": 00785 return unichr(int(text[3:-1], 16)) 00786 else: 00787 return unichr(int(text[2:-1])) 00788 except ValueError: 00789 pass 00790 elif text[:1] == "&": 00791 import htmlentitydefs 00792 entity = htmlentitydefs.entitydefs.get(text[1:-1]) 00793 if entity: 00794 if entity[:2] == "&#": 00795 try: 00796 return unichr(int(entity[2:-1])) 00797 except ValueError: 00798 pass 00799 else: 00800 return unicode(entity, "iso-8859-1") 00801 return text # leave as is 00802 return self.ampReplace(re.sub(u"(?s)<[^>]*>|&#?\w+;", fixup, self.textUtf8(text))).replace(u'\n',u' ') 00803 # end massageDescription() 00804 00805 00806 def _initLogger(self): 00807 """Setups a logger using the logging module, returns a log object 00808 """ 00809 logger = logging.getLogger(self.log_name) 00810 formatter = logging.Formatter('%(asctime)s) %(levelname)s %(message)s') 00811 00812 hdlr = logging.StreamHandler(sys.stdout) 00813 00814 hdlr.setFormatter(formatter) 00815 logger.addHandler(hdlr) 00816 00817 if self.config['debug_enabled']: 00818 logger.setLevel(logging.DEBUG) 00819 else: 00820 logger.setLevel(logging.WARNING) 00821 return logger 00822 #end initLogger 00823 00824 00825 def textUtf8(self, text): 00826 if text == None: 00827 return text 00828 try: 00829 return unicode(text, 'utf8') 00830 except UnicodeDecodeError: 00831 return u'' 00832 except (UnicodeEncodeError, TypeError): 00833 return text 00834 # end textUtf8() 00835 00836 00837 def ampReplace(self, text): 00838 '''Replace all "&" characters with "&" 00839 ''' 00840 text = self.textUtf8(text) 00841 return text.replace(u'&',u'~~~~~').replace(u'&',u'&').replace(u'~~~~~', u'&') 00842 # end ampReplace() 00843 00844 00845 def setTreeViewIcon(self, dir_icon=None): 00846 '''Check if there is a specific generic tree view icon. If not default to the channel icon. 00847 return self.tree_dir_icon 00848 ''' 00849 self.tree_dir_icon = self.channel_icon 00850 if not dir_icon: 00851 if not self.feed_icons.has_key(self.tree_key): 00852 return self.tree_dir_icon 00853 if not self.feed_icons[self.tree_key].has_key(self.feed): 00854 return self.tree_dir_icon 00855 dir_icon = self.feed_icons[self.tree_key][self.feed] 00856 if not dir_icon: 00857 return self.tree_dir_icon 00858 self.tree_dir_icon = u'%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, ) 00859 return self.tree_dir_icon 00860 # end setTreeViewIcon() 00861 00862 ########################################################################################################### 00863 # 00864 # End of Utility functions 00865 # 00866 ########################################################################################################### 00867 00868 def initializeVimeo(self): 00869 '''Initialize acccess methods for all Vimeo API calls 00870 raise errors if there are issues during the initalization steps 00871 return nothing 00872 ''' 00873 # 00874 self.client = SimpleOAuthClient(getData().update(getData().a), getData().update(getData().s)) 00875 if self.config['debug_enabled']: 00876 self.client.curly.debug = True 00877 try: 00878 self.client.get_request_token() 00879 except Exception, msg: 00880 raise VimeoRequestTokenError(u'%s', msg) 00881 try: 00882 self.client.get_authorize_token_url() 00883 except Exception, msg: 00884 raise VimeoAuthorizeTokenError(u'%s', msg) 00885 00886 self.config[u'methods']['channels'] = self.client.vimeo_channels_getAll 00887 self.config[u'methods']['channels_videos'] = self.client.vimeo_channels_getVideos 00888 00889 # end initializeVimeo() 00890 00891 def processVideoUrl(self, url): 00892 playerUrl = self.mythxml.getInternetContentUrl("nv_python_libs/configs/HTML/vimeo.html", \ 00893 url.replace(u'http://vimeo.com/', '')) 00894 return self.ampReplace(playerUrl) 00895 00896 def searchTitle(self, title, pagenumber, pagelen): 00897 '''Key word video search of the vimeo.com web site 00898 return an array of matching item dictionaries 00899 return 00900 ''' 00901 self.initializeVimeo() 00902 00903 # Used for debugging usually commented out 00904 # xml_data = self.client.vimeo_videos_search(title, sort='most_liked', 00905 # per_page=pagelen, 00906 # page=pagenumber) 00907 # print xml_data 00908 00909 try: 00910 xml_data = self.client.vimeo_videos_search(urllib.quote_plus(title.encode("utf-8")), 00911 sort='most_liked', 00912 per_page=pagelen, 00913 page=pagenumber) 00914 except Exception, msg: 00915 raise VimeoVideosSearchError(u'%s' % msg) 00916 00917 if xml_data == None: 00918 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title) 00919 00920 if not len(xml_data.keys()): 00921 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title) 00922 00923 if xml_data.tag == 'rsp': 00924 if not xml_data.get('stat') == 'ok': 00925 if __errmsgs__.has_key(xml_data.get('stat')): 00926 errmsg = __errmsg__[xml_data.get('stat')] 00927 raise VimeoResponseError(u"Error %s - %s" % (xml_data.get('stat'), errmsg)) 00928 else: 00929 errmsg = u'Unknown error' 00930 raise VimeoResponseError(u"Error %s - %s" % (xml_data.get('stat'), errmsg)) 00931 00932 elements_final = [] 00933 videos = xml_data.find(u"videos") 00934 if videos: 00935 if videos.get('total'): 00936 if not int(videos.get('total')): 00937 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title) 00938 self.channel['channel_numresults'] = int(videos.get('total')) 00939 00940 # Collect video meta data that matched the search value 00941 for video in xml_data.find(u"videos").getchildren(): 00942 hd_flag = False 00943 embed_flag = False 00944 if video.tag == 'video': 00945 if video.get('embed_privacy') == "anywhere": 00946 embed_flag = True 00947 if video.get('is_hd') == "1": 00948 hd_flag = True 00949 v_details = {} 00950 for details in video.getchildren(): 00951 if details.tag in ['tags', 'cast']: 00952 continue 00953 if details.tag == 'width': 00954 if details.text: 00955 v_details['width'] = details.text.strip() 00956 continue 00957 if details.tag == 'height': 00958 if details.text: 00959 v_details['height'] = details.text.strip() 00960 continue 00961 if details.tag == 'duration': 00962 if details.text: 00963 v_details['duration'] = details.text.strip() 00964 continue 00965 if details.tag == 'owner': 00966 if details.text: 00967 v_details['display_name'] = self.massageDescription(details.get('display_name').strip()) 00968 else: 00969 v_details['display_name'] = u'' 00970 continue 00971 if details.tag == 'description': 00972 if details.text: 00973 v_details[details.tag] = self.massageDescription(details.text.strip()) 00974 else: 00975 v_details[details.tag] = u'' 00976 continue 00977 if details.tag == 'upload_date': 00978 if details.text: 00979 pub_time = time.strptime(details.text.strip(), "%Y-%m-%d %H:%M:%S") 00980 v_details[details.tag] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', pub_time) 00981 else: 00982 v_details[details.tag] = u'' 00983 continue 00984 if details.tag == 'urls': 00985 for url in details.getchildren(): 00986 if url.get('type') == 'video': 00987 if url.text: # Make the link fullscreen and auto play 00988 if embed_flag: 00989 v_details[url.tag] = self.processVideoUrl(url.text.strip()) 00990 else: 00991 v_details[url.tag] = self.ampReplace(url.text.strip()) 00992 else: 00993 v_details[url.tag] = u'' 00994 continue 00995 if details.tag == 'thumbnails': 00996 largest = 0 00997 for image in details.getchildren(): 00998 if image.tag == 'thumbnail': 00999 height = int(image.get('height')) 01000 width = int(image.get('width')) 01001 default = image.text.find('default') 01002 if largest < height * width and not default != -1: 01003 if image.text: 01004 v_details[image.tag] = self.ampReplace(image.text.strip()) 01005 if width >= 200: 01006 break 01007 continue 01008 if details.text: 01009 v_details[details.tag] = self.massageDescription(details.text.strip()) 01010 else: 01011 v_details[details.tag] = u'' 01012 if hd_flag and not v_details.has_key('width'): 01013 v_details['width'] = 1280 01014 v_details['height'] = 720 01015 elements_final.append(v_details) 01016 01017 if not len(elements_final): 01018 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title) 01019 01020 return elements_final 01021 # end searchTitle() 01022 01023 01024 def searchForVideos(self, title, pagenumber): 01025 """Common name for a video search. Used to interface with MythTV plugin NetVision 01026 """ 01027 # Channel details and search results 01028 self.channel = {'channel_title': u'Vimeo', 'channel_link': u'http://vimeo.com', 'channel_description': u"Vimeo is a respectful community of creative people who are passionate about sharing the videos they make.", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0} 01029 01030 # Easier for debugging usually commented out 01031 # data = self.searchTitle(title, pagenumber, self.page_limit) 01032 # print data 01033 # sys.exit() 01034 01035 try: 01036 data = self.searchTitle(title, pagenumber, self.page_limit) 01037 except VimeoVideoNotFound, msg: 01038 sys.stderr.write(u'%s' % msg) 01039 return None 01040 except VimeoUrlError, msg: 01041 sys.stderr.write(self.error_messages['VimeoUrlError'] % msg) 01042 sys.exit(1) 01043 except VimeoHttpError, msg: 01044 sys.stderr.write(self.error_messages['VimeoHttpError'] % msg) 01045 sys.exit(1) 01046 except VimeoResponseError, msg: 01047 sys.stderr.write(self.error_messages['VimeoResponseError'] % msg) 01048 sys.exit(1) 01049 except VimeoAuthorizeTokenError, msg: 01050 sys.stderr.write(self.error_messages['VimeoAuthorizeTokenError'] % msg) 01051 sys.exit(1) 01052 except VimeoVideosSearchError, msg: 01053 sys.stderr.write(self.error_messages['VimeoVideosSearchError'] % msg) 01054 sys.exit(1) 01055 except VimeoRequestTokenError, msg: 01056 sys.stderr.write(self.error_messages['VimeoRequestTokenError'] % msg) 01057 sys.exit(1) 01058 except VimeoException, msg: 01059 sys.stderr.write(self.error_messages['VimeoException'] % msg) 01060 sys.exit(1) 01061 except Exception, e: 01062 sys.stderr.write(u"! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e)) 01063 sys.exit(1) 01064 01065 if data == None: 01066 return None 01067 if not len(data): 01068 return None 01069 01070 items = [] 01071 for match in data: 01072 item_data = {} 01073 for key in self.key_translation[1].keys(): 01074 if key == 'url': 01075 item_data['item_link'] = match[key] 01076 item_data['item_url'] = match[key] 01077 continue 01078 if key in match.keys(): 01079 item_data[self.key_translation[1][key]] = match[key] 01080 else: 01081 item_data[self.key_translation[1][key]] = u'' 01082 items.append(item_data) 01083 01084 self.channel['channel_startindex'] = self.page_limit * int(pagenumber) 01085 self.channel['channel_returned'] = len(items) 01086 01087 if len(items): 01088 return [[self.channel, items]] 01089 return None 01090 # end searchForVideos() 01091 01092 01093 def getChannels(self): 01094 '''Get the channel directory information and fill out the tree view directory structures as required 01095 raise exceptions if the there was an issue getting Channel information or no channel data 01096 return True if successful 01097 ''' 01098 self.channel_dict = {'N_R_S': {}, 'Everything HD': {}, } 01099 self.channel_count = {} 01100 for key in self.tree_key_list: 01101 self.channel_dict['N_R_S'][key] = [] 01102 self.channel_dict['Everything HD'][key] = [] 01103 self.channel_list_max = 2 01104 self.channel_count['N_R_S'] = {'newest': [10, 2], 'most_recently_updated': [10, 1], 'most_subscribed': [10, 10]} 01105 self.channel_count['Everything HD'] = {'newest': [10, 2], 'most_recently_updated': [10, 1], 'most_subscribed': [5, 20]} 01106 01107 for sort in self.tree_key_list: 01108 page = 0 01109 not_HD_count = 0 01110 HD_count = 0 01111 not_HD_list = [] 01112 HD_list = [] 01113 while True: 01114 try: 01115 page+=1 01116 if page > 20: # Throttles excessive searching for HD groups within a category 01117 break 01118 try: 01119 xml_data = self.config[u'methods']['channels'](sort=sort, 01120 per_page=None, 01121 page=page) 01122 except Exception, msg: 01123 raise VimeoAllChannelError(u'%s' % msg) 01124 01125 if xml_data == None: 01126 raise VimeoAllChannelError(self.error_messages['1-VimeoAllChannelError'] % sort) 01127 01128 if not len(xml_data.keys()): 01129 raise VimeoAllChannelError(self.error_messages['2-VimeoAllChannelError'] % sort) 01130 01131 if xml_data.tag == 'rsp': 01132 if not xml_data.get('stat') == 'ok': 01133 if __errmsgs__.has_key(xml_data.get('stat')): 01134 errmsg = __errmsg__[xml_data.get('stat')] 01135 raise VimeoResponseError(u"Error %s - %s" % (xml_data.get('stat'), errmsg)) 01136 else: 01137 errmsg = u'Unknown error' 01138 raise VimeoResponseError(u"Error %s - %s" % (xml_data.get('stat'), errmsg)) 01139 01140 for channel in xml_data.find('channels'): 01141 index = channel.find('name').text.find(u'HD') 01142 if index != -1: 01143 if HD_count < self.channel_count['Everything HD'][sort][0]: 01144 if not channel.get('id') in HD_list: 01145 self.channel_dict['Everything HD'][sort].append([channel.get('id'), channel.find('name').text]) 01146 HD_list.append(channel.get('id')) 01147 HD_count+=1 01148 else: 01149 if not_HD_count < self.channel_count['N_R_S'][sort][0]: 01150 if not channel.get('id') in not_HD_list: 01151 self.channel_dict['N_R_S'][sort].append([channel.get('id'), channel.find('name').text]) 01152 not_HD_list.append(channel.get('id')) 01153 not_HD_count+=1 01154 if not_HD_count >= self.channel_count['N_R_S'][sort][0] and HD_count >= self.channel_count['Everything HD']: 01155 break 01156 if not_HD_count >= self.channel_count['N_R_S'][sort][0] and HD_count >= self.channel_count['Everything HD']: 01157 break 01158 except: 01159 break 01160 01161 return True 01162 #end getChannels() 01163 01164 01165 def displayTreeView(self): 01166 '''Gather the Vimeo Groups/Channels...etc then get a max page of videos meta data in each of them 01167 return array of directories and their video metadata 01168 ''' 01169 try: 01170 self.initializeVimeo() 01171 except VimeoAuthorizeTokenError, msg: 01172 sys.stderr.write(self.error_messages['VimeoAuthorizeTokenError'] % msg) 01173 sys.exit(1) 01174 except VimeoRequestTokenError, msg: 01175 sys.stderr.write(self.error_messages['VimeoRequestTokenError'] % msg) 01176 sys.exit(1) 01177 except VimeoException, msg: 01178 sys.stderr.write(self.error_messages['VimeoException'] % msg) 01179 sys.exit(1) 01180 except Exception, msg: 01181 sys.stderr.write(u"! Error: Unknown error during a Vimeo API initialization (%s)\n" % msg) 01182 sys.exit(1) 01183 01184 # Channel details and search results 01185 self.channel = {'channel_title': u'Vimeo', 'channel_link': u'http://vimeo.com', 'channel_description': u"Vimeo is a respectful community of creative people who are passionate about sharing the videos they make.", 'channel_numresults': 0, 'channel_returned': 1, u'channel_startindex': 0} 01186 01187 if self.config['debug_enabled']: 01188 print self.config[u'methods'] 01189 print 01190 01191 # Initialize the Video channels data 01192 try: 01193 self.getChannels() 01194 except VimeoResponseError, msg: 01195 sys.stderr.write(self.error_messages['VimeoResponseError'] % msg) 01196 sys.exit(1) 01197 except VimeoAllChannelError, msg: 01198 sys.stderr.write(self.error_messages['VimeoAllChannelError'] % msg) 01199 sys.exit(1) 01200 except VimeoException, msg: 01201 sys.stderr.write(self.error_messages['VimeoException'] % msg) 01202 sys.exit(1) 01203 except Exception, msg: 01204 sys.stderr.write(u"! Error: Unknown error while getting all Channels (%s)\n" % msg) 01205 sys.exit(1) 01206 01207 dictionaries = [] 01208 01209 self.treeview = True 01210 01211 # Process the various video feeds/categories/... etc 01212 for key in self.tree_order: 01213 self.tree_key = key 01214 dictionaries = self.getVideos(self.tree_org[key], dictionaries) 01215 01216 return [[self.channel, dictionaries]] 01217 # end displayTreeView() 01218 01219 01220 def getVideos(self, dir_dict, dictionaries): 01221 '''Parse a list made of category lists and retrieve video meta data 01222 return a dictionary of directory names and categories video metadata 01223 ''' 01224 for sets in dir_dict: 01225 if not isinstance(sets[1], list): 01226 if sets[0] != '': # Add the nested dictionaries display name 01227 try: 01228 dictionaries.append([self.massageDescription(sets[0]), self.setTreeViewIcon(self.feed_icons[self.tree_key][sets[0]])]) 01229 except KeyError: 01230 dictionaries.append([self.massageDescription(sets[0]), self.channel_icon]) 01231 else: 01232 dictionaries.append(['', u'']) # Add the nested dictionary indicator 01233 continue 01234 temp_dictionary = [] 01235 for self.feed in sets[1]: 01236 if self.config[u'methods'][u'tree.view'][self.tree_key].has_key('__all__'): 01237 URL = self.config[u'methods'][u'tree.view'][self.tree_key]['__all__'] 01238 else: 01239 URL = self.config[u'methods'][u'tree.view'][self.tree_key][self.feed] 01240 temp_dictionary = self.config['item_parser'][URL[1]](self.config[u'methods'][URL[0]], temp_dictionary) 01241 if len(temp_dictionary): 01242 if len(sets[0]): # Add the nested dictionaries display name 01243 try: 01244 dictionaries.append([self.massageDescription(sets[0]), self.setTreeViewIcon(self.feed_icons[self.tree_key][sets[0]])]) 01245 except KeyError: 01246 dictionaries.append([self.massageDescription(sets[0]), self.channel_icon]) 01247 for element in temp_dictionary: 01248 dictionaries.append(element) 01249 if len(sets[0]): 01250 dictionaries.append(['', u'']) # Add the nested dictionary indicator 01251 return dictionaries 01252 # end getVideos() 01253 01254 01255 def getVideosForChannels(self, method, dictionary): 01256 '''Process all channel related directory videos 01257 return dictionary of new video items 01258 ''' 01259 self.next_page = 1 01260 self.max_page = self.channel_count[self.tree_key][self.feed][1] 01261 self.tree_list = False 01262 # Sometimes it is beter to see a list of videos rather than split them up by their channel names 01263 if self.max_page <= self.channel_list_max: 01264 self.tree_list = True 01265 tmp_dictionary = [] 01266 for channel in self.channel_dict[self.tree_key][self.feed]: 01267 self.channel_id = channel[0] 01268 self.dir_name = channel[1] 01269 01270 # Easier for debugging 01271 #tmp_dictionary = self.getTreeVideos(method, tmp_dictionary) 01272 01273 try: 01274 tmp_dictionary = self.getTreeVideos(method, tmp_dictionary) 01275 except VimeoVideoNotFound, msg: 01276 sys.stderr.write(self.error_messages['VimeoVideoNotFound'] % msg) 01277 continue 01278 except VimeoVideosSearchError, msg: 01279 sys.stderr.write(self.error_messages['VimeoVideosSearchError'] % msg) 01280 sys.exit(1) 01281 except VimeoResponseError, msg: 01282 sys.stderr.write(self.error_messages['VimeoResponseError'] % msg) 01283 sys.exit(1) 01284 except VimeoException, msg: 01285 sys.stderr.write(self.error_messages['VimeoException'] % msg) 01286 sys.exit(1) 01287 except Exception, e: 01288 sys.stderr.write(u"! Error: Unknown error during while getting all Channels (%s)\nError(%s)\n" % (self.dir_name, e)) 01289 sys.exit(1) 01290 01291 if len(tmp_dictionary): 01292 dictionary.append([self.feed_names[self.tree_key][self.feed], self.setTreeViewIcon()]) 01293 for element in tmp_dictionary: 01294 dictionary.append(element) 01295 dictionary.append(['', u'']) 01296 return dictionary 01297 # end getVideosForChannels() 01298 01299 01300 01301 def getTreeVideos(self, method, dictionaries): 01302 '''Get the video metadata for url search 01303 return the video dictionary of directories and their video mata data 01304 ''' 01305 initial_length = len(dictionaries) 01306 try: 01307 xml_data = method(channel_id=self.channel_id, full_response=1, 01308 per_page=self.max_page, 01309 page=self.next_page) 01310 except Exception, msg: 01311 raise VimeoVideosSearchError(u'%s' % msg) 01312 01313 if xml_data == None: 01314 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % self.dir_name) 01315 01316 if not len(xml_data.keys()): 01317 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % self.dir_name) 01318 01319 if xml_data.tag == 'rsp': 01320 if not xml_data.get('stat') == 'ok': 01321 if __errmsgs__.has_key(xml_data.get('stat')): 01322 errmsg = __errmsg__[xml_data.get('stat')] 01323 raise VimeoResponseError(u"Error %s - %s" % (xml_data.get('stat'), errmsg)) 01324 else: 01325 errmsg = u'Unknown error' 01326 raise VimeoResponseError(u"Error %s - %s" % (xml_data.get('stat'), errmsg)) 01327 01328 videos = xml_data.find(u"videos") 01329 if videos: 01330 if videos.get('total'): 01331 self.channel['channel_numresults'] = int(videos.get('total')) 01332 01333 # Collect video meta data that matched the search value 01334 dictionary_first = False 01335 for video in xml_data.find(u"videos").getchildren(): 01336 hd_flag = False 01337 embed_flag = False 01338 if video.tag == 'video': 01339 if video.get('embed_privacy') == "anywhere": 01340 embed_flag = True 01341 if video.get('is_hd') == "1": 01342 hd_flag = True 01343 v_details = {} 01344 for details in video.getchildren(): 01345 if details.tag in ['tags', 'cast']: 01346 continue 01347 if details.tag == 'width': 01348 if details.text: 01349 v_details['width'] = details.text.strip() 01350 continue 01351 if details.tag == 'height': 01352 if details.text: 01353 v_details['height'] = details.text.strip() 01354 continue 01355 if details.tag == 'duration': 01356 if details.text: 01357 v_details['duration'] = details.text.strip() 01358 continue 01359 if details.tag == 'owner': 01360 if details.text: 01361 v_details['display_name'] = self.massageDescription(details.get('display_name').strip()) 01362 else: 01363 v_details['display_name'] = u'' 01364 continue 01365 if details.tag == 'description': 01366 if details.text: 01367 if self.tree_list: 01368 v_details[details.tag] = self.massageDescription(u'Channel "%s": %s' % (self.dir_name, details.text.strip())) 01369 else: 01370 v_details[details.tag] = self.massageDescription(details.text.strip()) 01371 else: 01372 if self.tree_list: 01373 v_details[details.tag] = self.massageDescription(u'Channel "%s"' % self.dir_name) 01374 continue 01375 if details.tag == 'upload_date': 01376 if details.text: 01377 pub_time = time.strptime(details.text.strip(), "%Y-%m-%d %H:%M:%S") 01378 v_details[details.tag] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', pub_time) 01379 else: 01380 v_details[details.tag] = u'' 01381 continue 01382 if details.tag == 'urls': 01383 for url in details.getchildren(): 01384 if url.get('type') == 'video': 01385 if url.text: # Make the link fullscreen and auto play 01386 if embed_flag: 01387 v_details[url.tag] = self.processVideoUrl(url.text.strip()) 01388 else: 01389 v_details[url.tag] = self.ampReplace(url.text.strip()) 01390 else: 01391 v_details[url.tag] = u'' 01392 continue 01393 if details.tag == 'thumbnails': 01394 largest = 0 01395 for image in details.getchildren(): 01396 if image.tag == 'thumbnail': 01397 height = int(image.get('height')) 01398 width = int(image.get('width')) 01399 default = image.text.find('default') 01400 if largest < height * width and not default != -1: 01401 if image.text: 01402 v_details[image.tag] = self.ampReplace(image.text.strip()) 01403 if width >= 200: 01404 break 01405 continue 01406 if details.text: 01407 v_details[details.tag] = self.massageDescription(details.text.strip()) 01408 else: 01409 v_details[details.tag] = u'' 01410 01411 if hd_flag and not v_details.has_key('width'): 01412 v_details['width'] = 1280 01413 v_details['height'] = 720 01414 01415 if not v_details.has_key('url'): 01416 continue 01417 01418 if not dictionary_first and not self.tree_list: # Add the dictionaries display name 01419 dictionaries.append([self.dir_name, self.setTreeViewIcon()]) 01420 dictionary_first = True 01421 01422 final_item = {} 01423 for key in self.key_translation[1].keys(): 01424 if key == 'url': 01425 final_item['item_link'] = v_details[key] 01426 final_item['item_url'] = v_details[key] 01427 continue 01428 if v_details.has_key(key): 01429 final_item[self.key_translation[1][key]] = v_details[key] 01430 else: 01431 final_item[self.key_translation[1][key]] = u'' 01432 dictionaries.append(final_item) 01433 01434 # Need to check if there was any items for this directory 01435 if initial_length < len(dictionaries) and not self.tree_list: 01436 dictionaries.append(['', u'']) # Add the nested dictionary indicator 01437 return dictionaries 01438 # end getTreeVideos() 01439 # end Videos() class
1.7.6.1