1 # -*- coding: utf-8 -*-
2 ############################################################################
3 # Copyright (C) 2008 by Volker Christian #
4 # Volker.Christian@fh-hagenberg.at #
6 # This program is free software; you can redistribute it and#or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation; either version 2 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the #
18 # Free Software Foundation, Inc., #
19 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
20 ############################################################################
23 import gdata.youtube.service
25 from gdata.service import BadAuthentication
27 from Tools.LoadPixmap import LoadPixmap
29 from twisted.web.client import downloadPage
31 from urllib2 import urlopen, Request, URLError, HTTPError
32 #, quote, unquote, unquote_plus
33 from urllib import quote, unquote_plus, unquote
35 from httplib import HTTPConnection, HTTPException
37 from urlparse import parse_qs
39 from socket import gaierror, error
44 # http://code.google.com/apis/youtube/reference.html#youtube_data_api_tag_media:group
47 'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2',
48 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
49 'Accept': 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
50 'Accept-Language': 'en-us,en;q=0.5',
54 def __init__(self, cfg):
63 return self.cfg.name.value
71 return self.cfg.email.value
78 def getPassword(self):
79 return self.cfg.password.value
83 return self.cfg.password
87 return interface.login(self)
91 def __init__(self, feed, favoritesFeed = False):
92 print "[YTB] YouTubeFeed::__init__()"
94 self.favoritesFeed = favoritesFeed
100 print "[YTB] YouTubeFeed::update()"
101 sequenceNumber = int(self.feed.start_index.text)
102 print self.feed.entry
103 for entry in self.feed.entry:
104 self.entries.append(YouTubeEntry(self, entry, sequenceNumber, self.favoritesFeed))
105 sequenceNumber = sequenceNumber + 1
109 return self.feed.title.text
112 def getEntries(self):
113 print "[YTB] YouTubeFeed::getEntries()"
118 print "[YTB] YouTubeFeed::itemCount()"
119 return self.feed.items_per_page.text
122 def getTotalResults(self):
123 return self.feed.total_results.text
126 def getNextFeed(self):
127 print "[YTB] YouTubeFeed::getNextFeed()"
128 for link in self.feed.link:
129 if link.rel == "next":
134 def getPreviousFeed(self):
135 print "[YTB] YouTubeFeed::getPreviousFeed()"
136 for link in self.feed.link:
137 if link.rel == "previous":
142 def getSelfFeed(self):
143 print "[YTB] YouTubeFeed::getSelfFeed()"
144 for link in self.feed.link:
145 if link.rel == "self":
150 def loadThumbnails(self, callback):
151 print "[YTB] YouTubeFeed::loadThumbnails()"
152 for entry in self.entries:
153 entry.loadThumbnails(callback)
156 class YouTubeEntry():
157 def __init__(self, feed, entry, sequenceNumber, favoritesFeed = False):
158 print "[YTB] YouTubeEntry::__init__()"
161 self.sequenceNumber = sequenceNumber
162 self.favoritesFeed = favoritesFeed
166 def isPlaylistEntry(self):
170 def getYouTubeId(self):
171 print "[YTB] YouTubeEntry::getYouTubeId()"
173 if self.entry.media.player:
174 split = self.entry.media.player.url.split("=")
176 if ret == 'youtube_gdata':
178 if tmpval.endswith("&feature"):
179 tmp = tmpval.split("&")
185 print "[YTB] YouTubeEntry::getTitle()"
186 return self.entry.media.title.text
189 def getDescription(self):
190 print "[YTB] YouTubeEntry::getDescription()"
191 return self.entry.media.description.text
194 def getThumbnailUrl(self, index):
195 print "[YTB] YouTubeEntry::getThumbnailUrl"
196 if index < len(self.entry.media.thumbnail):
197 return self.entry.media.thumbnail[index].url
201 def getRelatedFeed(self):
202 print "[YTB] YouTubeEntry::getRelatedFeed()"
203 for link in self.entry.link:
204 print "Related link: ", link.rel.endswith
205 if link.rel.endswith("video.related"):
206 print "Found Related: ", link.href
210 def getResponsesFeed(self):
211 print "[YTB] YouTubeEntry::getResponseFeed()"
212 for link in self.entry.link:
213 print "Responses link: ", link.rel.endswith
214 if link.rel.endswith("video.responses"):
215 print "Found Responses: ", link.href
219 def loadThumbnail(self, index, callback):
220 print "[YTB] YouTubeEntry::loadThumbnail()"
221 thumbnailUrl = self.getThumbnailUrl(index)
222 if thumbnailUrl is not None and self.getYouTubeId() is not None:
223 thumbnailFile = "/tmp/" + self.getYouTubeId() + "_" + str(index) + ".jpg"
224 self.thumbnail[str(index)] = None
225 cookie = {"entry" : self, "file" : thumbnailFile, "callback" : callback, "index" : index}
226 downloadPage(thumbnailUrl, thumbnailFile).addCallback(fetchFinished, cookie).addErrback(fetchFailed, cookie)
229 def loadThumbnails(self, callback):
230 print "[YTB] YouTubeEntry::loadThumbnails()"
231 self.loadThumbnail(0, callback)
234 def getVideoUrl(self, fmt):
235 video_id = str(self.getYouTubeId())
236 for el_type in ['detailpage', 'embedded', 'vevo']:
237 video_info_url = ('http://www.youtube.com/get_video_info?&video_id=%s&el=%s&ps=default&eurl=&gl=DE&hl=en'% (video_id, el_type))
238 request = Request(video_info_url, None, std_headers)
240 video_info_page = urlopen(request).read()
241 video_info = parse_qs(video_info_page)
242 if 'token' in video_info:
244 except (URLError, HTTPException, error), err:
245 return None, # ('ERROR: unable to download video info webpage: %s' % str(err))
246 if 'token' not in video_info:
247 if 'reason' not in video_info:
248 reason = 'Unable to extract "t" parameter for unknown reason'
250 reason = unquote_plus(video_info['reason'][0])
251 return None #, reason
253 token = video_info['token'][0]
254 video_real_url = 'http://www.youtube.com/get_video?video_id=%s&t=%s&eurl=&el=detailpage&ps=default&gl=US&hl=en&fmt=18' % (video_id, token)
255 return video_real_url #, 'OK'
258 def getDuration(self):
259 if self.entry.media is not None and self.entry.media.duration is not None:
260 return self.entry.media.duration.seconds
261 return "not available"
264 def getRatingAverage(self):
265 if self.entry.rating is not None:
266 return self.entry.rating.average
267 return "not available"
270 def getNumRaters(self):
271 if self.entry.rating is not None:
272 return self.entry.rating.num_raters
276 def getRatingMax(self):
277 if self.entry.rating is not None:
278 return self.entry.rating.max
279 return "not available"
281 def getRatingMin(self):
282 if self.entry.rating is not None:
283 return self.entry.rating.min
284 return "not available"
287 def getFavoriteCount(self):
288 if self.entry.statistics is not None:
289 return self.entry.statistics.favorite_count
290 return "not available"
293 def getViewCount(self):
294 if self.entry.statistics is not None:
295 return self.entry.statistics.view_count
296 return "not available"
301 for author in self.entry.author:
302 authorList.append(author.name.text)
303 authors = ", ".join(authorList)
307 def getPublishedOn(self):
308 if self.entry.published is not None:
309 return self.entry.published.text
313 def getCategory(self):
314 return self.entry.GetYouTubeCategoryAsString()
318 if self.entry.media is not None and self.entry.media.keywords is not None:
319 return self.entry.media.keywords.text
320 return "not available"
323 def belongsToFavorites(self):
324 return self.favoritesFeed
327 def belongsToPlaylistId(self):
328 return self.playlistId
331 class YouTubePlaylistFeed():
332 def __init__(self, feed):
333 print "[YTB] YouTubePlayListFeed::__init__()"
340 print "[YTB] YouTubePlayListFeed::update()"
341 for entry in self.feed.entry:
342 self.entries.append(YouTubePlaylistEntry(entry))
346 print "[YTB] YouTubePlayListFeed::getTitle()"
347 return self.feed.title.text
350 def getEntries(self):
351 print "[YTB] YouTubePlayListFeed::getEntries()"
355 class YouTubePlaylistEntry():
356 def __init__(self, entry):
357 print "[YTB] YouTubePlaylistEntry::__init__()"
362 print "[YTB] YouTubePlaylistEntry::getTitle()"
363 return self.entry.title.text
366 def getDescription(self):
367 print "[YTB] YouTubePlaylistEntry::getDescription()"
368 return self.entry.description.text
371 def getFeed(self, index = 0):
372 print "[YTB] YouTubePlaylistEntry::getFeed()"
373 return self.entry.feed_link[index].href
376 def getSelfFeed(self):
377 print "[YTB] YouTubeFeed::getSelfFeed()"
378 for link in self.entry.link:
379 if link.rel == "self":
384 class YouTubePlaylistVideoFeed(YouTubeFeed):
385 def __init__(self, feed):
386 print "[YTB] YouTubePlaylistVideoFeed::__init__()"
387 YouTubeFeed.__init__(self, feed)
391 print "[YTB] YouTubePlaylistVideoFeed::update()"
393 print self.feed.entry
394 for entry in self.feed.entry:
395 self.entries.append(YouTubePlaylistVideoEntry(self, entry, sequenceNumber))
396 sequenceNumber = sequenceNumber + 1
399 print "[YTB] YouTubeFeed::getSelfFeed()"
400 for link in self.feed.link:
401 if link.rel == "feed":
406 class YouTubePlaylistVideoEntry(YouTubeEntry):
407 def __init__(self, feed, entry, sequenceNumber):
408 print "[YTB] YouTubePlaylistVideoEntry::__init__()"
409 YouTubeEntry.__init__(self, feed, entry, sequenceNumber)
412 def isPlaylistEntry(self):
417 print "[YTB] YouTubePlaylistVideoEntry::getSelfFeed()"
418 for link in self.entry.link:
419 if link.rel == "self":
424 YOUTUBE_DEVELOPER_TAG_SCHEME = "http://gdata.youtube.com/schemas/2007/developertags.cat"
425 def getCategory(self):
426 for category in self.entry.media.category:
427 if category.scheme != YouTubePlaylistVideoEntry.YOUTUBE_DEVELOPER_TAG_SCHEME:
429 return "not available"
432 class YouTubeInterface():
433 # Do not change the client_id and developer_key in the login-section!
434 # ClientId: ytapi-VolkerChristian-YouTubePlayer-pq3mrg1o-0
435 # DeveloperKey: AI39si7t0WNyg_tvjBPdRIvBfaUA_XrTY1LNzfjLgCn8A_m92YKtWTcR_auEmI5gKGitJb4SskrjxJSmRc3yhQ4YlHTBAzPSig
437 print "[YTB] YouTubeInterface::__init__()"
441 self.ytService = gdata.youtube.service.YouTubeService()
442 print "[YTB] YouTubeInterface::open()"
443 self.loggedIn = False
447 print "[YTB] YouTubeInterface::close()"
449 self.loggedIn = False
452 def login(self, user):
453 print "[YTB] YouTubeInterface::login()"
456 # http://code.google.com/apis/youtube/developers_guide_python.html#ClientLogin
457 self.ytService.email = user.getEmail()
458 self.ytService.password = user.getPassword()
459 self.ytService.source = 'my-example-application'
460 self.ytService.developer_key = "AI39si7t0WNyg_tvjBPdRIvBfaUA_XrTY1LNzfjLgCn8A_m92YKtWTcR_auEmI5gKGitJb4SskrjxJSmRc3yhQ4YlHTBAzPSig"
461 self.ytService.client_id = "ytapi-VolkerChristian-YouTubePlayer-pq3mrg1o-0"
463 self.ytService.ProgrammaticLogin()
464 except BadAuthentication:
472 def isLoggedIn(self):
476 def search(self, searchTerms, startIndex = 1, maxResults = 25,
477 orderby = "relevance", time = "all_time", racy = "include",
478 author = "", lr = "", categories = "", sortOrder = "ascending", format = "6"):
479 print "[YTB] YouTubeInterface::search()"
480 query = gdata.youtube.service.YouTubeVideoQuery()
481 query.vq = searchTerms
482 query.orderby = orderby
484 query.sortorder = sortOrder
487 if categories[0] is not None:
488 query.categories = categories
490 query.start_index = startIndex
491 query.max_results = maxResults
492 # query.format = format
494 feed = YouTubeFeed(self.ytService.YouTubeQuery(query))
500 def getFeed(self, url):
501 return YouTubeFeed(self.ytService.GetYouTubeVideoFeed(url))
504 def getUserFavoritesFeed(self, userName = "default"):
505 return YouTubeFeed(self.ytService.GetUserFavoritesFeed(userName), favoritesFeed = True)
508 def getUserPlaylistFeed(self, playlistEntry):
509 print "[YTB] getUserPlaylistFeed: ", playlistEntry.getFeed()
510 return YouTubePlaylistVideoFeed(self.ytService.GetYouTubePlaylistVideoFeed(playlistEntry.getFeed()))
513 def addToFavorites(self, entry):
514 response = self.ytService.AddVideoEntryToFavorites(entry.entry)
515 # The response, if succesfully posted is a YouTubeVideoEntry
516 if isinstance(response, gdata.youtube.YouTubeVideoEntry):
517 print "[YTB] Video successfully added to favorites"
523 def removeFromFavorites(self, entry):
524 response = self.ytService.DeleteVideoEntryFromFavorites(entry.getYouTubeId())
526 print "[YTB] Video deleted from favorites"
530 def getPlaylistFeed(self):
531 return YouTubePlaylistFeed(self.ytService.GetYouTubePlaylistFeed())
534 def addPlaylist(self, name, description, private):
536 newPlaylistEntry = self.ytService.AddPlaylist(name, description, private)
537 if isinstance(newPlaylistEntry, gdata.youtube.YouTubePlaylistEntry):
538 newPlaylist = YouTubePlaylistEntry(newPlaylistEntry)
542 def deletePlaylist(self, playlistEntry):
543 playListUrl = playlistEntry.getSelfFeed()
544 return self.ytService.DeletePlaylist(playListUrl)
547 def removeFromPlaylist(self, playlistVideoEntry):
548 print "[YTB] Removing from Playlist"
549 response = self.ytService.Delete(playlistVideoEntry.getSelf())
551 print "[YTB] Successfull deleted"
553 print "[YTB] Delete unsuccessfull"
557 def addToPlaylist(self, playlistEntry, videoEntry):
558 print "[YTB] Adding to Playlist"
559 playlistUri = playlistEntry.getFeed()
560 response = self.ytService.AddPlaylistVideoEntryToPlaylist(
561 playlistUri, videoEntry.getYouTubeId(), videoEntry.getTitle(), videoEntry.getDescription())
562 if isinstance(response, gdata.youtube.YouTubePlaylistVideoEntry):
563 print "[YTB] Video added"
569 def fetchFailed(string, cookie):
570 print "[YTB] fetchFailed(): ", string
571 if os.path.exists(cookie["file"]):
572 os.remove(cookie["file"])
573 cookie["callback"](cookie["entry"])
576 def fetchFinished(string, cookie):
577 print "[YTB] fetchFinished(): ", string
578 if os.path.exists(cookie["file"]):
579 print "Loading filename %s" % cookie["file"]
580 cookie["entry"].thumbnail[str(cookie["index"])] = LoadPixmap(cookie["file"])
581 os.remove(cookie["file"])
582 cookie["callback"](cookie["entry"])
585 interface = YouTubeInterface()