[MyTube] fix for broken API (it seems that the url string contains the signature...
[enigma2-plugins.git] / mytube / src / MyTubeService.py
1 # -*- coding: iso-8859-1 -*-
2 from enigma import ePythonMessagePump
3
4 from __init__ import decrypt_block
5 from ThreadQueue import ThreadQueue
6 import gdata.youtube
7 import gdata.youtube.service
8 from gdata.service import BadAuthentication
9
10 from twisted.web import client
11 from twisted.internet import reactor
12 from urllib2 import Request, URLError, urlopen as urlopen2
13 from socket import gaierror, error
14 import os, socket, httplib
15 from urllib import quote, unquote_plus, unquote, urlencode
16 from httplib import HTTPConnection, CannotSendRequest, BadStatusLine, HTTPException
17
18 from urlparse import parse_qs, parse_qsl
19 from threading import Thread
20
21 HTTPConnection.debuglevel = 1
22
23 if 'HTTPSConnection' not in dir(httplib):
24         # python on enimga2 has no https socket support
25         gdata.youtube.service.YOUTUBE_USER_FEED_URI = 'http://gdata.youtube.com/feeds/api/users'
26
27 def validate_cert(cert, key):
28         buf = decrypt_block(cert[8:], key)
29         if buf is None:
30                 return None
31         return buf[36:107] + cert[139:196]
32
33 def get_rnd():
34         try:
35                 rnd = os.urandom(8)
36                 return rnd
37         except:
38                 return None
39
40 std_headers = {
41         'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100627 Firefox/3.6.6',
42         'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
43         'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
44         'Accept-Language': 'en-us,en;q=0.5',
45 }
46
47 #config.plugins.mytube = ConfigSubsection()
48 #config.plugins.mytube.general = ConfigSubsection()
49 #config.plugins.mytube.general.useHTTPProxy = ConfigYesNo(default = False)
50 #config.plugins.mytube.general.ProxyIP = ConfigIP(default=[0,0,0,0])
51 #config.plugins.mytube.general.ProxyPort = ConfigNumber(default=8080)
52 #class MyOpener(FancyURLopener):
53 #       version = 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12'
54
55
56 class GoogleSuggestions():
57         def __init__(self):
58                 self.hl = "en"
59                 self.conn = None
60
61         def prepareQuery(self):
62                 #GET /complete/search?output=toolbar&client=youtube-psuggest&xml=true&ds=yt&hl=en&jsonp=self.gotSuggestions&q=s
63                 #self.prepQuerry = "/complete/search?output=toolbar&client=youtube&xml=true&ds=yt&"
64                 self.prepQuerry = "/complete/search?output=chrome&client=chrome&"
65                 if self.hl is not None:
66                         self.prepQuerry = self.prepQuerry + "hl=" + self.hl + "&"
67                 self.prepQuerry = self.prepQuerry + "jsonp=self.gotSuggestions&q="
68                 print "[MyTube - GoogleSuggestions] prepareQuery:",self.prepQuerry
69
70         def getSuggestions(self, queryString):
71                 self.prepareQuery()
72                 if queryString is not "":
73                         query = self.prepQuerry + quote(queryString)
74                         self.conn = HTTPConnection("google.com")
75                         try:
76                                 self.conn = HTTPConnection("google.com")
77                                 self.conn.request("GET", query, "", {"Accept-Encoding": "UTF-8"})
78                         except (CannotSendRequest, gaierror, error):
79                                 self.conn.close()
80                                 print "[MyTube - GoogleSuggestions] Can not send request for suggestions"
81                                 return None
82                         else:
83                                 try:
84                                         response = self.conn.getresponse()
85                                 except BadStatusLine:
86                                         self.conn.close()
87                                         print "[MyTube - GoogleSuggestions] Can not get a response from google"
88                                         return None
89                                 else:
90                                         if response.status == 200:
91                                                 data = response.read()
92                                                 header = response.getheader("Content-Type", "text/xml; charset=ISO-8859-1")
93                                                 charset = "ISO-8859-1"
94                                                 try:
95                                                         charset = header.split(";")[1].split("=")[1]
96                                                         print "[MyTube - GoogleSuggestions] Got charset %s" %charset
97                                                 except:
98                                                         print "[MyTube - GoogleSuggestions] No charset in Header, falling back to %s" %charset
99                                                 data = data.decode(charset).encode("utf-8")
100                                                 self.conn.close()
101                                                 return data
102                                         else:
103                                                 self.conn.close()
104                                                 return None
105                 else:
106                         return None
107
108 class MyTubeFeedEntry():
109         def __init__(self, feed, entry, favoritesFeed = False):
110                 self.feed = feed
111                 self.entry = entry
112                 self.favoritesFeed = favoritesFeed
113                 self.thumbnail = {}
114                 """self.myopener = MyOpener()
115                 urllib.urlopen = MyOpener().open
116                 if config.plugins.mytube.general.useHTTPProxy.value is True:
117                         proxy = {'http': 'http://'+str(config.plugins.mytube.general.ProxyIP.getText())+':'+str(config.plugins.mytube.general.ProxyPort.value)}
118                         self.myopener = MyOpener(proxies=proxy)
119                         urllib.urlopen = MyOpener(proxies=proxy).open
120                 else:
121                         self.myopener = MyOpener()
122                         urllib.urlopen = MyOpener().open"""
123
124         def isPlaylistEntry(self):
125                 return False
126
127         def getTubeId(self):
128                 #print "[MyTubeFeedEntry] getTubeId"
129                 ret = None
130                 if self.entry.media.player:
131                         split = self.entry.media.player.url.split("=")
132                         ret = split.pop()
133                         if ret.startswith('youtube_gdata'):
134                                 tmpval=split.pop()
135                                 if tmpval.endswith("&feature"):
136                                         tmp = tmpval.split("&")
137                                         ret = tmp.pop(0)
138                 return ret
139
140         def getTitle(self):
141                 #print "[MyTubeFeedEntry] getTitle",self.entry.media.title.text
142                 return self.entry.media.title.text
143
144         def getDescription(self):
145                 #print "[MyTubeFeedEntry] getDescription"
146                 if self.entry.media is not None and self.entry.media.description is not None:
147                         return self.entry.media.description.text
148                 return "not vailable"
149
150         def getThumbnailUrl(self, index = 0):
151                 #print "[MyTubeFeedEntry] getThumbnailUrl"
152                 if index < len(self.entry.media.thumbnail):
153                         return self.entry.media.thumbnail[index].url
154                 return None
155
156         def getPublishedDate(self):
157                 if self.entry.published is not None:
158                         return self.entry.published.text
159                 return "unknown"
160
161         def getViews(self):
162                 if self.entry.statistics is not None:
163                         return self.entry.statistics.view_count
164                 return "not available"
165
166         def getDuration(self):
167                 if self.entry.media is not None and self.entry.media.duration is not None:
168                         return self.entry.media.duration.seconds
169                 else:
170                         return 0
171
172         def getRatingAverage(self):
173                 if self.entry.rating is not None:
174                         return self.entry.rating.average
175                 return 0
176
177
178         def getNumRaters(self):
179                 if self.entry.rating is not None:
180                         return self.entry.rating.num_raters
181                 return ""
182
183         def getAuthor(self):
184                 authors = []
185                 for author in self.entry.author:
186                         authors.append(author.name.text)
187                 author = ", ".join(authors)
188                 return author
189
190         def getUserFeedsUrl(self):
191                 for author in self.entry.author:
192                         return author.uri.text
193
194                 return False
195
196         def getUserId(self):
197                 return self.getUserFeedsUrl().split('/')[-1]
198
199         def subscribeToUser(self):
200                 username = self.getUserId()
201                 return myTubeService.SubscribeToUser(username)
202                 
203         def addToFavorites(self):
204                 video_id = self.getTubeId()
205                 return myTubeService.addToFavorites(video_id)
206
207         def PrintEntryDetails(self):
208                 EntryDetails = { 'Title': None, 'TubeID': None, 'Published': None, 'Published': None, 'Description': None, 'Category': None, 'Tags': None, 'Duration': None, 'Views': None, 'Rating': None, 'Thumbnails': None}
209                 EntryDetails['Title'] = self.entry.media.title.text
210                 EntryDetails['TubeID'] = self.getTubeId()
211                 EntryDetails['Description'] = self.getDescription()
212                 EntryDetails['Category'] = self.entry.media.category[0].text
213                 EntryDetails['Tags'] = self.entry.media.keywords.text
214                 EntryDetails['Published'] = self.getPublishedDate()
215                 EntryDetails['Views'] = self.getViews()
216                 EntryDetails['Duration'] = self.getDuration()
217                 EntryDetails['Rating'] = self.getNumRaters()
218                 EntryDetails['RatingAverage'] = self.getRatingAverage()
219                 EntryDetails['Author'] = self.getAuthor()
220                 # show thumbnails
221                 list = []
222                 for thumbnail in self.entry.media.thumbnail:
223                         print 'Thumbnail url: %s' % thumbnail.url
224                         list.append(str(thumbnail.url))
225                 EntryDetails['Thumbnails'] = list
226                 #print EntryDetails
227                 return EntryDetails
228
229         def getVideoUrl(self):
230                 VIDEO_FMT_PRIORITY_MAP = {
231                         '38' : 1, #MP4 Original (HD)
232                         '37' : 2, #MP4 1080p (HD)
233                         '22' : 3, #MP4 720p (HD)
234                         '18' : 4, #MP4 360p
235                         '35' : 5, #FLV 480p
236                         '34' : 6, #FLV 360p
237                 }
238                 video_url = None
239                 video_id = str(self.getTubeId())
240
241                 # Getting video webpage
242                 #URLs for YouTube video pages will change from the format http://www.youtube.com/watch?v=ylLzyHk54Z0 to http://www.youtube.com/watch#!v=ylLzyHk54Z0.
243                 watch_url = 'http://www.youtube.com/watch?v=%s&gl=US&hl=en' % video_id
244                 watchrequest = Request(watch_url, None, std_headers)
245                 try:
246                         print "[MyTube] trying to find out if a HD Stream is available",watch_url
247                         watchvideopage = urlopen2(watchrequest).read()
248                 except (URLError, HTTPException, socket.error), err:
249                         print "[MyTube] Error: Unable to retrieve watchpage - Error code: ", str(err)
250                         return video_url
251
252                 # Get video info
253                 for el in ['&el=embedded', '&el=detailpage', '&el=vevo', '']:
254                         info_url = ('http://www.youtube.com/get_video_info?&video_id=%s%s&ps=default&eurl=&gl=US&hl=en' % (video_id, el))
255                         request = Request(info_url, None, std_headers)
256                         try:
257                                 infopage = urlopen2(request).read()
258                                 videoinfo = parse_qs(infopage)
259                                 if ('url_encoded_fmt_stream_map' or 'fmt_url_map') in videoinfo:
260                                         break
261                         except (URLError, HTTPException, socket.error), err:
262                                 print "[MyTube] Error: unable to download video infopage",str(err)
263                                 return video_url
264
265                 if ('url_encoded_fmt_stream_map' or 'fmt_url_map') not in videoinfo:
266                         # Attempt to see if YouTube has issued an error message
267                         if 'reason' not in videoinfo:
268                                 print '[MyTube] Error: unable to extract "fmt_url_map" or "url_encoded_fmt_stream_map" parameter for unknown reason'
269                         else:
270                                 reason = unquote_plus(videoinfo['reason'][0])
271                                 print '[MyTube] Error: YouTube said: %s' % reason.decode('utf-8')
272                         return video_url
273
274                 video_fmt_map = {}
275                 fmt_infomap = {}
276                 if videoinfo.has_key('url_encoded_fmt_stream_map'):
277                         tmp_fmtUrlDATA = videoinfo['url_encoded_fmt_stream_map'][0].split(',')
278                 else:
279                         tmp_fmtUrlDATA = videoinfo['fmt_url_map'][0].split(',')
280                 for fmtstring in tmp_fmtUrlDATA:
281                         fmturl = fmtid = ""
282                         if videoinfo.has_key('url_encoded_fmt_stream_map'):
283                                 try:
284                                         for arg in fmtstring.split('&'):
285                                                 if arg.find('=') >= 0:
286                                                         key, value = arg.split('=')
287                                                         if key == 'itag':
288                                                                 if len(value) > 3:
289                                                                         value = value[:2]
290                                                                 fmtid = value
291                                                         elif key == 'url':
292                                                                 fmturl = value
293
294                                         if fmtid != "" and fmturl != "" and VIDEO_FMT_PRIORITY_MAP.has_key(fmtid):
295                                                 video_fmt_map[VIDEO_FMT_PRIORITY_MAP[fmtid]] = { 'fmtid': fmtid, 'fmturl': unquote_plus(fmturl)}
296                                                 fmt_infomap[int(fmtid)] = "%s" %(unquote_plus(fmturl))
297                                         fmturl = fmtid = ""
298
299                                 except:
300                                         print "error parsing fmtstring:",fmtstring
301
302                         else:
303                                 (fmtid,fmturl) = fmtstring.split('|')
304                         if VIDEO_FMT_PRIORITY_MAP.has_key(fmtid) and fmtid != "":
305                                 video_fmt_map[VIDEO_FMT_PRIORITY_MAP[fmtid]] = { 'fmtid': fmtid, 'fmturl': unquote_plus(fmturl) }
306                                 fmt_infomap[int(fmtid)] = unquote_plus(fmturl)
307                 print "[MyTube] got",sorted(fmt_infomap.iterkeys())
308                 if video_fmt_map and len(video_fmt_map):
309                         print "[MyTube] found best available video format:",video_fmt_map[sorted(video_fmt_map.iterkeys())[0]]['fmtid']
310                         best_video = video_fmt_map[sorted(video_fmt_map.iterkeys())[0]]
311                         video_url = "%s" %(best_video['fmturl'].split(';')[0])
312                         print "[MyTube] found best available video url:",video_url
313
314                 return video_url
315
316         def getRelatedVideos(self):
317                 print "[MyTubeFeedEntry] getRelatedVideos()"
318                 for link in self.entry.link:
319                         #print "Related link: ", link.rel.endswith
320                         if link.rel.endswith("video.related"):
321                                 print "Found Related: ", link.href
322                                 return link.href
323
324         def getResponseVideos(self):
325                 print "[MyTubeFeedEntry] getResponseVideos()"
326                 for link in self.entry.link:
327                         #print "Responses link: ", link.rel.endswith
328                         if link.rel.endswith("video.responses"):
329                                 print "Found Responses: ", link.href
330                                 return link.href
331
332         def getUserVideos(self):
333                 print "[MyTubeFeedEntry] getUserVideos()"
334                 username = self.getUserId()
335                 myuri = 'http://gdata.youtube.com/feeds/api/users/%s/uploads' % username
336                 print "Found Uservideos: ", myuri
337                 return myuri
338
339 class MyTubePlayerService():
340 #       Do not change the client_id and developer_key in the login-section!
341 #       ClientId: ytapi-dream-MyTubePlayer-i0kqrebg-0
342 #       DeveloperKey: AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw
343
344         cached_auth_request = {}
345         current_auth_token = None
346         yt_service = None
347
348         def __init__(self):
349                 print "[MyTube] MyTubePlayerService - init"
350                 self.feedentries = []
351                 self.feed = None
352
353         def startService(self):
354                 print "[MyTube] MyTubePlayerService - startService"
355
356                 self.yt_service = gdata.youtube.service.YouTubeService()
357
358                 # missing ssl support? youtube will help us on some feed urls
359                 self.yt_service.ssl = self.supportsSSL()
360
361                 # dont use it on class init; error on post and auth
362                 self.yt_service.developer_key = 'AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw'
363                 self.yt_service.client_id = 'ytapi-dream-MyTubePlayer-i0kqrebg-0'
364
365                 # yt_service is reinit on every feed build; cache here to not reauth. remove init every time?
366                 if self.current_auth_token is not None:
367                         print "[MyTube] MyTubePlayerService - auth_cached"
368                         self.yt_service.SetClientLoginToken(self.current_auth_token)
369                 
370 #               self.loggedIn = False
371                 #os.environ['http_proxy'] = 'http://169.229.50.12:3128'
372                 #proxy = os.environ.get('http_proxy')
373                 #print "FOUND ENV PROXY-->",proxy
374                 #for a in os.environ.keys():
375                 #       print a
376
377         def stopService(self):
378                 print "[MyTube] MyTubePlayerService - stopService"
379                 del self.ytService
380
381         def getLoginTokenOnCurl(self, email, pw):
382
383                 opts = {
384                   'service':'youtube',
385                   'accountType': 'HOSTED_OR_GOOGLE',
386                   'Email': email,
387                   'Passwd': pw,
388                   'source': self.yt_service.client_id,
389                 }
390                 
391                 print "[MyTube] MyTubePlayerService - Starting external curl auth request"
392                 result = os.popen('curl -s -k -X POST "%s" -d "%s"' % (gdata.youtube.service.YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL , urlencode(opts))).read()
393                 
394                 return result
395
396         def supportsSSL(self):
397                 return 'HTTPSConnection' in dir(httplib)
398
399         def getFormattedTokenRequest(self, email, pw):
400                 return dict(parse_qsl(self.getLoginTokenOnCurl(email, pw).strip().replace('\n', '&')))
401         
402         def getAuthedUsername(self):
403                 # on external curl we can get real username
404                 if self.cached_auth_request.get('YouTubeUser') is not None:
405                         return self.cached_auth_request.get('YouTubeUser')
406
407                 if self.is_auth() is False:
408                         return ''
409
410                 # current gdata auth class save doesnt save realuser
411                 return 'Logged In'
412
413         def auth_user(self, username, password):
414                 print "[MyTube] MyTubePlayerService - auth_use - " + str(username)
415
416                 if self.yt_service is None:
417                         self.startService()
418                 
419                 if self.current_auth_token is not None:
420                         print "[MyTube] MyTubePlayerService - auth_cached"
421                         self.yt_service.SetClientLoginToken(self.current_auth_token)
422                         return
423
424                 if self.supportsSSL() is False:
425                         print "[MyTube] MyTubePlayerService - HTTPSConnection not found trying external curl"
426                         self.cached_auth_request = self.getFormattedTokenRequest(username, password)
427                         if self.cached_auth_request.get('Auth') is None:
428                                 raise Exception('Got no auth token from curl; you need curl and valid youtube login data')
429                         
430                         self.yt_service.SetClientLoginToken(self.cached_auth_request.get('Auth'))
431                 else:
432                         print "[MyTube] MyTubePlayerService - Using regularly ProgrammaticLogin for login"
433                         self.yt_service.email = username
434                         self.yt_service.password  = password
435                         self.yt_service.ProgrammaticLogin()
436                         
437                 # double check login: reset any token on wrong logins
438                 if self.is_auth() is False:
439                         print "[MyTube] MyTubePlayerService - auth_use - auth not possible resetting"
440                         self.resetAuthState()
441                         return
442
443                 print "[MyTube] MyTubePlayerService - Got successful login"
444                 self.current_auth_token = self.auth_token()
445
446         def resetAuthState(self):
447                 print "[MyTube] MyTubePlayerService - resetting auth"
448                 self.cached_auth_request = {}
449                 self.current_auth_token = None
450
451                 if self.yt_service is None:
452                         return
453
454                 self.yt_service.current_token = None
455                 self.yt_service.token_store.remove_all_tokens()
456
457         def is_auth(self):
458                 if self.current_auth_token is not None:
459                         return True             
460                 
461                 if self.yt_service.current_token is None:
462                         return False
463                 
464                 return self.yt_service.current_token.get_token_string() != 'None'
465
466         def auth_token(self):
467                 return self.yt_service.current_token.get_token_string()
468
469         def getFeedService(self, feedname):
470                 if feedname == "top_rated":
471                         return self.yt_service.GetTopRatedVideoFeed
472                 elif feedname == "most_viewed":
473                         return self.yt_service.GetMostViewedVideoFeed
474                 elif feedname == "recently_featured":
475                         return self.yt_service.GetRecentlyFeaturedVideoFeed
476                 elif feedname == "top_favorites":
477                         return self.yt_service.GetTopFavoritesVideoFeed
478                 elif feedname == "most_recent":
479                         return self.yt_service.GetMostRecentVideoFeed
480                 elif feedname == "most_discussed":
481                         return self.yt_service.GetMostDiscussedVideoFeed
482                 elif feedname == "most_linked":
483                         return self.yt_service.GetMostLinkedVideoFeed
484                 elif feedname == "most_responded":
485                         return self.yt_service.GetMostRespondedVideoFeed
486                 return self.yt_service.GetYouTubeVideoFeed
487
488         def getFeed(self, url, feedname = "", callback = None, errorback = None):
489                 print "[MyTube] MyTubePlayerService - getFeed:",url, feedname
490                 self.feedentries = []
491                 ytservice = self.yt_service.GetYouTubeVideoFeed
492                 
493                 if feedname == "my_subscriptions":
494                         url = "http://gdata.youtube.com/feeds/api/users/default/newsubscriptionvideos"
495                 elif feedname == "my_favorites":
496                         url = "http://gdata.youtube.com/feeds/api/users/default/favorites"
497                 elif feedname == "my_history":
498                         url = "http://gdata.youtube.com/feeds/api/users/default/watch_history?v=2"
499                 elif feedname == "my_recommendations":
500                         url = "http://gdata.youtube.com/feeds/api/users/default/recommendations?v=2"
501                 elif feedname == "my_watch_later":
502                         url = "http://gdata.youtube.com/feeds/api/users/default/watch_later?v=2"
503                 elif feedname == "my_uploads":
504                         url = "http://gdata.youtube.com/feeds/api/users/default/uploads"
505                 elif feedname in ("hd", "most_popular", "most_shared", "on_the_web"):
506                         if feedname == "hd":
507                                 url = "http://gdata.youtube.com/feeds/api/videos/-/HD"
508                         else:
509                                 url = url + feedname
510                 elif feedname in ("top_rated","most_viewed","recently_featured","top_favorites","most_recent","most_discussed","most_linked","most_responded"):
511                         url = None
512                         ytservice = self.getFeedService(feedname)
513
514                 queryThread = YoutubeQueryThread(ytservice, url, self.gotFeed, self.gotFeedError, callback, errorback)
515                 queryThread.start()
516                 return queryThread
517
518         def search(self, searchTerms, startIndex = 1, maxResults = 25,
519                                         orderby = "relevance", time = 'all_time', racy = "include",
520                                         author = "", lr = "", categories = "", sortOrder = "ascending",
521                                         callback = None, errorback = None):
522                 print "[MyTube] MyTubePlayerService - search()"
523                 self.feedentries = []
524                 query = gdata.youtube.service.YouTubeVideoQuery()
525                 query.vq = searchTerms
526                 query.orderby = orderby
527                 query.time = time
528                 query.racy = racy
529                 query.sortorder = sortOrder
530                 if lr is not None:
531                         query.lr = lr
532                 if categories[0] is not None:
533                         query.categories = categories
534                 query.start_index = startIndex
535                 query.max_results = maxResults
536                 queryThread = YoutubeQueryThread(self.yt_service.YouTubeQuery, query, self.gotFeed, self.gotFeedError, callback, errorback)
537                 queryThread.start()
538                 return queryThread
539
540         def gotFeed(self, feed, callback):
541                 if feed is not None:
542                         self.feed = feed
543                         for entry in self.feed.entry:
544                                 MyFeedEntry = MyTubeFeedEntry(self, entry)
545                                 self.feedentries.append(MyFeedEntry)
546                 if callback is not None:
547                         callback(self.feed)
548
549         def gotFeedError(self, exception, errorback):
550                 if errorback is not None:
551                         errorback(exception)
552
553         def SubscribeToUser(self, username):
554                 try:
555                         new_subscription = self.yt_service.AddSubscriptionToChannel(username_to_subscribe_to=username)
556         
557                         if isinstance(new_subscription, gdata.youtube.YouTubeSubscriptionEntry):
558                                 print '[MyTube] MyTubePlayerService: New subscription added'
559                                 return _('New subscription added')
560                         
561                         return _('Unknown error')
562                 except gdata.service.RequestError as req:
563                         return str('Error: ' + str(req[0]["body"]))
564                 except Exception as e:
565                         return str('Error: ' + e)
566         
567         def addToFavorites(self, video_id):
568                 try:
569                         video_entry = self.yt_service.GetYouTubeVideoEntry(video_id=video_id)
570                         response = self.yt_service.AddVideoEntryToFavorites(video_entry)
571                         
572                         # The response, if succesfully posted is a YouTubeVideoEntry
573                         if isinstance(response, gdata.youtube.YouTubeVideoEntry):
574                                 print '[MyTube] MyTubePlayerService: Video successfully added to favorites'
575                                 return _('Video successfully added to favorites')       
576         
577                         return _('Unknown error')
578                 except gdata.service.RequestError as req:
579                         return str('Error: ' + str(req[0]["body"]))
580                 except Exception as e:
581                         return str('Error: ' + e)
582         
583         def getTitle(self):
584                 return self.feed.title.text
585
586         def getEntries(self):
587                 return self.feedentries
588
589         def itemCount(self):
590                 return self.feed.items_per_page.text
591
592         def getTotalResults(self):
593                 if self.feed.total_results is None:
594                         return 0
595                                 
596                 return self.feed.total_results.text
597
598         def getNextFeedEntriesURL(self):
599                 for link in self.feed.link:
600                         if link.rel == "next":
601                                 return link.href
602                 return None
603
604         def getCurrentPage(self):
605                 if self.feed.start_index is None:
606                         return 1
607                 
608                 return int(int(self.feed.start_index.text) / int(self.itemCount())) + 1
609
610 class YoutubeQueryThread(Thread):
611         def __init__(self, query, param, gotFeed, gotFeedError, callback, errorback):
612                 Thread.__init__(self)
613                 self.messagePump = ePythonMessagePump()
614                 self.messages = ThreadQueue()
615                 self.gotFeed = gotFeed
616                 self.gotFeedError = gotFeedError
617                 self.callback = callback
618                 self.errorback = errorback
619                 self.query = query
620                 self.param = param
621                 self.canceled = False
622                 self.messagePump.recv_msg.get().append(self.finished)
623
624         def cancel(self):
625                 self.canceled = True
626
627         def run(self):
628                 try:
629                         if self.param is None:
630                                 feed = self.query()
631                         else:
632                                 feed = self.query(self.param)
633                         self.messages.push((True, feed, self.callback))
634                         self.messagePump.send(0)
635                 except Exception, ex:
636                         self.messages.push((False, ex, self.errorback))
637                         self.messagePump.send(0)
638
639         def finished(self, val):
640                 if not self.canceled:
641                         message = self.messages.pop()
642                         if message[0]:
643                                 self.gotFeed(message[1], message[2])
644                         else:
645                                 self.gotFeedError(message[1], message[2])
646
647 myTubeService = MyTubePlayerService()
648