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