YouTube has changed its "inofficial api" to extract the location of a video.
[enigma2-plugins.git] / youtubeplayer / src / YouTubeInterface.py
1 ############################################################################
2 #    Copyright (C) 2008 by Volker Christian                                #
3 #    Volker.Christian@fh-hagenberg.at                                      #
4 #                                                                          #
5 #    This program is free software; you can redistribute it and#or modify  #
6 #    it under the terms of the GNU General Public License as published by  #
7 #    the Free Software Foundation; either version 2 of the License, or     #
8 #    (at your option) any later version.                                   #
9 #                                                                          #
10 #    This program is distributed in the hope that it will be useful,       #
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
13 #    GNU General Public License for more details.                          #
14 #                                                                          #
15 #    You should have received a copy of the GNU General Public License     #
16 #    along with this program; if not, write to the                         #
17 #    Free Software Foundation, Inc.,                                       #
18 #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
19 ############################################################################
20
21 import gdata.youtube
22 import gdata.youtube.service
23
24 from gdata.service import BadAuthentication
25
26 from Tools.LoadPixmap import LoadPixmap
27
28 from twisted.web.client import downloadPage
29
30 #from urllib import urlretrieve, quote
31
32 from urllib2 import urlopen, Request, URLError, HTTPError, quote
33
34 from httplib import HTTPConnection
35
36 from socket import gaierror
37
38 import os
39 import re
40
41 # http://code.google.com/apis/youtube/reference.html#youtube_data_api_tag_media:group
42
43
44 class YouTubeUser():
45         def __init__(self, cfg):
46                 self.cfg = cfg
47
48
49         def getCfg(self):
50                 return self.cfg
51
52
53         def getName(self):
54                 return self.cfg.name.value
55
56
57         def name(self):
58                 return self.cfg.name
59
60
61         def getEmail(self):
62                 return self.cfg.email.value
63
64
65         def email(self):
66                 return self.cfg.email
67
68
69         def getPassword(self):
70                 return self.cfg.password.value
71
72
73         def password(self):
74                 return self.cfg.password
75
76
77         def login(self):
78                 return interface.login(self)
79
80
81 class YouTubeFeed():
82         def __init__(self, feed, favoritesFeed = False):
83                 print "[YTB] YouTubeFeed::__init__()"
84                 self.feed = feed
85                 self.favoritesFeed = favoritesFeed
86                 self.entries = []
87                 self.update()
88
89
90         def update(self):
91                 print "[YTB] YouTubeFeed::update()"
92                 sequenceNumber = int(self.feed.start_index.text)
93                 print self.feed.entry
94                 for entry in self.feed.entry:
95                         self.entries.append(YouTubeEntry(self, entry, sequenceNumber, self.favoritesFeed))
96                         sequenceNumber = sequenceNumber + 1
97
98
99         def getTitle(self):
100                 return self.feed.title.text
101
102
103         def getEntries(self):
104                 print "[YTB] YouTubeFeed::getEntries()"
105                 return self.entries
106
107
108         def itemCount(self):
109                 print "[YTB] YouTubeFeed::itemCount()"
110                 return self.feed.items_per_page.text
111
112
113         def getTotalResults(self):
114                 return self.feed.total_results.text
115         
116
117         def getNextFeed(self):
118                 print "[YTB] YouTubeFeed::getNextFeed()"
119                 for link in self.feed.link:
120                         if link.rel == "next":
121                                 return link.href
122                 return None
123
124
125         def getPreviousFeed(self):
126                 print "[YTB] YouTubeFeed::getPreviousFeed()"
127                 for link in self.feed.link:
128                         if link.rel == "previous":
129                                 return link.href
130                 return None
131
132
133         def getSelfFeed(self):
134                 print "[YTB] YouTubeFeed::getSelfFeed()"
135                 for link in self.feed.link:
136                         if link.rel == "self":
137                                 return link.href
138                 return None
139
140
141         def loadThumbnails(self, callback):
142                 print "[YTB] YouTubeFeed::loadThumbnails()"
143                 for entry in self.entries:
144                         entry.loadThumbnails(callback)
145
146
147 class YouTubeEntry():
148         def __init__(self, feed, entry, sequenceNumber, favoritesFeed = False):
149                 print "[YTB] YouTubeEntry::__init__()"
150                 self.feed = feed
151                 self.entry = entry
152                 self.sequenceNumber = sequenceNumber
153                 self.favoritesFeed = favoritesFeed
154                 self.thumbnail = {}
155
156
157         def isPlaylistEntry(self):
158                 return False
159
160
161         def getYouTubeId(self):
162                 print "[YTB] YouTubeEntry::getYouTubeId()"
163                 ret = None
164                 if self.entry.media.player:
165                         ret = self.entry.media.player.url.split("=").pop()
166                 return ret
167
168
169         def getTitle(self):
170                 print "[YTB] YouTubeEntry::getTitle()"
171                 return self.entry.media.title.text
172
173
174         def getDescription(self):
175                 print "[YTB] YouTubeEntry::getDescription()"
176                 return self.entry.media.description.text
177
178
179         def getThumbnailUrl(self, index):
180                 print "[YTB] YouTubeEntry::getThumbnailUrl"
181                 if index < len(self.entry.media.thumbnail):
182                         return self.entry.media.thumbnail[index].url
183                 return None
184
185
186         def getRelatedFeed(self):
187                 print "[YTB] YouTubeEntry::getRelatedFeed()"
188                 for link in self.entry.link:
189                         print "Related link: ", link.rel.endswith
190                         if link.rel.endswith("video.related"):
191                                 print "Found Related: ", link.href
192                                 return link.href
193
194
195         def getResponsesFeed(self):
196                 print "[YTB] YouTubeEntry::getResponseFeed()"
197                 for link in self.entry.link:
198                         print "Responses link: ", link.rel.endswith
199                         if link.rel.endswith("video.responses"):
200                                 print "Found Responses: ", link.href
201                                 return link.href
202
203
204         def loadThumbnail(self, index, callback):
205                 print "[YTB] YouTubeEntry::loadThumbnail()"
206                 thumbnailUrl = self.getThumbnailUrl(index)
207                 if thumbnailUrl is not None and self.getYouTubeId() is not None:
208                         thumbnailFile = "/tmp/" + self.getYouTubeId() + "_" + str(index) + ".jpg"
209                         self.thumbnail[str(index)] = None
210                         cookie = {"entry" : self, "file" : thumbnailFile, "callback" : callback, "index" : index}
211                         downloadPage(thumbnailUrl, thumbnailFile).addCallback(fetchFinished, cookie).addErrback(fetchFailed, cookie)
212                 
213
214         def loadThumbnails(self, callback):
215                 print "[YTB] YouTubeEntry::loadThumbnails()"
216                 self.loadThumbnail(0, callback)
217
218
219 #       def getVideoUrl(self, fmt):
220 #               conn = HTTPConnection("www.youtube.com")
221 #               conn.request("GET", "/v/" + quote(self.getYouTubeId()))
222 #               response = conn.getresponse()
223 #               conn.close()
224 #               mrl = None
225 #               print "[YTB] Response: ", response.status, response.reason
226 #               if response.status == 303:
227 #                       location = response.getheader("location")
228 #                       mrl = "http://www.youtube.com/get_video?video_id=" + quote(self.getYouTubeId()) + "&t=" + quote(re.match (".*[?&]t=([^&]+)", location).groups()[0]) + fmt
229 #                       print "[YTB] Playing ", mrl
230 #               else:
231 #                       print "[YTB] No valid flv-mrl found"
232 #               return mrl
233
234
235         def getVideoUrl(self, fmt):
236                 mrl = None
237                 req = Request("http://www.youtube.com/watch?v=" + quote(self.getYouTubeId()))
238                 try:
239                         response = urlopen(req)
240                 except HTTPError, e:
241                         print "[YTB] The server coundn't fulfill the request."
242                         print "[YTB] Error code: ", e.code
243                 except URLError, e:
244                         print "[YTB] We failed to reach a server."
245                         print "[YTB] Reason: ", e.reason
246                 else:
247                         while not None:
248                                 data = response.readline()
249                                 if data == "":
250                                         break
251                                 m = re.search("watch_fullscreen\\?(?P<vid_query>.*?)&title=(?P<name>.*)';\n", data)
252                                 if m:
253                                         break
254                         response.close
255                         if m:
256                                 mrl = "http://www.youtube.com/get_video?" + quote(m.group("vid_query")) + fmt
257                                 print "[YTB] Playing ", mrl
258                         else:
259                                 print "[YTB] No valid flv-mrl found"
260                 return mrl
261
262
263         def getDuration(self):
264                 if self.entry.media is not None and self.entry.media.duration is not None:
265                         return self.entry.media.duration.seconds
266                 return "not available"
267
268         
269         def getRatingAverage(self):
270                 if self.entry.rating is not None:
271                         return self.entry.rating.average
272                 return "not available"
273
274
275         def getNumRaters(self):
276                 if self.entry.rating is not None:
277                         return self.entry.rating.num_raters
278                 return ""
279         
280         
281         def getRatingMax(self):
282                 if self.entry.rating is not None:
283                         return self.entry.rating.max
284                 return "not available"
285         
286         def getRatingMin(self):
287                 if self.entry.rating is not None:
288                         return self.entry.rating.min
289                 return "not available"
290
291         
292         def getFavoriteCount(self):
293                 if self.entry.statistics is not None:
294                         return self.entry.statistics.favorite_count
295                 return "not available"
296         
297         
298         def getViewCount(self):
299                 if self.entry.statistics is not None:
300                         return self.entry.statistics.view_count
301                 return "not available"
302
303         
304         def getAuthor(self):
305                 authorList = []
306                 for author in self.entry.author:
307                         authorList.append(author.name.text)
308                 authors = ", ".join(authorList)
309                 return authors
310
311         
312         def getPublishedOn(self):
313                 if self.entry.published is not None:
314                         return self.entry.published.text
315                 return "unknown"
316
317         
318         def getCategory(self):
319                 return self.entry.GetYouTubeCategoryAsString()
320
321         
322         def getTags(self):
323                 if self.entry.media is not None and self.entry.media.keywords is not None:
324                         return self.entry.media.keywords.text
325                 return "not available"
326
327
328         def belongsToFavorites(self):
329                 return self.favoritesFeed
330
331
332         def belongsToPlaylistId(self):
333                 return self.playlistId
334
335
336 class YouTubePlaylistFeed():
337         def __init__(self, feed):
338                 print "[YTB] YouTubePlayListFeed::__init__()"
339                 self.feed = feed
340                 self.entries = []
341                 self.update()
342
343
344         def update(self):
345                 print "[YTB] YouTubePlayListFeed::update()"
346                 for entry in self.feed.entry:
347                         self.entries.append(YouTubePlaylistEntry(entry))
348
349
350         def getTitle(self):
351                 print "[YTB] YouTubePlayListFeed::getTitle()"
352                 return self.feed.title.text
353
354
355         def getEntries(self):
356                 print "[YTB] YouTubePlayListFeed::getEntries()"
357                 return self.entries
358
359
360 class YouTubePlaylistEntry():
361         def __init__(self, entry):
362                 print "[YTB] YouTubePlaylistEntry::__init__()"
363                 self.entry = entry
364
365
366         def getTitle(self):
367                 print "[YTB] YouTubePlaylistEntry::getTitle()"
368                 return self.entry.title.text
369
370
371         def getDescription(self):
372                 print "[YTB] YouTubePlaylistEntry::getDescription()"
373                 return self.entry.description.text
374
375
376         def getFeed(self, index = 0):
377                 print "[YTB] YouTubePlaylistEntry::getFeed()"
378                 return self.entry.feed_link[index].href
379
380         
381         def getSelfFeed(self):
382                 print "[YTB] YouTubeFeed::getSelfFeed()"
383                 for link in self.entry.link:
384                         if link.rel == "self":
385                                 return link.href
386                 return None
387
388
389 class YouTubePlaylistVideoFeed(YouTubeFeed):
390         def __init__(self, feed):
391                 print "[YTB] YouTubePlaylistVideoFeed::__init__()"
392                 YouTubeFeed.__init__(self, feed)
393
394
395         def update(self):
396                 print "[YTB] YouTubePlaylistVideoFeed::update()"
397                 sequenceNumber = 1
398                 print self.feed.entry
399                 for entry in self.feed.entry:
400                         self.entries.append(YouTubePlaylistVideoEntry(self, entry, sequenceNumber))
401                         sequenceNumber = sequenceNumber + 1
402
403         def getFeed(self):
404                 print "[YTB] YouTubeFeed::getSelfFeed()"
405                 for link in self.feed.link:
406                         if link.rel == "feed":
407                                 return link.href
408                 return None
409
410
411 class YouTubePlaylistVideoEntry(YouTubeEntry):
412         def __init__(self, feed, entry, sequenceNumber):
413                 print "[YTB] YouTubePlaylistVideoEntry::__init__()"
414                 YouTubeEntry.__init__(self, feed, entry, sequenceNumber)
415
416
417         def isPlaylistEntry(self):
418                 return True
419
420
421         def getSelf(self):
422                 print "[YTB] YouTubePlaylistVideoEntry::getSelfFeed()"
423                 for link in self.entry.link:
424                         if link.rel == "self":
425                                 return link.href
426                 return None
427
428
429         YOUTUBE_DEVELOPER_TAG_SCHEME = "http://gdata.youtube.com/schemas/2007/developertags.cat"
430         def getCategory(self):
431                 for category in self.entry.media.category:
432                         if category.scheme != YouTubePlaylistVideoEntry.YOUTUBE_DEVELOPER_TAG_SCHEME:
433                                 return category.text
434                 return "not available"
435
436
437 class YouTubeInterface():
438 #       Do not change the client_id and developer_key in the login-section!
439 #       ClientId: ytapi-VolkerChristian-YouTubePlayer-pq3mrg1o-0
440 #       DeveloperKey: AI39si7t0WNyg_tvjBPdRIvBfaUA_XrTY1LNzfjLgCn8A_m92YKtWTcR_auEmI5gKGitJb4SskrjxJSmRc3yhQ4YlHTBAzPSig
441         def __init__(self):
442                 print "[YTB] YouTubeInterface::__init__()"
443
444
445         def open(self):
446                 self.ytService = gdata.youtube.service.YouTubeService()
447                 print "[YTB] YouTubeInterface::open()"
448                 self.loggedIn = False
449
450
451         def close(self):
452                 print "[YTB] YouTubeInterface::close()"
453                 del self.ytService
454                 self.loggedIn = False
455
456
457         def login(self, user):
458                 print "[YTB] YouTubeInterface::login()"
459                 ret = False
460                 if user is not None:
461                         try:
462                                 # http://code.google.com/apis/youtube/developers_guide_python.html#ClientLogin
463                                 self.ytService.email = user.getEmail()
464                                 self.ytService.password = user.getPassword()
465                                 self.ytService.source = 'my-example-application'
466                                 self.ytService.developer_key = "AI39si7t0WNyg_tvjBPdRIvBfaUA_XrTY1LNzfjLgCn8A_m92YKtWTcR_auEmI5gKGitJb4SskrjxJSmRc3yhQ4YlHTBAzPSig"
467                                 self.ytService.client_id = "ytapi-VolkerChristian-YouTubePlayer-pq3mrg1o-0"
468                                 self.ytService.ProgrammaticLogin()
469                         except BadAuthentication:
470                                 pass
471                         else:
472                                 self.loggedIn = True
473                                 ret = True
474                 return ret
475
476
477         def isLoggedIn(self):
478                 return self.loggedIn
479
480
481         def search(self, searchTerms, startIndex = 1, maxResults = 25,
482                                         orderby = "relevance", time = "all_time", racy = "include", 
483                                         author = ""):
484                 print "[YTB] YouTubeInterface::search()"
485                 query = gdata.youtube.service.YouTubeVideoQuery()
486                 query.vq = searchTerms
487                 query.orderby = orderby
488                 query.racy = racy
489                 query.start_index = startIndex
490                 query.max_results = maxResults
491                 try:
492                         feed = YouTubeFeed(self.ytService.YouTubeQuery(query))
493                 except gaierror:
494                         feed = None
495                 return feed
496
497
498         def getFeed(self, url):
499                 return YouTubeFeed(self.ytService.GetYouTubeVideoFeed(url))
500
501
502         def getUserFavoritesFeed(self, userName = "default"):
503                 return YouTubeFeed(self.ytService.GetUserFavoritesFeed(userName), favoritesFeed = True)
504
505
506         def getUserPlaylistFeed(self, playlistEntry):
507                 print "[YTB] getUserPlaylistFeed: ", playlistEntry.getFeed()
508                 return YouTubePlaylistVideoFeed(self.ytService.GetYouTubePlaylistVideoFeed(playlistEntry.getFeed()))
509
510
511         def addToFavorites(self, entry):
512                 response = self.ytService.AddVideoEntryToFavorites(entry.entry)
513                 # The response, if succesfully posted is a YouTubeVideoEntry
514                 if isinstance(response, gdata.youtube.YouTubeVideoEntry):
515                         print "[YTB] Video successfully added to favorites"
516                         return response
517                 else:
518                         return None
519
520
521         def removeFromFavorites(self, entry):
522                 response = self.ytService.DeleteVideoEntryFromFavorites(entry.getYouTubeId())
523                 if response is True:
524                         print "[YTB] Video deleted from favorites"
525                 return response
526
527
528         def getPlaylistFeed(self):
529                 return YouTubePlaylistFeed(self.ytService.GetYouTubePlaylistFeed())
530
531
532         def addPlaylist(self, name, description, private):
533                 newPlaylist = None
534                 newPlaylistEntry = self.ytService.AddPlaylist(name, description, private)
535                 if isinstance(newPlaylistEntry, gdata.youtube.YouTubePlaylistEntry):
536                         newPlaylist = YouTubePlaylistEntry(newPlaylistEntry)
537                 return newPlaylist
538
539
540         def deletePlaylist(self, playlistEntry):
541                 playListUrl = playlistEntry.getSelfFeed()
542                 return self.ytService.DeletePlaylist(playListUrl)
543
544         
545         def removeFromPlaylist(self, playlistVideoEntry):
546                 print "[YTB] Removing from Playlist"
547                 response = self.ytService.Delete(playlistVideoEntry.getSelf())
548                 if response:
549                         print "[YTB] Successfull deleted"
550                 else:
551                         print "[YTB] Delete unsuccessfull"
552                 return response
553
554
555         def addToPlaylist(self, playlistEntry, videoEntry):
556                 print "[YTB] Adding to Playlist"
557                 playlistUri = playlistEntry.getFeed()
558                 response = self.ytService.AddPlaylistVideoEntryToPlaylist(
559                                                 playlistUri, videoEntry.getYouTubeId(), videoEntry.getTitle(), videoEntry.getDescription())
560                 if isinstance(response, gdata.youtube.YouTubePlaylistVideoEntry):
561                         print "[YTB] Video added"
562                         return response
563                 else:
564                         return None
565
566
567 def fetchFailed(string, cookie):
568         print "[YTB] fetchFailed(): ", string
569         if os.path.exists(cookie["file"]):
570                 os.remove(cookie["file"])
571         cookie["callback"](cookie["entry"])
572
573
574 def fetchFinished(string, cookie):
575         print "[YTB] fetchFinished(): ", string
576         if os.path.exists(cookie["file"]):
577                 print "Loading filename %s" % cookie["file"]
578                 cookie["entry"].thumbnail[str(cookie["index"])] = LoadPixmap(cookie["file"])
579                 os.remove(cookie["file"])
580         cookie["callback"](cookie["entry"])
581
582
583 interface = YouTubeInterface()