- fix hd stream detection after recent Youtube changes.
[enigma2-plugins.git] / mytube / src / MyTubeService.py
1 # -*- coding: iso-8859-1 -*-
2 from __init__ import _
3 import gdata.youtube
4 import gdata.youtube.service
5 from gdata.service import BadAuthentication
6 from Tools.LoadPixmap import LoadPixmap
7 from Components.config import config, Config, ConfigSelection, ConfigText, getConfigListEntry, ConfigSubsection, ConfigYesNo, ConfigIP, ConfigNumber
8 from Components.ConfigList import ConfigListScreen
9 from Components.config import KEY_DELETE, KEY_BACKSPACE, KEY_LEFT, KEY_RIGHT, KEY_HOME, KEY_END, KEY_TOGGLEOW, KEY_ASCII, KEY_TIMEOUT
10
11 from twisted.web import client
12 from twisted.internet import reactor
13 from urllib2 import Request, URLError, HTTPError, urlopen as urlopen2
14 from socket import gaierror,error
15 import re, os, sys, socket
16 from urllib import quote, unquote_plus, unquote   #FancyURLopener,
17 import cookielib
18 from httplib import HTTPConnection,CannotSendRequest,BadStatusLine,HTTPException
19 HTTPConnection.debuglevel = 1
20
21 std_headers = {
22         'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2',
23         'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
24         'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
25         'Accept-Language': 'en-us,en;q=0.5',
26 }
27
28 #config.plugins.mytube = ConfigSubsection()
29 #config.plugins.mytube.general = ConfigSubsection()
30 #config.plugins.mytube.general.useHTTPProxy = ConfigYesNo(default = False)
31 #config.plugins.mytube.general.ProxyIP = ConfigIP(default=[0,0,0,0])
32 #config.plugins.mytube.general.ProxyPort = ConfigNumber(default=8080)
33 #class MyOpener(FancyURLopener):
34 #       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'
35
36
37 class GoogleSuggestions():
38         def __init__(self, callback, ds = None, json = None, hl = None):
39                 self.callback = callback
40                 self.conn = HTTPConnection("google.com")
41                 self.prepQuerry = "/complete/search?"
42                 if ds is not None:
43                         self.prepQuerry = self.prepQuerry + "ds=" + ds + "&"
44                 if json is not None:
45                         self.prepQuerry = self.prepQuerry + "json=" + json + "&"
46                 if hl is not None:
47                         self.prepQuerry = self.prepQuerry + "hl=" + hl + "&"
48                 self.prepQuerry = self.prepQuerry + "jsonp=self.gotSuggestions&q="
49
50         def gotSuggestions(self, suggestslist):
51                 self.callback(suggestslist)
52
53         def getSuggestions(self, querryString):
54                 if querryString is not "":
55                         querry = self.prepQuerry + quote(querryString)
56                         try:
57                                 self.conn.request("GET", querry)
58                         except (CannotSendRequest, gaierror, error):
59                                 print "[YTB] Can not send request for suggestions"
60                                 self.callback(None)
61                         else:
62                                 try:
63                                         response = self.conn.getresponse()
64                                 except BadStatusLine:
65                                         print "[YTB] Can not get a response from google"
66                                         self.callback(None)
67                                 else:
68                                         if response.status == 200:
69                                                 data = response.read()
70                                                 exec data
71                                         else:
72                                                 self.callback(None)
73                         self.conn.close()
74                 else:
75                         self.callback(None)
76
77
78 class MyTubeFeedEntry():
79         def __init__(self, feed, entry, favoritesFeed = False):
80                 self.feed = feed
81                 self.entry = entry
82                 self.favoritesFeed = favoritesFeed
83                 self.thumbnail = {}
84                 """self.myopener = MyOpener()
85                 urllib.urlopen = MyOpener().open
86                 if config.plugins.mytube.general.useHTTPProxy.value is True:
87                         proxy = {'http': 'http://'+str(config.plugins.mytube.general.ProxyIP.getText())+':'+str(config.plugins.mytube.general.ProxyPort.value)}
88                         self.myopener = MyOpener(proxies=proxy)
89                         urllib.urlopen = MyOpener(proxies=proxy).open
90                 else:
91                         self.myopener = MyOpener()
92                         urllib.urlopen = MyOpener().open"""
93                 
94         def isPlaylistEntry(self):
95                 return False
96
97         def getTubeId(self):
98                 #print "[MyTubeFeedEntry] getTubeId"
99                 ret = None
100                 if self.entry.media.player:
101                         split = self.entry.media.player.url.split("=")
102                         ret = split.pop()
103                         if ret == 'youtube_gdata':
104                                 tmpval=split.pop()
105                                 if tmpval.endswith("&feature"):
106                                         tmp = tmpval.split("&")
107                                         ret = tmp.pop(0)
108                 return ret
109
110         def getTitle(self):
111                 #print "[MyTubeFeedEntry] getTitle",self.entry.media.title.text
112                 return self.entry.media.title.text
113
114         def getDescription(self):
115                 #print "[MyTubeFeedEntry] getDescription"
116                 if self.entry.media is not None and self.entry.media.description is not None:
117                         return self.entry.media.description.text
118                 return "not vailable"
119
120         def getThumbnailUrl(self, index = 0):
121                 #print "[MyTubeFeedEntry] getThumbnailUrl"
122                 if index < len(self.entry.media.thumbnail):
123                         return self.entry.media.thumbnail[index].url
124                 return None
125
126         def getPublishedDate(self):
127                 if self.entry.published is not None:
128                         return self.entry.published.text
129                 return "unknown"
130
131         def getViews(self):
132                 if self.entry.statistics is not None:
133                         return self.entry.statistics.view_count
134                 return "not available"
135         
136         def getDuration(self):
137                 if self.entry.media is not None and self.entry.media.duration is not None:
138                         return self.entry.media.duration.seconds
139                 else:
140                         return 0
141
142         def getRatingAverage(self):
143                 if self.entry.rating is not None:
144                         return self.entry.rating.average
145                 return 0
146
147
148         def getNumRaters(self):
149                 if self.entry.rating is not None:
150                         return self.entry.rating.num_raters
151                 return ""       
152
153         def getAuthor(self):
154                 authors = []
155                 for author in self.entry.author:
156                         authors.append(author.name.text)
157                 author = ", ".join(authors)
158                 return author
159
160         def PrintEntryDetails(self):
161                 EntryDetails = { 'Title': None, 'TubeID': None, 'Published': None, 'Published': None, 'Description': None, 'Category': None, 'Tags': None, 'Duration': None, 'Views': None, 'Rating': None, 'Thumbnails': None}
162                 EntryDetails['Title'] = self.entry.media.title.text
163                 EntryDetails['TubeID'] = self.getTubeId()
164                 EntryDetails['Description'] = self.getDescription()
165                 EntryDetails['Category'] = self.entry.media.category[0].text
166                 EntryDetails['Tags'] = self.entry.media.keywords.text
167                 EntryDetails['Published'] = self.getPublishedDate()
168                 EntryDetails['Views'] = self.getViews()
169                 EntryDetails['Duration'] = self.getDuration()
170                 EntryDetails['Rating'] = self.getNumRaters()
171                 EntryDetails['RatingAverage'] = self.getRatingAverage()
172                 EntryDetails['Author'] = self.getAuthor()
173                 # show thumbnails
174                 list = []
175                 for thumbnail in self.entry.media.thumbnail:
176                         print 'Thumbnail url: %s' % thumbnail.url
177                         list.append(str(thumbnail.url))
178                 EntryDetails['Thumbnails'] = list
179                 #print EntryDetails
180                 return EntryDetails
181
182
183         def getVideoUrl(self):
184                 mrl = None
185                 isHDAvailable = False
186                 video_id = str(self.getTubeId())
187                 watch_url = "http://www.youtube.com/watch?v=" + video_id
188                 watchrequest = Request(watch_url, None, std_headers)
189                 try:
190                         print "trying to find out if a HD Stream is available"
191                         watchvideopage = urlopen2(watchrequest).read()
192                 except (urllib2.URLError, httplib.HTTPException, socket.error), err:
193                         print "[MyTube] Error: Unable to retrieve watchpage"
194                         print "[MyTube] Error code: ", str(err)
195                         print "[MyTube] No valid mp4-url found"
196                         return mrl
197
198                 if "'IS_HD_AVAILABLE': true" in watchvideopage:
199                         isHDAvailable = True
200                         print "HD AVAILABLE"
201                 else:
202                         print "HD Stream NOT AVAILABLE"
203
204                 # Get video info
205                 info_url = 'http://www.youtube.com/get_video_info?&video_id=%s&el=detailpage&ps=default&eurl=&gl=US&hl=en' % video_id
206                 inforequest = Request(info_url, None, std_headers)
207                 try:
208                         print "getting video_info_webpage",info_url
209                         infopage = urlopen2(inforequest).read()
210                 except (urllib2.URLError, httplib.HTTPException, socket.error), err:
211                         print "[MyTube] Error: Unable to retrieve infopage"
212                         print "[MyTube] Error code: ", str(err)
213                         print "[MyTube] No valid mp4-url found"
214                         return mrl
215
216                 mobj = re.search(r'(?m)&token=([^&]+)(?:&|$)', infopage)
217                 if mobj is None:
218                         # was there an error ?
219                         mobj = re.search(r'(?m)&reason=([^&]+)(?:&|$)', infopage)
220                         if mobj is None:
221                                 print 'ERROR: unable to extract "t" parameter for unknown reason'
222                         else:
223                                 reason = unquote_plus(mobj.group(1))
224                                 print 'ERROR: YouTube said: %s' % reason.decode('utf-8')
225                         return mrl
226         
227                 token = unquote(mobj.group(1))
228                 myurl = 'http://www.youtube.com/get_video?video_id=%s&t=%s&eurl=&el=detailpage&ps=default&gl=US&hl=en' % (video_id, token)
229                 if isHDAvailable is True:
230                         mrl = '%s&fmt=%s' % (myurl, '22')
231                         print "[MyTube] GOT HD URL: ", mrl
232                 else:
233                         mrl = '%s&fmt=%s' % (myurl, '18')
234                         print "[MyTube] GOT SD URL: ", mrl
235
236                 return mrl
237
238         def getRelatedVideos(self):
239                 print "[MyTubeFeedEntry] getResponseVideos()"
240                 for link in self.entry.link:
241                         #print "Related link: ", link.rel.endswith
242                         if link.rel.endswith("video.related"):
243                                 print "Found Related: ", link.href
244                                 return link.href
245
246         def getResponseVideos(self):
247                 print "[MyTubeFeedEntry] getResponseVideos()"
248                 for link in self.entry.link:
249                         #print "Responses link: ", link.rel.endswith
250                         if link.rel.endswith("video.responses"):
251                                 print "Found Responses: ", link.href
252                                 return link.href
253
254 class MyTubePlayerService():
255 #       Do not change the client_id and developer_key in the login-section!
256 #       ClientId: ytapi-dream-MyTubePlayer-i0kqrebg-0
257 #       DeveloperKey: AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw
258         def __init__(self):
259                 print "[MyTube] MyTubePlayerService - init"
260                 self.feedentries = []
261                 self.feed = None
262                 
263         def startService(self):
264                 print "[MyTube] MyTubePlayerService - startService"
265                 self.yt_service = gdata.youtube.service.YouTubeService()
266                 self.yt_service.developer_key = 'AI39si4AjyvU8GoJGncYzmqMCwelUnqjEMWTFCcUtK-VUzvWygvwPO-sadNwW5tNj9DDCHju3nnJEPvFy4WZZ6hzFYCx8rJ6Mw'
267                 self.yt_service.client_id = 'ytapi-dream-MyTubePlayer-i0kqrebg-0'
268                 self.loggedIn = False
269                 #os.environ['http_proxy'] = 'http://169.229.50.12:3128'
270                 #proxy = os.environ.get('http_proxy')
271                 #print "FOUND ENV PROXY-->",proxy
272                 #for a in os.environ.keys():
273                 #       print a
274
275         def stopService(self):
276                 print "[MyTube] MyTubePlayerService - stopService"
277                 del self.ytService
278                 self.loggedIn = False
279
280         def isLoggedIn(self):
281                 return self.loggedIn
282
283         def getFeed(self, url):
284                 print "[MyTube] MyTubePlayerService - getFeed:",url
285                 self.feedentries = []
286                 self.feed = self.yt_service.GetYouTubeVideoFeed(url)
287                 for entry in self.feed.entry:
288                         MyFeedEntry = MyTubeFeedEntry(self, entry)
289                         self.feedentries.append(MyFeedEntry)
290                 return self.feed                        
291
292         def search(self, searchTerms, startIndex = 1, maxResults = 25,
293                                         orderby = "relevance", racy = "include", 
294                                         author = "", lr = "", categories = "", sortOrder = "ascending"):
295                 print "[MyTube] MyTubePlayerService - search()"
296                 self.feedentries = []
297                 query = gdata.youtube.service.YouTubeVideoQuery()
298                 query.vq = searchTerms
299                 query.orderby = orderby
300                 query.racy = racy
301                 query.sortorder = sortOrder
302                 if lr is not None:
303                         query.lr = lr
304                 if categories[0] is not None:
305                         query.categories = categories
306                 query.start_index = startIndex
307                 query.max_results = maxResults
308                 try:
309                         feed = self.yt_service.YouTubeQuery(query)
310                 except gaierror:
311                         feed = None
312                 if feed is not None:
313                         self.feed = feed
314                         for entry in self.feed.entry:
315                                 MyFeedEntry = MyTubeFeedEntry(self, entry)
316                                 self.feedentries.append(MyFeedEntry)
317                 return self.feed                
318
319         def getTitle(self):
320                 return self.feed.title.text
321
322         def getEntries(self):
323                 return self.feedentries
324
325         def itemCount(self):
326                 return self.feed.items_per_page.text
327
328         def getTotalResults(self):
329                 return self.feed.total_results.text
330         
331         def getNextFeedEntriesURL(self):
332                 for link in self.feed.link:
333                         if link.rel == "next":
334                                 return link.href
335                 return None
336
337
338 myTubeService = MyTubePlayerService()