Format and time parameter prepared
[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?video_id=" + quote(self.getYouTubeId()) + "&t=" + quote(re.match (".*[?&]t=([^&]+)", m.group('vid_query')).groups()[0]) + "&fmt=" + fmt
255 #"&sk=" + quote(re.match (".*[?&]sk=([^&]+)", 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 = "", lr = "", categories = "", sortOrder = "ascending", format = "6"):
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.sortorder = sortOrder
489                 if lr is not None:
490                         query.lr = lr
491                 if categories[0] is not None:
492                         query.categories = categories
493 #               query.time = time
494                 query.start_index = startIndex
495                 query.max_results = maxResults
496 #               query.format = format
497                 try:
498                         feed = YouTubeFeed(self.ytService.YouTubeQuery(query))
499                 except gaierror:
500                         feed = None
501                 return feed
502
503
504         def getFeed(self, url):
505                 return YouTubeFeed(self.ytService.GetYouTubeVideoFeed(url))
506
507
508         def getUserFavoritesFeed(self, userName = "default"):
509                 return YouTubeFeed(self.ytService.GetUserFavoritesFeed(userName), favoritesFeed = True)
510
511
512         def getUserPlaylistFeed(self, playlistEntry):
513                 print "[YTB] getUserPlaylistFeed: ", playlistEntry.getFeed()
514                 return YouTubePlaylistVideoFeed(self.ytService.GetYouTubePlaylistVideoFeed(playlistEntry.getFeed()))
515
516
517         def addToFavorites(self, entry):
518                 response = self.ytService.AddVideoEntryToFavorites(entry.entry)
519                 # The response, if succesfully posted is a YouTubeVideoEntry
520                 if isinstance(response, gdata.youtube.YouTubeVideoEntry):
521                         print "[YTB] Video successfully added to favorites"
522                         return response
523                 else:
524                         return None
525
526
527         def removeFromFavorites(self, entry):
528                 response = self.ytService.DeleteVideoEntryFromFavorites(entry.getYouTubeId())
529                 if response is True:
530                         print "[YTB] Video deleted from favorites"
531                 return response
532
533
534         def getPlaylistFeed(self):
535                 return YouTubePlaylistFeed(self.ytService.GetYouTubePlaylistFeed())
536
537
538         def addPlaylist(self, name, description, private):
539                 newPlaylist = None
540                 newPlaylistEntry = self.ytService.AddPlaylist(name, description, private)
541                 if isinstance(newPlaylistEntry, gdata.youtube.YouTubePlaylistEntry):
542                         newPlaylist = YouTubePlaylistEntry(newPlaylistEntry)
543                 return newPlaylist
544
545
546         def deletePlaylist(self, playlistEntry):
547                 playListUrl = playlistEntry.getSelfFeed()
548                 return self.ytService.DeletePlaylist(playListUrl)
549
550         
551         def removeFromPlaylist(self, playlistVideoEntry):
552                 print "[YTB] Removing from Playlist"
553                 response = self.ytService.Delete(playlistVideoEntry.getSelf())
554                 if response:
555                         print "[YTB] Successfull deleted"
556                 else:
557                         print "[YTB] Delete unsuccessfull"
558                 return response
559
560
561         def addToPlaylist(self, playlistEntry, videoEntry):
562                 print "[YTB] Adding to Playlist"
563                 playlistUri = playlistEntry.getFeed()
564                 response = self.ytService.AddPlaylistVideoEntryToPlaylist(
565                                                 playlistUri, videoEntry.getYouTubeId(), videoEntry.getTitle(), videoEntry.getDescription())
566                 if isinstance(response, gdata.youtube.YouTubePlaylistVideoEntry):
567                         print "[YTB] Video added"
568                         return response
569                 else:
570                         return None
571
572
573 def fetchFailed(string, cookie):
574         print "[YTB] fetchFailed(): ", string
575         if os.path.exists(cookie["file"]):
576                 os.remove(cookie["file"])
577         cookie["callback"](cookie["entry"])
578
579
580 def fetchFinished(string, cookie):
581         print "[YTB] fetchFinished(): ", string
582         if os.path.exists(cookie["file"]):
583                 print "Loading filename %s" % cookie["file"]
584                 cookie["entry"].thumbnail[str(cookie["index"])] = LoadPixmap(cookie["file"])
585                 os.remove(cookie["file"])
586         cookie["callback"](cookie["entry"])
587
588
589 interface = YouTubeInterface()