MythTV master
vimeo_api.py
Go to the documentation of this file.
1# -*- coding: utf-8 -*-
2
3# Copyright (C) 2009 Marc Poulhiès
4#
5# Python module for Vimeo
6# originaly part of 'plopifier'
7#
8# Plopifier is free software: you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation, either version 3 of the License, or
11# (at your option) any later version.
12#
13# Plopifier is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with Plopifier. If not, see <http://www.gnu.org/licenses/>.
20# ------------------------------------------------------------------
21# Name: vimeo_api - Simple-to-use Python interface to the vimeo API (http://vimeo.com)
22# Python Script
23# Author: Marc Poulhiès and modified by R.D. Vaughan
24# Purpose: This python script is intended to perform a variety of utility functions to search and access text
25# metadata and video/image URLs from vimeo. These routines are based on the v2 api. Specifications
26# for this api are published at http://vimeo.com/api/docs/advanced-api
27#
28# License:Creative Commons GNU GPL v2
29# (http://creativecommons.org/licenses/GPL/2.0/)
30#-------------------------------------
31__title__ ="vimeo_api - Simple-to-use Python interface to the vimeo API (http://vimeo.com)"
32__author__="Marc Poulhiès and modified by R.D. Vaughan"
33__purpose__='''
34 This python script is intended to perform a variety of utility functions to search and access text
35metadata and video/image URLs from vimeo. These routines are based on the v2 api. Specifications
36for this api are published at http://vimeo.com/api/docs/advanced-api
37'''
38
39__version__="v0.2.5"
40# 0.1.0 Initial development
41# 0.1.1 Added Tree view processing
42# 0.1.2 Documentation review
43# 0.2.0 Public release
44# 0.2.1 Fixed bug where some videos cannot be embedded (played fullscreen automatically)
45# 0.2.2 New python bindings conversion
46# Better exception error reporting
47# Better handling of invalid unicode data from source
48# 0.2.3 Completed the exception error reporting improvements
49# Fixed an exception message error when vimeo returns poorly formed XML.
50# For example search term "flute" returns bad XML while "Flute music" returns proper XML
51# 0.2.4 Fixed an exception message output code error
52# 0.2.5 Removed the need for python MythTV bindings and added "%SHAREDIR%" to icon directory path
53
54"""
55Python module to interact with Vimeo through its API (version 2)
56"""
57import os, struct, sys, re, time, datetime
58import urllib.request, urllib.parse, urllib.error, urllib.request, urllib.error, urllib.parse
59import logging
60import pycurl
61import xml.etree.ElementTree as ET
62import inspect
63from .oauth import oauth_api as oauth
64from MythTV import MythXML
65
66from .vimeo_exceptions import (VimeoUrlError, VimeoHttpError, VimeoResponseError, VimeoVideoNotFound, VimeoRequestTokenError, VimeoAuthorizeTokenError, VimeoVideosSearchError, VimeoAllChannelError, __errmsgs__)
67
68from .vimeo_data import getData
69
70import io
71
72
73REQUEST_TOKEN_URL = 'http://vimeo.com/oauth/request_token'
74ACCESS_TOKEN_URL = 'http://vimeo.com/oauth/access_token'
75AUTHORIZATION_URL = 'http://vimeo.com/oauth/authorize'
76
77API_REST_URL = 'http://vimeo.com/api/rest/v2/'
78API_V2_CALL_URL = 'http://vimeo.com/api/v2/'
79
80USER_AGENT = 'python-vimeo http://github.com/dkm/python-vimeo'
81
82PORT=80
83
84HMAC_SHA1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
85
86
87class VimeoException(Exception):
88 def __init__(self, msg):
89 Exception.__init__(self)
90 self.msg = msg
91
92 def __str__(self):
93 return self.msg
94
95class CurlyRestException(Exception):
96 def __init__(self, code, msg, full):
97 Exception.__init__(self)
98 self.code = code
99 self.msg = msg
100 self.full = full
101
102 def __str__(self):
103 return "Error code: %s, message: %s\nFull message: %s" % (self.code,
104 self.msg,
105 self.full)
106
107
109 """
110 A CurlyRequest object is used to send HTTP requests.
111 It's a simple wrapper around basic curl methods.
112 In particular, it can upload files and display a progress bar.
113 """
114 def __init__(self, pbarsize=19):
115 self.buf = None
116 self.pbar_size = pbarsize
117 self.pidx = 0
118 self.debug = False
119
120 def do_rest_call(self, url):
121 """
122 Send a simple GET request and interpret the answer as a REST reply.
123 """
124
125 res = self.do_request(url)
126 try:
127 t = ET.fromstring(res)
128
129 if t.attrib['stat'] == 'fail':
130 err_code = t.find('err').attrib['code']
131 err_msg = t.find('err').attrib['msg']
132 raise Exception(err_code, err_msg, ET.tostring(t))
133 return t
134 except Exception as e:
135 raise Exception('%s' % (e))
136
137 def _body_callback(self, buf):
138 self.buf += buf
139
140 def do_request(self, url):
141 """
142 Send a simple GET request
143 """
144 if self.debug:
145 print("Request URL:")
146 print(url)
147 print()
148
149 self.buf = ""
150 curl = pycurl.Curl()
151 curl.setopt(pycurl.USERAGENT, USER_AGENT)
152 curl.setopt(curl.URL, url)
153 curl.setopt(curl.WRITEFUNCTION, self._body_callback)
154 curl.perform()
155 curl.close()
156 p = self.buf
157 self.buf = ""
158
159 if self.debug:
160 print("Raw response:")
161 print(p)
162 print()
163
164 return p
165
166 def _upload_progress(self, download_t, download_d, upload_t, upload_d):
167 # this is only for upload progress bar
168 if upload_t == 0:
169 return 0
170
171 self.pidx = (self.pidx + 1) % len(TURNING_BAR)
172
173 done = int(self.pbar_size * upload_d / upload_t)
174
175 if done != self.pbar_size:
176 pstr = '#'*done +'>' + ' '*(self.pbar_size - done - 1)
177 else:
178 pstr = '#'*done
179
180 print("\r%s[%s] " %(TURNING_BAR[self.pidx], pstr), end=' ')
181 return 0
182
183 def do_post_call(self, url, args, use_progress=False):
184 """
185 Send a simple POST request
186 """
187 c = pycurl.Curl()
188 c.setopt(c.POST, 1)
189 c.setopt(c.URL, url)
190 c.setopt(c.HTTPPOST, args)
191 c.setopt(c.WRITEFUNCTION, self.body_callback)
192 #c.setopt(c.VERBOSE, 1)
193 self.buf = ""
194
195 c.setopt(c.NOPROGRESS, 0)
196
197 if use_progress:
198 c.setopt(c.PROGRESSFUNCTION, self._upload_progress)
199
200 c.perform()
201 c.close()
202 res = self.buf
203 self.buf = ""
204 return res
205
206class SimpleOAuthClient(oauth.OAuthClient):
207 """
208 Class used for handling authenticated call to the API.
209 """
210
211 def __init__(self, key, secret,
212 server="vimeo.com", port=PORT,
213 request_token_url=REQUEST_TOKEN_URL,
214 access_token_url=ACCESS_TOKEN_URL,
215 authorization_url=AUTHORIZATION_URL,
216 token=None,
217 token_secret=None):
218 """
219 You need to give both key (consumer key) and secret (consumer secret).
220 If you already have an access token (token+secret), you can use it
221 by giving it through token and token_secret parameters.
222 If not, then you need to call both get_request_token(), get_authorize_token_url() and
223 finally get_access_token().
224 """
225
227 self.key = key
228 self.secret = secret
229 self.server = server
230 self.port = PORT
231 self.request_token_url = request_token_url
232 self.access_token_url = access_token_url
233 self.authorization_url = authorization_url
234 self.consumer = oauth.OAuthConsumer(self.key, self.secret)
235
236 if token is not None and token_secret is not None:
237 self.token = oauth.OAuthToken(token, token_secret)
238 else:
239 self.token = None
240
242 """
243 Requests a request token and return it on success.
244 """
245 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
246 http_url=self.request_token_url,
247 callback="oob")
248 oauth_request.sign_request(HMAC_SHA1, self.consumer, None)
249 self.token = self._fetch_token(oauth_request)
250
251
253 """
254 Returns a URL used to verify and authorize the application to access
255 user's account. The pointed page should contain a simple 'password' that
256 acts as the 'verifier' in oauth.
257 """
258
259 oauth_request = oauth.OAuthRequest.from_token_and_callback(token=self.token,
260 http_url=self.authorization_url)
261 return oauth_request.to_url()
262
263
264 def get_access_token(self, verifier):
265 """
266 Should be called after having received the 'verifier' from the authorization page.
267 See 'get_authorize_token_url()' method.
268 """
269
270 self.token.set_verifier(verifier)
271 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
272 token=self.token,
273 verifier=verifier,
274 http_url=self.access_token_url)
275 oauth_request.sign_request(HMAC_SHA1, self.consumer, self.token)
276 self.token = self._fetch_token(oauth_request)
277
278 def _fetch_token(self, oauth_request):
279 """
280 Sends a requests and interprets the result as a token string.
281 """
282 ans = self.curly.do_request(oauth_request.to_url())
283 return oauth.OAuthToken.from_string(ans)
284
285 def vimeo_oauth_checkAccessToken(self, auth_token):
286 pass
287
288
289 def _do_vimeo_authenticated_call(self, method, parameters={}):
290 """
291 Wrapper to send an authenticated call to vimeo. You first need to have
292 an access token.
293 """
294
295 parameters['method'] = method
296 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
297 token=self.token,
298 http_method='GET',
299 http_url=API_REST_URL,
300 parameters=parameters)
301 oauth_request.sign_request(HMAC_SHA1, self.consumer, self.token)
302 return self.curly.do_rest_call(oauth_request.to_url())
303
304 def _do_vimeo_unauthenticated_call(self, method, parameters={}):
305 """
306 Wrapper to send an unauthenticated call to vimeo. You don't need to have
307 an access token.
308 """
309 parameters['method'] = method
310 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
311 http_method='GET',
312 http_url=API_REST_URL,
313 parameters=parameters)
314 oauth_request.sign_request(HMAC_SHA1, self.consumer, None)
315 return self.curly.do_rest_call(oauth_request.to_url())
316
317
320 def vimeo_albums_getAll(self, user_id, sort=None,
321 per_page=None,
322 page=None):
323 """
324 Get a list of a user's albums.
325 This method does not require authentication.
326 """
327 params = {'user_id': user_id}
328 if sort in ('newest', 'oldest', 'alphabetical'):
329 params['sort'] = sort
330 if per_page is not None:
331 params['per_page'] = per_page
332 if page is not None:
333 params['page'] = page
334 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'),
335 parameters=params)
336
337
340 def vimeo_videos_search(self, query, sort=None,
341 per_page=None,
342 page=None):
343 """
344 Search for matching Videos.
345 This method does not require authentication.
346 """
347 params = {}
348 if sort in ('newest', 'most_played', 'relevant', 'most_liked', 'oldest'):
349 params['sort'] = sort
350 else:
351 params['sort'] = 'most_liked'
352 if per_page is not None:
353 params['per_page'] = per_page
354 if page is not None:
355 params['page'] = page
356 params['full_response'] = '1'
357 #params['query'] = query.replace(u' ', u'_')
358 params['query'] = query
359 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'),
360 parameters=params)
361
362
365 def vimeo_channels_getAll(self, sort=None,
366 per_page=None,
367 page=None):
368 """
369 Get a list of all public channels.
370 This method does not require authentication.
371 """
372 params = {}
373 if sort in ('newest', 'oldest', 'alphabetical',
374 'most_videos', 'most_subscribed', 'most_recently_updated'):
375 params['sort'] = sort
376 if per_page is not None:
377 params['per_page'] = per_page
378 if page is not None:
379 params['page'] = page
380
381 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'),
382 parameters=params)
383
384 def vimeo_channels_getVideos(self, channel_id=None, full_response=None,
385 per_page=None,
386 page=None):
387 """
388 Get a list of Videos for a specific channels.
389 This method does not require authentication.
390 """
391 # full_response channel_id
392 params = {}
393 if channel_id is not None:
394 params['channel_id'] = channel_id
395 if full_response is not None:
396 params['full_response'] = 1
397 if per_page is not None:
398 params['per_page'] = per_page
399 if page is not None:
400 params['page'] = page
401
402 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'),
403 parameters=params)
404
405
406
409
410
411
414
415
418
419
422
423
426
427
430
431
434 def vimeo_test_echo(self, params={}):
435 """
436 This will just repeat back any parameters that you send.
437 No auth required
438 """
439
442 return self._do_vimeo_unauthenticated_call(inspect.stack()[0][3].replace('_', '.'),
443 parameters=params)
444
445
447 """
448 Is the user logged in?
449 """
450 return self._do_vimeo_authenticated_call(inspect.stack()[0][3].replace('_', '.'))
451
452
454 """
455 This is just a simple null/ping test.
456
457 You can use this method to make sure that you are properly
458 contacting to the Vimeo API.
459 """
460 return self._do_vimeo_authenticated_call(inspect.stack()[0][3].replace('_', '.'))
461
462
463
466
467
470
471
474
475
476
479
481 """
482 (from vimeo API documentation)
483 Get the space and number of HD uploads left for a user.
484
485 Numbers are provided in bytes. It's a good idea to check this
486 method before you upload a video to let the user know if their
487 video will be converted to HD. hd_quota will have a value of 0
488 if the user reached the max number of uploads, 1
489 otherwise. Resets is the number of the day of the week,
490 starting with Sunday.
491 """
492 return self._do_vimeo_authenticated_call(inspect.stack()[0][3].replace('_', '.'))
493
494
495
496
497def _simple_request(url, format):
498 if format != 'xml':
499 raise VimeoException("Sorry, only 'xml' supported. '%s' was requested." %format)
500
501 curly = CurlyRequest()
502 url = url %(format)
503 ans = curly.do_request(url)
504
505 if format == 'xml':
506 return ET.fromstring(ans)
507
508
512
513def _user_request(user, info, format):
514 url = API_V2_CALL_URL + '%s/%s.%%s' %(user,info)
515 return _simple_request(url, format)
516
517def user_info(user, format="xml"):
518 """
519 User info for the specified user
520 """
521 return _user_request(user, inspect.stack()[0][3][5:], format)
522
523
524def user_videos(user, format="xml"):
525 """
526 Videos created by user
527 """
528 return _user_request(user, inspect.stack()[0][3][5:], format)
529
530def user_likes(user, format="xml"):
531 """
532 Videos the user likes
533 """
534 return _user_request(user, inspect.stack()[0][3][5:], format)
535
536def user_appears_in(user, format="xml"):
537 """
538 Videos that the user appears in
539 """
540 return _user_request(user, inspect.stack()[0][3][5:], format)
541
542def user_all_videos(user, format="xml"):
543 """
544 Videos that the user appears in and created
545 """
546 return _user_request(user, inspect.stack()[0][3][5:], format)
547
548def user_subscriptions(user, format="xml"):
549 """
550 Videos the user is subscribed to
551 """
552 return _user_request(user, inspect.stack()[0][3][5:], format)
553
554def user_albums(user, format="xml"):
555 """
556 Albums the user has created
557 """
558 return _user_request(user, inspect.stack()[0][3][5:], format)
559
560def user_channels(user, format="xml"):
561 """
562 Channels the user has created and subscribed to
563 """
564 return _user_request(user, inspect.stack()[0][3][5:], format)
565
566def user_groups(user, format="xml"):
567 """
568 Groups the user has created and joined
569 """
570 return _user_request(user, inspect.stack()[0][3][5:], format)
571
572def user_contacts_videos(user, format="xml"):
573 """
574 Videos that the user's contacts created
575 """
576 return _user_request(user, inspect.stack()[0][3][5:], format)
577
578def user_contacts_like(user, format="xml"):
579 """
580 Videos that the user's contacts like
581 """
582 return _user_request(user, inspect.stack()[0][3][5:], format)
583
584
585
588def video_request(video, format):
589 url = API_V2_CALL_URL + 'video/%s.%%s' %(video)
590 return _simple_request(url)
591
592
595
596class OutStreamEncoder(object):
597 """Wraps a stream with an encoder"""
598 def __init__(self, outstream, encoding=None):
599 self.out = outstream
600 if not encoding:
601 self.encoding = sys.getfilesystemencoding()
602 else:
603 self.encoding = encoding
604
605 def write(self, obj):
606 """Wraps the output stream, encoding Unicode strings with the specified encoding"""
607 if isinstance(obj, str):
608 obj = obj.encode(self.encoding)
609 self.out.buffer.write(obj)
610
611 def __getattr__(self, attr):
612 """Delegate everything but write to the stream"""
613 return getattr(self.out, attr)
614
615if isinstance(sys.stdout, io.TextIOWrapper):
616 sys.stdout = OutStreamEncoder(sys.stdout, 'utf8')
617 sys.stderr = OutStreamEncoder(sys.stderr, 'utf8')
618
619
620class Videos(object):
621 """Main interface to http://vimeo.com/
622 This is done to support a common naming framework for all python Netvision plugins no matter their site
623 target.
624
625 Supports search and tree view methods
626 The apikey is a not passed but is created as a session token to access http://vimeo.com/
627 """
628 def __init__(self,
629 apikey,
630 mythtv = True,
631 interactive = False,
632 select_first = False,
633 debug = False,
634 custom_ui = None,
635 language = None,
636 search_all_languages = False,
637 ):
638 """apikey (str/unicode):
639 Specify the target site API key. Applications need their own key in some cases
640
641 mythtv (True/False):
642 When True, the returned meta data is being returned has the key and values massaged to match MythTV
643 When False, the returned meta data is being returned matches what target site returned
644
645 interactive (True/False): (This option is not supported by all target site apis)
646 When True, uses built-in console UI is used to select the correct show.
647 When False, the first search result is used.
648
649 select_first (True/False): (This option is not supported currently implemented in any grabbers)
650 Automatically selects the first series search result (rather
651 than showing the user a list of more than one series).
652 Is overridden by interactive = False, or specifying a custom_ui
653
654 debug (True/False):
655 shows verbose debugging information
656
657 custom_ui (xx_ui.BaseUI subclass): (This option is not supported currently implemented in any grabbers)
658 A callable subclass of interactive class (overrides interactive option)
659
660 language (2 character language abbreviation): (This option is not supported by all target site apis)
661 The language of the returned data. Is also the language search
662 uses. Default is "en" (English). For full list, run..
663
664 search_all_languages (True/False): (This option is not supported by all target site apis)
665 By default, a Netvision grabber will only search in the language specified using
666 the language option. When this is True, it will search for the
667 show in any language
668
669 """
670 self.config = {}
671 self.mythxml = MythXML()
672
673 self.config['debug_enabled'] = debug # show debugging messages
674
675 self.log_name = "vimeo"
676 self.log = self._initLogger() # Setups the logger (self.log.debug() etc)
677
678 self.config['custom_ui'] = custom_ui
679
680 self.config['interactive'] = interactive # prompt for correct series?
681
682 self.config['select_first'] = select_first
683
684 self.config['search_all_languages'] = search_all_languages
685
686 # Defaulting to ENGISH but the vimeo.com apis do not support specifying a language
687 self.config['language'] = "en"
688
689 self.error_messages = {'VimeoUrlError': "! Error: The URL (%s) cause the exception error (%s)\n", 'VimeoHttpError': "! Error: An HTTP communications error with vimeo.com was raised (%s)\n", 'VimeoResponseError': "! Error: Invalid XML metadata\nwas received from vimeo.com error (%s). Skipping item.\n", 'VimeoVideoNotFound': "! Error: Video search with vimeo.com did not return any results for (%s)\n", 'VimeoRequestTokenError': "! Error: Vimeo get request token failed (%s)\n", 'VimeoAuthorizeTokenError': "! Error: Video get authorise token failed (%s)\n", 'VimeoVideosSearchError': "! Error: Video search failed (%s)\n", 'VimeoException': "! Error: VimeoException (%s)\n", 'VimeoAllChannelError': "! Error: Access Video Channel information failed (%s)\n", }
690
691 # This is an example that must be customized for each target site
692 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'}]
693
694 # The following url_ configs are based of the
695 # http://vimeo.com/api/docs/advanced-api
696 self.config['methods'] = {}
697 # Methods are actually set in initializeVimeo()
698 self.config['methods']['channels'] = None
699 self.config['methods']['channels_videos'] = None
700
701 # Functions that parse video data from RSS data
702 self.config['item_parser'] = {}
703 self.config['item_parser']['channels'] = self.getVideosForChannels
704
705 # Tree view url and the function that parses that urls meta data
706 self.config['methods']['tree.view'] = {
707 'N_R_S': {
708 '__all__': ['channels_videos', 'channels'],
709 },
710 'Everything HD': {
711 '__all__': ['channels_videos', 'channels'],
712 },
713 }
714
715 self.config['image_extentions'] = ["png", "jpg", "bmp"] # Acceptable image extentions
716
717 self.tree_key_list = ['newest', 'most_recently_updated', 'most_subscribed']
718
719 self.tree_order = ['N_R_S', 'Everything HD', ]
720
721 self.tree_org = {
722 'N_R_S': [
723 ['Newest Channels/Most ...', ''],
724 ['', self.tree_key_list],
725 ['','']
726 ],
727 'Everything HD': [
728 ['Everything HD: Newest Channels/Most ...', self.tree_key_list ]
729 ],
730 }
731
733 'N_R_S': {
734 '__default__': { },
735 #'cat name': {},
736 },
737 'Everything HD': {
738 '__default__': { },
739 #'cat name': {},
740 },
741 }
742
743 self.feed_names = {
744 'N_R_S': {'newest': 'Most Recent Channels', 'most_recently_updated': "This Month's Channels'", 'most_subscribed': 'Most Subscribed Channels',
745 },
746 'Everything HD': {'newest': 'Most Recent Channels', 'most_recently_updated': "This Month's Channels'", 'most_subscribed': 'Most Subscribed Channels',
747 },
748
749 }
750
751 self.feed_icons = {
752 'N_R_S': {'newest': 'directories/topics/most_recent', 'most_recently_updated': 'directories/topics/month', 'most_subscribed': 'directories/topics/most_subscribed',
753 },
754 'Everything HD': {'newest': 'directories/topics/most_recent', 'most_recently_updated': 'directories/topics/month', 'most_subscribed': 'directories/topics/most_subscribed', 'Everything HD': 'directories/topics/hd'
755 },
756
757 }
758
759 # Initialize the tree view flag so that the item parsing code can be used for multiple purposes
760 self.treeview = False
761 self.channel_icon = '%SHAREDIR%/mythnetvision/icons/vimeo.jpg'
762 # end __init__()
763
764
769
770 def massageDescription(self, text):
771 '''Removes HTML markup from a text string.
772 @param text The HTML source.
773 @return The plain text. If the HTML source contains non-ASCII
774 entities or character references, this is a Unicode string.
775 '''
776 def fixup(m):
777 text = m.group(0)
778 if text[:1] == "<":
779 return "" # ignore tags
780 if text[:2] == "&#":
781 try:
782 if text[:3] == "&#x":
783 return chr(int(text[3:-1], 16))
784 else:
785 return chr(int(text[2:-1]))
786 except ValueError:
787 pass
788 elif text[:1] == "&":
789 import html.entities
790 entity = html.entities.entitydefs.get(text[1:-1])
791 if entity:
792 if entity[:2] == "&#":
793 try:
794 return chr(int(entity[2:-1]))
795 except ValueError:
796 pass
797 else:
798 return str(entity, "iso-8859-1")
799 return text # leave as is
800 return self.ampReplace(re.sub(r"(?s)<[^>]*>|&#?\w+;", fixup, self.textUtf8(text))).replace('\n',' ')
801 # end massageDescription()
802
803
804 def _initLogger(self):
805 """Setups a logger using the logging module, returns a log object
806 """
807 logger = logging.getLogger(self.log_name)
808 formatter = logging.Formatter('%(asctime)s) %(levelname)s %(message)s')
809
810 hdlr = logging.StreamHandler(sys.stdout)
811
812 hdlr.setFormatter(formatter)
813 logger.addHandler(hdlr)
814
815 if self.config['debug_enabled']:
816 logger.setLevel(logging.DEBUG)
817 else:
818 logger.setLevel(logging.WARNING)
819 return logger
820 #end initLogger
821
822
823 def textUtf8(self, text):
824 if text is None:
825 return text
826 try:
827 return str(text, 'utf8')
828 except UnicodeDecodeError:
829 return ''
830 except (UnicodeEncodeError, TypeError):
831 return text
832 # end textUtf8()
833
834
835 def ampReplace(self, text):
836 '''Replace all "&" characters with "&amp;"
837 '''
838 text = self.textUtf8(text)
839 return text.replace('&amp;','~~~~~').replace('&','&amp;').replace('~~~~~', '&amp;')
840 # end ampReplace()
841
842
843 def setTreeViewIcon(self, dir_icon=None):
844 '''Check if there is a specific generic tree view icon. If not default to the channel icon.
845 return self.tree_dir_icon
846 '''
848 if not dir_icon:
849 if self.tree_key not in self.feed_icons:
850 return self.tree_dir_icon
851 if self.feed not in self.feed_icons[self.tree_key]:
852 return self.tree_dir_icon
853 dir_icon = self.feed_icons[self.tree_key][self.feed]
854 if not dir_icon:
855 return self.tree_dir_icon
856 self.tree_dir_icon = '%%SHAREDIR%%/mythnetvision/icons/%s.png' % (dir_icon, )
857 return self.tree_dir_icon
858 # end setTreeViewIcon()
859
860
865
867 '''Initialize acccess methods for all Vimeo API calls
868 raise errors if there are issues during the initalization steps
869 return nothing
870 '''
871 #
872 self.client = SimpleOAuthClient(getData().update(getData().a), getData().update(getData().s))
873 if self.config['debug_enabled']:
874 self.client.curly.debug = True
875 try:
876 self.client.get_request_token()
877 except Exception as msg:
878 raise VimeoRequestTokenError('%s', msg)
879 try:
880 self.client.get_authorize_token_url()
881 except Exception as msg:
882 raise VimeoAuthorizeTokenError('%s', msg)
883
884 self.config['methods']['channels'] = self.client.vimeo_channels_getAll
885 self.config['methods']['channels_videos'] = self.client.vimeo_channels_getVideos
886
887 # end initializeVimeo()
888
889 def processVideoUrl(self, url):
890 playerUrl = self.mythxml.getInternetContentUrl("nv_python_libs/configs/HTML/vimeo.html", \
891 url.replace('http://vimeo.com/', ''))
892 return self.ampReplace(playerUrl)
893
894 def searchTitle(self, title, pagenumber, pagelen):
895 '''Key word video search of the vimeo.com web site
896 return an array of matching item dictionaries
897 return
898 '''
899 self.initializeVimeo()
900
901 # Used for debugging usually commented out
902# xml_data = self.client.vimeo_videos_search(title, sort='most_liked',
903# per_page=pagelen,
904# page=pagenumber)
905# print xml_data
906
907 try:
908 xml_data = self.client.vimeo_videos_search(urllib.parse.quote_plus(title.encode("utf-8")),
909 sort='most_liked',
910 per_page=pagelen,
911 page=pagenumber)
912 except Exception as msg:
913 raise VimeoVideosSearchError('%s' % msg)
914
915 if xml_data is None:
916 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title)
917
918 if not len(list(xml_data.keys())):
919 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title)
920
921 if xml_data.tag == 'rsp':
922 if not xml_data.get('stat') == 'ok':
923 if xml_data.get('stat') in __errmsgs__:
924 errmsg = __errmsg__[xml_data.get('stat')]
925 raise VimeoResponseError("Error %s - %s" % (xml_data.get('stat'), errmsg))
926 else:
927 errmsg = 'Unknown error'
928 raise VimeoResponseError("Error %s - %s" % (xml_data.get('stat'), errmsg))
929
930 elements_final = []
931 videos = xml_data.find("videos")
932 if videos:
933 if videos.get('total'):
934 if not int(videos.get('total')):
935 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title)
936 self.channel['channel_numresults'] = int(videos.get('total'))
937
938 # Collect video meta data that matched the search value
939 for video in xml_data.find("videos").getchildren():
940 hd_flag = False
941 embed_flag = False
942 if video.tag == 'video':
943 if video.get('embed_privacy') == "anywhere":
944 embed_flag = True
945 if video.get('is_hd') == "1":
946 hd_flag = True
947 v_details = {}
948 for details in video.getchildren():
949 if details.tag in ['tags', 'cast']:
950 continue
951 if details.tag == 'width':
952 if details.text:
953 v_details['width'] = details.text.strip()
954 continue
955 if details.tag == 'height':
956 if details.text:
957 v_details['height'] = details.text.strip()
958 continue
959 if details.tag == 'duration':
960 if details.text:
961 v_details['duration'] = details.text.strip()
962 continue
963 if details.tag == 'owner':
964 if details.text:
965 v_details['display_name'] = self.massageDescription(details.get('display_name').strip())
966 else:
967 v_details['display_name'] = ''
968 continue
969 if details.tag == 'description':
970 if details.text:
971 v_details[details.tag] = self.massageDescription(details.text.strip())
972 else:
973 v_details[details.tag] = ''
974 continue
975 if details.tag == 'upload_date':
976 if details.text:
977 pub_time = time.strptime(details.text.strip(), "%Y-%m-%d %H:%M:%S")
978 v_details[details.tag] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', pub_time)
979 else:
980 v_details[details.tag] = ''
981 continue
982 if details.tag == 'urls':
983 for url in details.getchildren():
984 if url.get('type') == 'video':
985 if url.text: # Make the link fullscreen and auto play
986 if embed_flag:
987 v_details[url.tag] = self.processVideoUrl(url.text.strip())
988 else:
989 v_details[url.tag] = self.ampReplace(url.text.strip())
990 else:
991 v_details[url.tag] = ''
992 continue
993 if details.tag == 'thumbnails':
994 largest = 0
995 for image in details.getchildren():
996 if image.tag == 'thumbnail':
997 height = int(image.get('height'))
998 width = int(image.get('width'))
999 default = image.text.find('default')
1000 if largest < height * width and not default != -1:
1001 if image.text:
1002 v_details[image.tag] = self.ampReplace(image.text.strip())
1003 if width >= 200:
1004 break
1005 continue
1006 if details.text:
1007 v_details[details.tag] = self.massageDescription(details.text.strip())
1008 else:
1009 v_details[details.tag] = ''
1010 if hd_flag and 'width' not in v_details:
1011 v_details['width'] = 1280
1012 v_details['height'] = 720
1013 elements_final.append(v_details)
1014
1015 if not len(elements_final):
1016 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % title)
1017
1018 return elements_final
1019 # end searchTitle()
1020
1021
1022 def searchForVideos(self, title, pagenumber):
1023 """Common name for a video search. Used to interface with MythTV plugin NetVision
1024 """
1025 # Channel details and search results
1026 self.channel = {'channel_title': 'Vimeo', 'channel_link': 'http://vimeo.com', 'channel_description': "Vimeo is a respectful community of creative people who are passionate about sharing the videos they make.", 'channel_numresults': 0, 'channel_returned': 1, 'channel_startindex': 0}
1027
1028 # Easier for debugging usually commented out
1029# data = self.searchTitle(title, pagenumber, self.page_limit)
1030# print data
1031# sys.exit()
1032
1033 try:
1034 data = self.searchTitle(title, pagenumber, self.page_limit)
1035 except VimeoVideoNotFound as msg:
1036 sys.stderr.write('%s' % msg)
1037 return None
1038 except VimeoUrlError as msg:
1039 sys.stderr.write(self.error_messages['VimeoUrlError'] % msg)
1040 sys.exit(1)
1041 except VimeoHttpError as msg:
1042 sys.stderr.write(self.error_messages['VimeoHttpError'] % msg)
1043 sys.exit(1)
1044 except VimeoResponseError as msg:
1045 sys.stderr.write(self.error_messages['VimeoResponseError'] % msg)
1046 sys.exit(1)
1047 except VimeoAuthorizeTokenError as msg:
1048 sys.stderr.write(self.error_messages['VimeoAuthorizeTokenError'] % msg)
1049 sys.exit(1)
1050 except VimeoVideosSearchError as msg:
1051 sys.stderr.write(self.error_messages['VimeoVideosSearchError'] % msg)
1052 sys.exit(1)
1053 except VimeoRequestTokenError as msg:
1054 sys.stderr.write(self.error_messages['VimeoRequestTokenError'] % msg)
1055 sys.exit(1)
1056 except VimeoException as msg:
1057 sys.stderr.write(self.error_messages['VimeoException'] % msg)
1058 sys.exit(1)
1059 except Exception as e:
1060 sys.stderr.write("! Error: Unknown error during a Video search (%s)\nError(%s)\n" % (title, e))
1061 sys.exit(1)
1062
1063 if data is None:
1064 return None
1065 if not len(data):
1066 return None
1067
1068 items = []
1069 for match in data:
1070 item_data = {}
1071 for key in list(self.key_translation[1].keys()):
1072 if key == 'url':
1073 item_data['item_link'] = match[key]
1074 item_data['item_url'] = match[key]
1075 continue
1076 if key in list(match.keys()):
1077 item_data[self.key_translation[1][key]] = match[key]
1078 else:
1079 item_data[self.key_translation[1][key]] = ''
1080 items.append(item_data)
1081
1082 self.channel['channel_startindex'] = self.page_limit * int(pagenumber)
1083 self.channel['channel_returned'] = len(items)
1084
1085 if len(items):
1086 return [[self.channel, items]]
1087 return None
1088 # end searchForVideos()
1089
1090
1091 def getChannels(self):
1092 '''Get the channel directory information and fill out the tree view directory structures as required
1093 raise exceptions if the there was an issue getting Channel information or no channel data
1094 return True if successful
1095 '''
1096 self.channel_dict = {'N_R_S': {}, 'Everything HD': {}, }
1098 for key in self.tree_key_list:
1099 self.channel_dict['N_R_S'][key] = []
1100 self.channel_dict['Everything HD'][key] = []
1102 self.channel_count['N_R_S'] = {'newest': [10, 2], 'most_recently_updated': [10, 1], 'most_subscribed': [10, 10]}
1103 self.channel_count['Everything HD'] = {'newest': [10, 2], 'most_recently_updated': [10, 1], 'most_subscribed': [5, 20]}
1104
1105 for sort in self.tree_key_list:
1106 page = 0
1107 not_HD_count = 0
1108 HD_count = 0
1109 not_HD_list = []
1110 HD_list = []
1111 while True:
1112 try:
1113 page+=1
1114 if page > 20: # Throttles excessive searching for HD groups within a category
1115 break
1116 try:
1117 xml_data = self.config['methods']['channels'](sort=sort,
1118 per_page=None,
1119 page=page)
1120 except Exception as msg:
1121 raise VimeoAllChannelError('%s' % msg)
1122
1123 if xml_data is None:
1124 raise VimeoAllChannelError(self.error_messages['1-VimeoAllChannelError'] % sort)
1125
1126 if not len(list(xml_data.keys())):
1127 raise VimeoAllChannelError(self.error_messages['2-VimeoAllChannelError'] % sort)
1128
1129 if xml_data.tag == 'rsp':
1130 if not xml_data.get('stat') == 'ok':
1131 if xml_data.get('stat') in __errmsgs__:
1132 errmsg = __errmsg__[xml_data.get('stat')]
1133 raise VimeoResponseError("Error %s - %s" % (xml_data.get('stat'), errmsg))
1134 else:
1135 errmsg = 'Unknown error'
1136 raise VimeoResponseError("Error %s - %s" % (xml_data.get('stat'), errmsg))
1137
1138 for channel in xml_data.find('channels'):
1139 index = channel.find('name').text.find('HD')
1140 if index != -1:
1141 if HD_count < self.channel_count['Everything HD'][sort][0]:
1142 if not channel.get('id') in HD_list:
1143 self.channel_dict['Everything HD'][sort].append([channel.get('id'), channel.find('name').text])
1144 HD_list.append(channel.get('id'))
1145 HD_count+=1
1146 else:
1147 if not_HD_count < self.channel_count['N_R_S'][sort][0]:
1148 if not channel.get('id') in not_HD_list:
1149 self.channel_dict['N_R_S'][sort].append([channel.get('id'), channel.find('name').text])
1150 not_HD_list.append(channel.get('id'))
1151 not_HD_count+=1
1152 if not_HD_count >= self.channel_count['N_R_S'][sort][0] and HD_count >= self.channel_count['Everything HD']:
1153 break
1154 if not_HD_count >= self.channel_count['N_R_S'][sort][0] and HD_count >= self.channel_count['Everything HD']:
1155 break
1156 except:
1157 break
1158
1159 return True
1160 #end getChannels()
1161
1162
1164 '''Gather the Vimeo Groups/Channels...etc then get a max page of videos meta data in each of them
1165 return array of directories and their video metadata
1166 '''
1167 try:
1168 self.initializeVimeo()
1169 except VimeoAuthorizeTokenError as msg:
1170 sys.stderr.write(self.error_messages['VimeoAuthorizeTokenError'] % msg)
1171 sys.exit(1)
1172 except VimeoRequestTokenError as msg:
1173 sys.stderr.write(self.error_messages['VimeoRequestTokenError'] % msg)
1174 sys.exit(1)
1175 except VimeoException as msg:
1176 sys.stderr.write(self.error_messages['VimeoException'] % msg)
1177 sys.exit(1)
1178 except Exception as msg:
1179 sys.stderr.write("! Error: Unknown error during a Vimeo API initialization (%s)\n" % msg)
1180 sys.exit(1)
1181
1182 # Channel details and search results
1183 self.channel = {'channel_title': 'Vimeo', 'channel_link': 'http://vimeo.com', 'channel_description': "Vimeo is a respectful community of creative people who are passionate about sharing the videos they make.", 'channel_numresults': 0, 'channel_returned': 1, 'channel_startindex': 0}
1184
1185 if self.config['debug_enabled']:
1186 print(self.config['methods'])
1187 print()
1188
1189 # Initialize the Video channels data
1190 try:
1191 self.getChannels()
1192 except VimeoResponseError as msg:
1193 sys.stderr.write(self.error_messages['VimeoResponseError'] % msg)
1194 sys.exit(1)
1195 except VimeoAllChannelError as msg:
1196 sys.stderr.write(self.error_messages['VimeoAllChannelError'] % msg)
1197 sys.exit(1)
1198 except VimeoException as msg:
1199 sys.stderr.write(self.error_messages['VimeoException'] % msg)
1200 sys.exit(1)
1201 except Exception as msg:
1202 sys.stderr.write("! Error: Unknown error while getting all Channels (%s)\n" % msg)
1203 sys.exit(1)
1204
1205 dictionaries = []
1206
1207 self.treeview = True
1208
1209 # Process the various video feeds/categories/... etc
1210 for key in self.tree_order:
1211 self.tree_key = key
1212 dictionaries = self.getVideos(self.tree_org[key], dictionaries)
1213
1214 return [[self.channel, dictionaries]]
1215 # end displayTreeView()
1216
1217
1218 def getVideos(self, dir_dict, dictionaries):
1219 '''Parse a list made of category lists and retrieve video meta data
1220 return a dictionary of directory names and categories video metadata
1221 '''
1222 for sets in dir_dict:
1223 if not isinstance(sets[1], list):
1224 if sets[0] != '': # Add the nested dictionaries display name
1225 try:
1226 dictionaries.append([self.massageDescription(sets[0]), self.setTreeViewIcon(self.feed_icons[self.tree_key][sets[0]])])
1227 except KeyError:
1228 dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
1229 else:
1230 dictionaries.append(['', '']) # Add the nested dictionary indicator
1231 continue
1232 temp_dictionary = []
1233 for self.feed in sets[1]:
1234 if '__all__' in self.config['methods']['tree.view'][self.tree_key]:
1235 URL = self.config['methods']['tree.view'][self.tree_key]['__all__']
1236 else:
1237 URL = self.config['methods']['tree.view'][self.tree_key][self.feed]
1238 temp_dictionary = self.config['item_parser'][URL[1]](self.config['methods'][URL[0]], temp_dictionary)
1239 if len(temp_dictionary):
1240 if len(sets[0]): # Add the nested dictionaries display name
1241 try:
1242 dictionaries.append([self.massageDescription(sets[0]), self.setTreeViewIcon(self.feed_icons[self.tree_key][sets[0]])])
1243 except KeyError:
1244 dictionaries.append([self.massageDescription(sets[0]), self.channel_icon])
1245 for element in temp_dictionary:
1246 dictionaries.append(element)
1247 if len(sets[0]):
1248 dictionaries.append(['', '']) # Add the nested dictionary indicator
1249 return dictionaries
1250 # end getVideos()
1251
1252
1253 def getVideosForChannels(self, method, dictionary):
1254 '''Process all channel related directory videos
1255 return dictionary of new video items
1256 '''
1257 self.next_page = 1
1258 self.max_page = self.channel_count[self.tree_key][self.feed][1]
1259 self.tree_list = False
1260 # Sometimes it is beter to see a list of videos rather than split them up by their channel names
1261 if self.max_page <= self.channel_list_max:
1262 self.tree_list = True
1263 tmp_dictionary = []
1264 for channel in self.channel_dict[self.tree_key][self.feed]:
1265 self.channel_id = channel[0]
1266 self.dir_name = channel[1]
1267
1268 # Easier for debugging
1269 #tmp_dictionary = self.getTreeVideos(method, tmp_dictionary)
1270
1271 try:
1272 tmp_dictionary = self.getTreeVideos(method, tmp_dictionary)
1273 except VimeoVideoNotFound as msg:
1274 sys.stderr.write(self.error_messages['VimeoVideoNotFound'] % msg)
1275 continue
1276 except VimeoVideosSearchError as msg:
1277 sys.stderr.write(self.error_messages['VimeoVideosSearchError'] % msg)
1278 sys.exit(1)
1279 except VimeoResponseError as msg:
1280 sys.stderr.write(self.error_messages['VimeoResponseError'] % msg)
1281 sys.exit(1)
1282 except VimeoException as msg:
1283 sys.stderr.write(self.error_messages['VimeoException'] % msg)
1284 sys.exit(1)
1285 except Exception as e:
1286 sys.stderr.write("! Error: Unknown error during while getting all Channels (%s)\nError(%s)\n" % (self.dir_name, e))
1287 sys.exit(1)
1288
1289 if len(tmp_dictionary):
1290 dictionary.append([self.feed_names[self.tree_key][self.feed], self.setTreeViewIcon()])
1291 for element in tmp_dictionary:
1292 dictionary.append(element)
1293 dictionary.append(['', ''])
1294 return dictionary
1295 # end getVideosForChannels()
1296
1297
1298
1299 def getTreeVideos(self, method, dictionaries):
1300 '''Get the video metadata for url search
1301 return the video dictionary of directories and their video mata data
1302 '''
1303 initial_length = len(dictionaries)
1304 try:
1305 xml_data = method(channel_id=self.channel_id, full_response=1,
1306 per_page=self.max_page,
1307 page=self.next_page)
1308 except Exception as msg:
1309 raise VimeoVideosSearchError('%s' % msg)
1310
1311 if xml_data is None:
1312 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % self.dir_name)
1313
1314 if not len(list(xml_data.keys())):
1315 raise VimeoVideoNotFound(self.error_messages['VimeoVideoNotFound'] % self.dir_name)
1316
1317 if xml_data.tag == 'rsp':
1318 if not xml_data.get('stat') == 'ok':
1319 if xml_data.get('stat') in __errmsgs__:
1320 errmsg = __errmsg__[xml_data.get('stat')]
1321 raise VimeoResponseError("Error %s - %s" % (xml_data.get('stat'), errmsg))
1322 else:
1323 errmsg = 'Unknown error'
1324 raise VimeoResponseError("Error %s - %s" % (xml_data.get('stat'), errmsg))
1325
1326 videos = xml_data.find("videos")
1327 if videos:
1328 if videos.get('total'):
1329 self.channel['channel_numresults'] = int(videos.get('total'))
1330
1331 # Collect video meta data that matched the search value
1332 dictionary_first = False
1333 for video in xml_data.find("videos").getchildren():
1334 hd_flag = False
1335 embed_flag = False
1336 if video.tag == 'video':
1337 if video.get('embed_privacy') == "anywhere":
1338 embed_flag = True
1339 if video.get('is_hd') == "1":
1340 hd_flag = True
1341 v_details = {}
1342 for details in video.getchildren():
1343 if details.tag in ['tags', 'cast']:
1344 continue
1345 if details.tag == 'width':
1346 if details.text:
1347 v_details['width'] = details.text.strip()
1348 continue
1349 if details.tag == 'height':
1350 if details.text:
1351 v_details['height'] = details.text.strip()
1352 continue
1353 if details.tag == 'duration':
1354 if details.text:
1355 v_details['duration'] = details.text.strip()
1356 continue
1357 if details.tag == 'owner':
1358 if details.text:
1359 v_details['display_name'] = self.massageDescription(details.get('display_name').strip())
1360 else:
1361 v_details['display_name'] = ''
1362 continue
1363 if details.tag == 'description':
1364 if details.text:
1365 if self.tree_list:
1366 v_details[details.tag] = self.massageDescription('Channel "%s": %s' % (self.dir_name, details.text.strip()))
1367 else:
1368 v_details[details.tag] = self.massageDescription(details.text.strip())
1369 else:
1370 if self.tree_list:
1371 v_details[details.tag] = self.massageDescription('Channel "%s"' % self.dir_name)
1372 continue
1373 if details.tag == 'upload_date':
1374 if details.text:
1375 pub_time = time.strptime(details.text.strip(), "%Y-%m-%d %H:%M:%S")
1376 v_details[details.tag] = time.strftime('%a, %d %b %Y %H:%M:%S GMT', pub_time)
1377 else:
1378 v_details[details.tag] = ''
1379 continue
1380 if details.tag == 'urls':
1381 for url in details.getchildren():
1382 if url.get('type') == 'video':
1383 if url.text: # Make the link fullscreen and auto play
1384 if embed_flag:
1385 v_details[url.tag] = self.processVideoUrl(url.text.strip())
1386 else:
1387 v_details[url.tag] = self.ampReplace(url.text.strip())
1388 else:
1389 v_details[url.tag] = ''
1390 continue
1391 if details.tag == 'thumbnails':
1392 largest = 0
1393 for image in details.getchildren():
1394 if image.tag == 'thumbnail':
1395 height = int(image.get('height'))
1396 width = int(image.get('width'))
1397 default = image.text.find('default')
1398 if largest < height * width and not default != -1:
1399 if image.text:
1400 v_details[image.tag] = self.ampReplace(image.text.strip())
1401 if width >= 200:
1402 break
1403 continue
1404 if details.text:
1405 v_details[details.tag] = self.massageDescription(details.text.strip())
1406 else:
1407 v_details[details.tag] = ''
1408
1409 if hd_flag and 'width' not in v_details:
1410 v_details['width'] = 1280
1411 v_details['height'] = 720
1412
1413 if 'url' not in v_details:
1414 continue
1415
1416 if not dictionary_first and not self.tree_list: # Add the dictionaries display name
1417 dictionaries.append([self.dir_name, self.setTreeViewIcon()])
1418 dictionary_first = True
1419
1420 final_item = {}
1421 for key in list(self.key_translation[1].keys()):
1422 if key == 'url':
1423 final_item['item_link'] = v_details[key]
1424 final_item['item_url'] = v_details[key]
1425 continue
1426 if key in v_details:
1427 final_item[self.key_translation[1][key]] = v_details[key]
1428 else:
1429 final_item[self.key_translation[1][key]] = ''
1430 dictionaries.append(final_item)
1431
1432 # Need to check if there was any items for this directory
1433 if initial_length < len(dictionaries) and not self.tree_list:
1434 dictionaries.append(['', '']) # Add the nested dictionary indicator
1435 return dictionaries
1436 # end getTreeVideos()
1437# end Videos() class
def do_post_call(self, url, args, use_progress=False)
Definition: vimeo_api.py:183
def _upload_progress(self, download_t, download_d, upload_t, upload_d)
Definition: vimeo_api.py:166
MythTV Netvideo specific classes start here.
Definition: vimeo_api.py:596
def __init__(self, outstream, encoding=None)
Definition: vimeo_api.py:598
def vimeo_test_echo(self, params={})
Contacts section.
Definition: vimeo_api.py:434
def vimeo_albums_getAll(self, user_id, sort=None, per_page=None, page=None)
Album section.
Definition: vimeo_api.py:322
def __init__(self, key, secret, server="vimeo.com", port=PORT, request_token_url=REQUEST_TOKEN_URL, access_token_url=ACCESS_TOKEN_URL, authorization_url=AUTHORIZATION_URL, token=None, token_secret=None)
Definition: vimeo_api.py:217
def _do_vimeo_unauthenticated_call(self, method, parameters={})
Definition: vimeo_api.py:304
def vimeo_channels_getVideos(self, channel_id=None, full_response=None, per_page=None, page=None)
Definition: vimeo_api.py:386
def vimeo_videos_search(self, query, sort=None, per_page=None, page=None)
Video section.
Definition: vimeo_api.py:342
def vimeo_oauth_checkAccessToken(self, auth_token)
Definition: vimeo_api.py:285
def vimeo_videos_upload_getQuota(self)
Videos section.
Definition: vimeo_api.py:480
def _do_vimeo_authenticated_call(self, method, parameters={})
Definition: vimeo_api.py:289
def vimeo_channels_getAll(self, sort=None, per_page=None, page=None)
Channel section.
Definition: vimeo_api.py:367
def setTreeViewIcon(self, dir_icon=None)
Definition: vimeo_api.py:843
def searchForVideos(self, title, pagenumber)
Definition: vimeo_api.py:1022
def getVideos(self, dir_dict, dictionaries)
Definition: vimeo_api.py:1218
def initializeVimeo(self)
End of Utility functions.
Definition: vimeo_api.py:866
def __init__(self, apikey, mythtv=True, interactive=False, select_first=False, debug=False, custom_ui=None, language=None, search_all_languages=False)
Definition: vimeo_api.py:637
def massageDescription(self, text)
Start - Utility functions.
Definition: vimeo_api.py:770
def searchTitle(self, title, pagenumber, pagelen)
Definition: vimeo_api.py:894
def getTreeVideos(self, method, dictionaries)
Definition: vimeo_api.py:1299
def getVideosForChannels(self, method, dictionary)
Definition: vimeo_api.py:1253
def user_groups(user, format="xml")
Definition: vimeo_api.py:566
def user_info(user, format="xml")
Definition: vimeo_api.py:517
def user_videos(user, format="xml")
Definition: vimeo_api.py:524
def user_albums(user, format="xml")
Definition: vimeo_api.py:554
def user_appears_in(user, format="xml")
Definition: vimeo_api.py:536
def user_all_videos(user, format="xml")
Definition: vimeo_api.py:542
def _user_request(user, info, format)
User related call from the "Simple API".
Definition: vimeo_api.py:513
def _simple_request(url, format)
Definition: vimeo_api.py:497
def user_contacts_like(user, format="xml")
Definition: vimeo_api.py:578
def user_contacts_videos(user, format="xml")
Definition: vimeo_api.py:572
def video_request(video, format)
get a specific video
Definition: vimeo_api.py:588
def user_subscriptions(user, format="xml")
Definition: vimeo_api.py:548
def user_likes(user, format="xml")
Definition: vimeo_api.py:530
def user_channels(user, format="xml")
Definition: vimeo_api.py:560
static void print(const QList< uint > &raw_minimas, const QList< uint > &raw_maximas, const QList< float > &minimas, const QList< float > &maximas)