Merge branch 'master' into master_internal
[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 = fmtsig = ""
282                         if videoinfo.has_key('url_encoded_fmt_stream_map'):
283                                 try:
284                                         for arg in fmtstring.split('&'):
285                                                 if arg.find('=') >= 0:
286                                                         print arg.split('=')
287                                                         key, value = arg.split('=')
288                                                         if key == 'itag':
289                                                                 if len(value) > 3:
290                                                                         value = value[:2]
291                                                                 fmtid = value
292                                                         elif key == 'url':
293                                                                 fmturl = value
294                                                         elif key == 'sig':
295                                                                 fmtsig = value
296                                                                 
297                                         if fmtid != "" and fmturl != "" and fmtsig != ""  and VIDEO_FMT_PRIORITY_MAP.has_key(fmtid):
298                                                 video_fmt_map[VIDEO_FMT_PRIORITY_MAP[fmtid]] = { 'fmtid': fmtid, 'fmturl': unquote_plus(fmturl), 'fmtsig': fmtsig }
299                                                 fmt_infomap[int(fmtid)] = "%s&signature=%s" %(unquote_plus(fmturl), fmtsig)
300                                         fmturl = fmtid = fmtsig = ""
301
302                                 except:
303                                         print "error parsing fmtstring:",fmtstring
304                                         
305                         else:
306                                 (fmtid,fmturl) = fmtstring.split('|')
307                         if VIDEO_FMT_PRIORITY_MAP.has_key(fmtid) and fmtid != "":
308                                 video_fmt_map[VIDEO_FMT_PRIORITY_MAP[fmtid]] = { 'fmtid': fmtid, 'fmturl': unquote_plus(fmturl) }
309                                 fmt_infomap[int(fmtid)] = unquote_plus(fmturl)
310                 print "[MyTube] got",sorted(fmt_infomap.iterkeys())
311                 if video_fmt_map and len(video_fmt_map):
312                         print "[MyTube] found best available video format:",video_fmt_map[sorted(video_fmt_map.iterkeys())[0]]['fmtid']
313                         best_video = video_fmt_map[sorted(video_fmt_map.iterkeys())[0]]
314                         video_url = "%s&signature=%s" %(best_video['fmturl'].split(';')[0], best_video['fmtsig'])
315                         print "[MyTube] found best available video url:",video_url
316
317                 return video_url
318
319         def getRelatedVideos(self):
320                 print "[MyTubeFeedEntry] getRelatedVideos()"
321                 for link in self.entry.link:
322                         #print "Related link: ", link.rel.endswith
323                         if link.rel.endswith("video.related"):
324                                 print "Found Related: ", link.href
325                                 return link.href
326
327         def getResponseVideos(self):
328                 print "[MyTubeFeedEntry] getResponseVideos()"
329                 for link in self.entry.link:
330                         #print "Responses link: ", link.rel.endswith
331                         if link.rel.endswith("video.responses"):
332                                 print "Found Responses: ", link.href
333                                 return link.href
334
335         def getUserVideos(self):
336                 print "[MyTubeFeedEntry] getUserVideos()"
337                 username = self.getUserId()
338                 myuri = 'http://gdata.youtube.com/feeds/api/users/%s/uploads' % username
339                 print "Found Uservideos: ", myuri
340                 return myuri
341
342 class MyTubePlayerService():
343 #       Do not change the client_id and developer_key in the login-section!
344 #       ClientId: ytapi-dream-MyTubePlayer-i0kqrebg-0
345 #       DeveloperKey: AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw
346
347         cached_auth_request = {}
348         current_auth_token = None
349         yt_service = None
350
351         def __init__(self):
352                 print "[MyTube] MyTubePlayerService - init"
353                 self.feedentries = []
354                 self.feed = None
355
356         def startService(self):
357                 print "[MyTube] MyTubePlayerService - startService"
358
359                 self.yt_service = gdata.youtube.service.YouTubeService()
360
361                 # missing ssl support? youtube will help us on some feed urls
362                 self.yt_service.ssl = self.supportsSSL()
363
364                 # dont use it on class init; error on post and auth
365                 self.yt_service.developer_key = 'AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw'
366                 self.yt_service.client_id = 'ytapi-dream-MyTubePlayer-i0kqrebg-0'
367
368                 # yt_service is reinit on every feed build; cache here to not reauth. remove init every time?
369                 if self.current_auth_token is not None:
370                         print "[MyTube] MyTubePlayerService - auth_cached"
371                         self.yt_service.SetClientLoginToken(self.current_auth_token)
372                 
373 #               self.loggedIn = False
374                 #os.environ['http_proxy'] = 'http://169.229.50.12:3128'
375                 #proxy = os.environ.get('http_proxy')
376                 #print "FOUND ENV PROXY-->",proxy
377                 #for a in os.environ.keys():
378                 #       print a
379
380         def stopService(self):
381                 print "[MyTube] MyTubePlayerService - stopService"
382                 del self.ytService
383
384         def getLoginTokenOnCurl(self, email, pw):
385
386                 opts = {
387                   'service':'youtube',
388                   'accountType': 'HOSTED_OR_GOOGLE',
389                   'Email': email,
390                   'Passwd': pw,
391                   'source': self.yt_service.client_id,
392                 }
393                 
394                 print "[MyTube] MyTubePlayerService - Starting external curl auth request"
395                 result = os.popen('curl -s -k -X POST "%s" -d "%s"' % (gdata.youtube.service.YOUTUBE_CLIENTLOGIN_AUTHENTICATION_URL , urlencode(opts))).read()
396                 
397                 return result
398
399         def supportsSSL(self):
400                 return 'HTTPSConnection' in dir(httplib)
401
402         def getFormattedTokenRequest(self, email, pw):
403                 return dict(parse_qsl(self.getLoginTokenOnCurl(email, pw).strip().replace('\n', '&')))
404         
405         def getAuthedUsername(self):
406                 # on external curl we can get real username
407                 if self.cached_auth_request.get('YouTubeUser') is not None:
408                         return self.cached_auth_request.get('YouTubeUser')
409
410                 if self.is_auth() is False:
411                         return ''
412
413                 # current gdata auth class save doesnt save realuser
414                 return 'Logged In'
415
416         def auth_user(self, username, password):
417                 print "[MyTube] MyTubePlayerService - auth_use - " + str(username)
418
419                 if self.yt_service is None:
420                         self.startService()
421                 
422                 if self.current_auth_token is not None:
423                         print "[MyTube] MyTubePlayerService - auth_cached"
424                         self.yt_service.SetClientLoginToken(self.current_auth_token)
425                         return
426
427                 if self.supportsSSL() is False:
428                         print "[MyTube] MyTubePlayerService - HTTPSConnection not found trying external curl"
429                         self.cached_auth_request = self.getFormattedTokenRequest(username, password)
430                         if self.cached_auth_request.get('Auth') is None:
431                                 raise Exception('Got no auth token from curl; you need curl and valid youtube login data')
432                         
433                         self.yt_service.SetClientLoginToken(self.cached_auth_request.get('Auth'))
434                 else:
435                         print "[MyTube] MyTubePlayerService - Using regularly ProgrammaticLogin for login"
436                         self.yt_service.email = username
437                         self.yt_service.password  = password
438                         self.yt_service.ProgrammaticLogin()
439                         
440                 # double check login: reset any token on wrong logins
441                 if self.is_auth() is False:
442                         print "[MyTube] MyTubePlayerService - auth_use - auth not possible resetting"
443                         self.resetAuthState()
444                         return
445
446                 print "[MyTube] MyTubePlayerService - Got successful login"
447                 self.current_auth_token = self.auth_token()
448
449         def resetAuthState(self):
450                 print "[MyTube] MyTubePlayerService - resetting auth"
451                 self.cached_auth_request = {}
452                 self.current_auth_token = None
453
454                 if self.yt_service is None:
455                         return
456
457                 self.yt_service.current_token = None
458                 self.yt_service.token_store.remove_all_tokens()
459
460         def is_auth(self):
461                 if self.current_auth_token is not None:
462                         return True             
463                 
464                 if self.yt_service.current_token is None:
465                         return False
466                 
467                 return self.yt_service.current_token.get_token_string() != 'None'
468
469         def auth_token(self):
470                 return self.yt_service.current_token.get_token_string()
471
472         def getFeedService(self, feedname):
473                 if feedname == "top_rated":
474                         return self.yt_service.GetTopRatedVideoFeed
475                 elif feedname == "most_viewed":
476                         return self.yt_service.GetMostViewedVideoFeed
477                 elif feedname == "recently_featured":
478                         return self.yt_service.GetRecentlyFeaturedVideoFeed
479                 elif feedname == "top_favorites":
480                         return self.yt_service.GetTopFavoritesVideoFeed
481                 elif feedname == "most_recent":
482                         return self.yt_service.GetMostRecentVideoFeed
483                 elif feedname == "most_discussed":
484                         return self.yt_service.GetMostDiscussedVideoFeed
485                 elif feedname == "most_linked":
486                         return self.yt_service.GetMostLinkedVideoFeed
487                 elif feedname == "most_responded":
488                         return self.yt_service.GetMostRespondedVideoFeed
489                 return self.yt_service.GetYouTubeVideoFeed
490
491         def getFeed(self, url, feedname = "", callback = None, errorback = None):
492                 print "[MyTube] MyTubePlayerService - getFeed:",url, feedname
493                 self.feedentries = []
494                 ytservice = self.yt_service.GetYouTubeVideoFeed
495                 
496                 if feedname == "my_subscriptions":
497                         url = "http://gdata.youtube.com/feeds/api/users/default/newsubscriptionvideos"
498                 elif feedname == "my_favorites":
499                         url = "http://gdata.youtube.com/feeds/api/users/default/favorites"
500                 elif feedname == "my_history":
501                         url = "http://gdata.youtube.com/feeds/api/users/default/watch_history?v=2"
502                 elif feedname == "my_recommendations":
503                         url = "http://gdata.youtube.com/feeds/api/users/default/recommendations?v=2"
504                 elif feedname == "my_watch_later":
505                         url = "http://gdata.youtube.com/feeds/api/users/default/watch_later?v=2"
506                 elif feedname == "my_uploads":
507                         url = "http://gdata.youtube.com/feeds/api/users/default/uploads"
508                 elif feedname in ("hd", "most_popular", "most_shared", "on_the_web"):
509                         if feedname == "hd":
510                                 url = "http://gdata.youtube.com/feeds/api/videos/-/HD"
511                         else:
512                                 url = url + feedname
513                 elif feedname in ("top_rated","most_viewed","recently_featured","top_favorites","most_recent","most_discussed","most_linked","most_responded"):
514                         url = None
515                         ytservice = self.getFeedService(feedname)
516
517                 queryThread = YoutubeQueryThread(ytservice, url, self.gotFeed, self.gotFeedError, callback, errorback)
518                 queryThread.start()
519                 return queryThread
520
521         def search(self, searchTerms, startIndex = 1, maxResults = 25,
522                                         orderby = "relevance", time = 'all_time', racy = "include",
523                                         author = "", lr = "", categories = "", sortOrder = "ascending",
524                                         callback = None, errorback = None):
525                 print "[MyTube] MyTubePlayerService - search()"
526                 self.feedentries = []
527                 query = gdata.youtube.service.YouTubeVideoQuery()
528                 query.vq = searchTerms
529                 query.orderby = orderby
530                 query.time = time
531                 query.racy = racy
532                 query.sortorder = sortOrder
533                 if lr is not None:
534                         query.lr = lr
535                 if categories[0] is not None:
536                         query.categories = categories
537                 query.start_index = startIndex
538                 query.max_results = maxResults
539                 queryThread = YoutubeQueryThread(self.yt_service.YouTubeQuery, query, self.gotFeed, self.gotFeedError, callback, errorback)
540                 queryThread.start()
541                 return queryThread
542
543         def gotFeed(self, feed, callback):
544                 if feed is not None:
545                         self.feed = feed
546                         for entry in self.feed.entry:
547                                 MyFeedEntry = MyTubeFeedEntry(self, entry)
548                                 self.feedentries.append(MyFeedEntry)
549                 if callback is not None:
550                         callback(self.feed)
551
552         def gotFeedError(self, exception, errorback):
553                 if errorback is not None:
554                         errorback(exception)
555
556         def SubscribeToUser(self, username):
557                 try:
558                         new_subscription = self.yt_service.AddSubscriptionToChannel(username_to_subscribe_to=username)
559         
560                         if isinstance(new_subscription, gdata.youtube.YouTubeSubscriptionEntry):
561                                 print '[MyTube] MyTubePlayerService: New subscription added'
562                                 return _('New subscription added')
563                         
564                         return _('Unknown error')
565                 except gdata.service.RequestError as req:
566                         return str('Error: ' + str(req[0]["body"]))
567                 except Exception as e:
568                         return str('Error: ' + e)
569         
570         def addToFavorites(self, video_id):
571                 try:
572                         video_entry = self.yt_service.GetYouTubeVideoEntry(video_id=video_id)
573                         response = self.yt_service.AddVideoEntryToFavorites(video_entry)
574                         
575                         # The response, if succesfully posted is a YouTubeVideoEntry
576                         if isinstance(response, gdata.youtube.YouTubeVideoEntry):
577                                 print '[MyTube] MyTubePlayerService: Video successfully added to favorites'
578                                 return _('Video successfully added to favorites')       
579         
580                         return _('Unknown error')
581                 except gdata.service.RequestError as req:
582                         return str('Error: ' + str(req[0]["body"]))
583                 except Exception as e:
584                         return str('Error: ' + e)
585         
586         def getTitle(self):
587                 return self.feed.title.text
588
589         def getEntries(self):
590                 return self.feedentries
591
592         def itemCount(self):
593                 return self.feed.items_per_page.text
594
595         def getTotalResults(self):
596                 if self.feed.total_results is None:
597                         return 0
598                                 
599                 return self.feed.total_results.text
600
601         def getNextFeedEntriesURL(self):
602                 for link in self.feed.link:
603                         if link.rel == "next":
604                                 return link.href
605                 return None
606
607         def getCurrentPage(self):
608                 if self.feed.start_index is None:
609                         return 1
610                 
611                 return int(int(self.feed.start_index.text) / int(self.itemCount())) + 1
612
613 class YoutubeQueryThread(Thread):
614         def __init__(self, query, param, gotFeed, gotFeedError, callback, errorback):
615                 Thread.__init__(self)
616                 self.messagePump = ePythonMessagePump()
617                 self.messages = ThreadQueue()
618                 self.gotFeed = gotFeed
619                 self.gotFeedError = gotFeedError
620                 self.callback = callback
621                 self.errorback = errorback
622                 self.query = query
623                 self.param = param
624                 self.canceled = False
625                 self.messagepPump_conn = self.messagePump.recv_msg.connect(self.finished)
626
627         def cancel(self):
628                 self.canceled = True
629
630         def run(self):
631                 try:
632                         if self.param is None:
633                                 feed = self.query()
634                         else:
635                                 feed = self.query(self.param)
636                         self.messages.push((True, feed, self.callback))
637                         self.messagePump.send(0)
638                 except Exception, ex:
639                         self.messages.push((False, ex, self.errorback))
640                         self.messagePump.send(0)
641
642         def finished(self, val):
643                 if not self.canceled:
644                         message = self.messages.pop()
645                         if message[0]:
646                                 self.gotFeed(message[1], message[2])
647                         else:
648                                 self.gotFeedError(message[1], message[2])
649
650 myTubeService = MyTubePlayerService()
651