1 ############################################################################
2 # Copyright (C) 2008 by Volker Christian #
3 # Volker.Christian@fh-hagenberg.at #
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. #
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. #
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 ############################################################################
21 from YouTubeInterface import interface
23 from Components.ActionMap import ActionMap
24 from Components.MenuList import MenuList
25 from Components.Label import Label
26 from Components.ScrollLabel import ScrollLabel
27 from Components.Pixmap import Pixmap
28 from Components.ProgressBar import ProgressBar
30 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
31 from Tools.Directories import resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
32 from enigma import eListboxPythonMultiContent, gFont, RT_HALIGN_LEFT, RT_VALIGN_TOP, RT_WRAP
33 from enigma import eTimer
35 from Tools.NumericalTextInput import NumericalTextInput
37 from Screens.Screen import Screen
38 from Screens.MessageBox import MessageBox
40 from Components.config import config
42 from Plugins.Extensions.VlcPlayer.VlcServerConfig import vlcServerConfig
43 from Plugins.Extensions.VlcPlayer.VlcServer import VlcServer
44 from Plugins.Extensions.VlcPlayer.VlcServerList import VlcServerListScreen
46 from YouTubeContextMenu import YouTubeEntryContextMenu, YouTubeEntryContextMenuList
48 from Tools.BoundFunction import boundFunction
50 from YouTubePlayer import YouTubePlayer
52 from YouTubeUserConfig import youTubeUserConfig
53 from YouTubeUserList import YouTubeUserListScreen
54 from YouTubePlayList import YouTubePlaylistScreen
58 def YouTubeEntryComponent(entry):
61 res.append(MultiContentEntryText(pos = (150, 5), size = (370, 42), font = 0, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = entry.getTitle()))
62 res.append(MultiContentEntryText(pos = (150, 46), size = (370, 56), font = 1, color = 0xFFA323, color_sel = 0xFFA323, flags = RT_HALIGN_LEFT | RT_VALIGN_TOP| RT_WRAP, text = entry.getDescription()))
64 if entry.thumbnail["0"] is None:
65 png = LoadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/plugin.png"))
67 png = entry.thumbnail["0"]
68 res.append(MultiContentEntryPixmapAlphaTest(pos = (10, 5), size = (130, 97), png = png))
73 class YouTubeVideoDetailsScreen(Screen):
74 def __init__(self, session, entry):
75 Screen.__init__(self, session)
77 self["video_description"] = ScrollLabel(entry.getDescription())
78 durationInSecs = int(entry.getDuration())
79 mins = int(durationInSecs / 60)
80 secs = durationInSecs - mins * 60
81 duration = "%d:%02d" % (mins, secs)
83 self["label_video_duration"] = Label(_("Duration") + ":")
84 self["video_duration"] = Label(duration)
86 self["label_video_rating_average"] = Label(_("Rate") + ":")
87 self["starsbg"] = Pixmap()
88 self["stars"] = ProgressBar()
90 self["label_video_numraters"] = Label(_("Ratings") + ":")
91 self["video_numraters"] = Label(entry.getNumRaters())
93 self["label_video_statistics_favorite_count"] = Label(_("Favorited") + ":")
94 self["video_statistics_favorite_count"] = Label(entry.getFavoriteCount())
96 self["label_video_statistics_view_count"] = Label(_("Views") + ":")
97 self["video_statistics_view_count"] = Label(entry.getViewCount())
99 self["label_video_author"] = Label(_("Author:") + ":")
100 self["video_author"] = Label(entry.getAuthor())
102 self["label_video_published_on"] = Label(_("Added") + ":")
103 self["video_published_on"] = Label(entry.getPublishedOn().split("T")[0])
105 self["label_video_category"] = Label(_("Category") + ":")
106 self["video_category"] = Label(entry.getCategory())
107 self["label_video_tags"] = Label(_("Tags") + ":")
108 self["video_tags"] = Label(entry.getTags())
110 self["video_thumbnail_1"] = Pixmap()
111 self["video_thumbnail_2"] = Pixmap()
112 self["video_thumbnail_3"] = Pixmap()
114 self["actions"] = ActionMap(["YouTubeVideoDetailsScreenActions"],
117 "cancel" : self.close,
119 "down" : self.pageDown,
120 "left" : self.pageUp,
121 "right" : self.pageDown
124 self.onFirstExecBegin.append(self.setPixmap)
125 self.onFirstExecBegin.append(self.setInitialize)
129 self["video_description"].pageUp()
133 self["video_description"].pageDown()
136 def setInitialize(self):
137 Screen.setTitle(self, self.entry.getTitle())
138 if self.entry.getRatingAverage() != "not available":
139 ratingStars = int(round(20 * float(self.entry.getRatingAverage()), 0))
140 print "[YTB] Rating: ", ratingStars, " ", self["stars"].getRange()
141 self["stars"].setValue(ratingStars)
144 self["starsbg"].hide()
148 self["video_thumbnail_1"].instance.setPixmap(self.entry.thumbnail["0"].__deref__())
149 self.entry.loadThumbnail(1, self.setThumbnail_2)
150 self.entry.loadThumbnail(2, self.setThumbnail_3)
151 self["video_thumbnail_2"].hide()
152 self["video_thumbnail_3"].hide()
155 def setThumbnail_2(self, entry):
156 self["video_thumbnail_2"].instance.setPixmap(self.entry.thumbnail["1"].__deref__())
157 self["video_thumbnail_2"].show()
160 def setThumbnail_3(self, entry):
161 self["video_thumbnail_3"].instance.setPixmap(self.entry.thumbnail["2"].__deref__())
162 self["video_thumbnail_3"].show()
165 class PatientMessageBox(MessageBox):
166 def __init__(self, session, text, type = 1, timeout = -1, close_on_any_key = False, default = True):
167 MessageBox.__init__(self, session, text, type, timeout, close_on_any_key, default)
168 self.skinName = "MessageBox"
171 def processDelayed(self, function):
172 self.delay_timer = eTimer()
173 self.delay_timer.callback.append(self.processDelay)
174 self.delay_timer.start(0, 1)
175 self.function = function
178 def processDelay(self):
194 class YouTubeList(MenuList):
195 def __init__(self, list, enableWrapAround = False):
196 MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
197 self.l.setFont(0, gFont("Regular", 18))
198 self.l.setFont(1, gFont("Regular", 14))
199 self.l.setItemHeight(105)
202 class YouTubeListScreen(Screen, NumericalTextInput):
203 def __init__(self, session):
204 Screen.__init__(self, session)
205 NumericalTextInput.__init__(self)
207 self.session = session
208 self.serverName = config.plugins.youtubeplayer.serverprofile.value
209 self.currentServer = vlcServerConfig.getServerByName(self.serverName)
211 self["red"] = Label(_("Select a VLC-Server"))
212 self["green"] = Label(_("New YouTube search"))
215 self["list"] = YouTubeList(self.list)
217 self["label_total_results"] = Label(_("Total results") + ":")
218 self["total_results"] = Label("")
220 self["label_currently_shown"] = Label(_("Shown") + ":")
221 self["currently_shown"] = Label("")
224 self.historyIndex = 0
226 self.isFavoritesFeed = False
228 self.patientDialog = None
230 self["actions"] = ActionMap(["YouTubeVideoListActions"],
232 "play" : self.tryToPlay,
233 "select" : self.justSelectServer,
234 "search" : self.searchAgain,
235 "menu" : self.openContextMenu,
236 "forward" : self.forwardInHistory,
237 "backward" : self.backInHistory,
238 "left" : self.keyLeft,
239 "right" : self.keyRight,
241 "down" : self.keyDown,
242 "info" : self.showVideoInfo,
243 "cancel" : self.close
248 self["list"].pageUp()
252 if self["list"].getSelectionIndex() == len(self.list) - 1 and self.feed.getNextFeed() is not None:
253 dlg = self.session.openWithCallback(self.loadNextFeed, MessageBox, _("Load further entries of current Feed?"))
255 self["list"].pageDown()
263 if self["list"].getSelectionIndex() == len(self.list) - 1 and self.feed.getNextFeed() is not None:
264 dlg = self.session.openWithCallback(self.loadNextFeed, MessageBox, _("Load further entries of current Feed?"))
269 def insertEntry(self, entry):
270 print "[YTB] YouTubeTest::updateFinished()"
271 self.list.append(YouTubeEntryComponent(entry))
272 self.list.sort(cmp = lambda x, y : cmp(x[0].sequenceNumber, y[0].sequenceNumber))
273 currentlyShown = "%d" % len(self.list)
274 self["currently_shown"].setText(currentlyShown)
275 self["list"].setList(self.list)
278 def closePatientDialogDelayed(self):
279 if self.patientDialog:
280 self.patientDialog.close()
281 self.patientDialog = None
282 self["list"].setList(self.list)
285 def showFeed(self, feed, append):
288 self.setTitle(feed.getTitle())
289 self["total_results"].setText(feed.getTotalResults())
292 self["list"].setList(self.list)
293 self.feed.loadThumbnails(self.insertEntry)
294 self.delay_timer = eTimer()
295 self.delay_timer.callback.append(self.closePatientDialogDelayed)
296 self.delay_timer.start(100, 1)
299 def addToHistory(self, feed):
301 del self.history[self.historyIndex : len(self.history)]
302 self.history.insert(self.historyIndex, feed.getSelfFeed())
303 self.historyIndex = self.historyIndex + 1
306 def searchFeedReal(self, searchContext):
307 print "[YTB] searchFeedReal"
309 feed = interface.search(searchContext.searchTerm.value)
312 self.session.open(MessageBox, _("Error querying feed for search term %s:\n%s" %
313 (searchContext.searchTerm.value, e)), MessageBox.TYPE_ERROR)
314 self.showFeed(feed, False)
315 self.addToHistory(feed)
318 def searchFeed(self, searchContext):
319 self.patientDialog = self.session.open(PatientMessageBox, _("Searching, be patient ..."))
320 self.patientDialog.processDelayed(boundFunction(self.searchFeedReal, searchContext = searchContext))
321 self.isFavoritesFeed = False
324 def loadPlaylistFeedReal(self, playlist):
326 feed = interface.getUserPlaylistFeed(playlist)
329 self.session.open(MessageBox, _("Error querying playlist-feed for playlist %s:\n%s" %
330 (playlist.getTitle(), e)), MessageBox.TYPE_ERROR)
331 self.showFeed(feed, False)
332 self.addToHistory(feed)
335 def loadPlaylistFeed(self, playlist):
336 self.patientDialog = self.session.open(PatientMessageBox, _("Loading playlist, be patient ..."))
337 self.patientDialog.processDelayed(boundFunction(self.loadPlaylistFeedReal, playlist = playlist))
340 def loadFavoritesFeedReal(self, userName = "default"):
342 feed = interface.getUserFavoritesFeed(userName)
345 self.session.open(MessageBox, _("Error querying favorites feed:\n%s" %
346 e), MessageBox.TYPE_ERROR)
347 self.showFeed(feed, False)
348 self.addToHistory(feed)
351 def loadFavoritesFeed(self, userName = "default"):
352 self.patientDialog = self.session.open(PatientMessageBox, _("Loading favorits, be patient ..."))
353 self.patientDialog.processDelayed(boundFunction(self.loadFavoritesFeedReal, userName = userName))
354 self.isFavoritesFeed = True
357 def loadStandardFeed(self, url):
358 self.loadFeed(_("Loading standard feed, be patient ..."), url, "standard feed")
361 def loadFeedReal(self, feedUrl, feedName, append = False, addToHistory = True):
363 feed = interface.getFeed(feedUrl)
366 self.session.open(MessageBox, _("Error querying feed %s:\n%s" %
367 (feedName, e)), MessageBox.TYPE_ERROR)
368 self.showFeed(feed, append)
370 self.addToHistory(feed)
373 def loadFeed(self, text, feedUrl, feedName, append = False, addToHistory = True):
374 self.patientDialog = self.session.open(PatientMessageBox, text)
375 self.patientDialog.processDelayed(boundFunction(self.loadFeedReal, feedName = feedName,
376 feedUrl = feedUrl, append = append, addToHistory = addToHistory))
379 def loadPreviousFeed(self, result):
382 prevUrl = self.feed.getPreviousFeed()
383 if prevUrl is not None:
384 self.loadFeed(_("Loading additional videos, be patient ..."), prevUrl, _("additional videos"),
388 def loadNextFeed(self, result):
391 nextUrl = self.feed.getNextFeed()
392 if nextUrl is not None:
393 self.loadFeed(_("Loading additional videos, be patient ..."), nextUrl, _("additional videos"),
397 def getRelated(self):
398 self.loadFeed(_("Loading related videos, be patient ..."), self["list"].getCurrent()[0].getRelatedFeed(), _("related videos"), False, True)
399 self.isFavoritesFeed = False
402 def getResponses(self):
403 self.loadFeed(_("Loading response videos, be patient ..."), self["list"].getCurrent()[0].getResponsesFeed(), _("response videos"), False, True)
404 self.isFavoritesFeed = False
407 def backInHistory(self):
408 if self.historyIndex > 1:
409 self.historyIndex = self.historyIndex - 1
410 self.loadFeed(_("Back in history, be patient ..."), self.history[self.historyIndex - 1], _("back in history"), False, False)
413 def forwardInHistory(self):
414 if self.historyIndex < len(self.history):
415 self.historyIndex = self.historyIndex + 1
416 self.loadFeed(_("Forward in history, be patient ..."), self.history[self.historyIndex - 1], _("forward in history"), False, False)
419 def showVideoInfo(self):
420 self.session.open(YouTubeVideoDetailsScreen, self["list"].getCurrent()[0])
423 def justSelectServer(self):
424 defaultServer = vlcServerConfig.getServerByName(config.plugins.youtubeplayer.serverprofile.value)
425 self.selectServer(self.serverSelectedCB, defaultServer)
428 def selectServer(self, callback, currentServer):
429 self.session.openWithCallback(callback, VlcServerListScreen, currentServer)
432 def serverSelectedCB(self, selectedServer, defaultServer):
433 if selectedServer is not None:
434 self.currentServer = selectedServer
435 elif defaultServer is not None:
436 self.currentServer = defaultServer
437 if defaultServer is not None:
438 config.plugins.youtubeplayer.serverprofile.value = defaultServer.getName()
439 config.plugins.youtubeplayer.serverprofile.save()
442 def selectAndPlayCB(self, selectedServer, defaultServer):
443 self.serverSelectedCB(selectedServer, defaultServer)
448 if self.currentServer is not None:
451 self.selectServer(self.selectAndPlayCB, None)
454 def login(self, callback):
455 self.session.openWithCallback(callback, YouTubeUserListScreen, youTubeUserConfig.getDefaultUser())
458 def addToFavoritesReal(self):
460 interface.addToFavorites(self["list"].getCurrent()[0])
462 self.session.open(MessageBox, _("Error adding video to favorites:\n%s" %
463 e), MessageBox.TYPE_ERROR)
466 def addToFavoritesLogin(self, loginState):
467 if loginState == YouTubeUserListScreen.LOGIN_SUCCESS:
468 self.addToFavoritesReal()
469 elif loginState == YouTubeUserListScreen.LOGIN_FAILED:
470 self.session.open(MessageBox, _("Login not successful"), MessageBox.TYPE_INFO)
475 def addToFavorites(self):
476 if not interface.isLoggedIn():
477 self.login(self.addToFavoritesLogin)
479 self.addToFavoritesReal()
482 def removeFromFavoritesReal(self):
484 if interface.removeFromFavorites(self["list"].getCurrent()[0]):
485 self.list.remove(self["list"].getCurrent())
486 self["list"].setList(self.list)
488 self.session.open(MessageBox, _("Error removing video from favorites:\n%s" %
489 e), MessageBox.TYPE_ERROR)
492 def removeFromFavoritesLogin(self, loginState):
493 if loginState == YouTubeUserListScreen.LOGIN_SUCCESS:
494 self.removeFromFavoritesReal()
495 elif loginState == YouTubeUserListScreen.LOGIN_FAILED:
496 self.session.open(MessageBox, _("Login not successful"), MessageBox.TYPE_INFO)
501 def removeFromFavorites(self):
502 if not interface.isLoggedIn():
503 self.login(self.removeFromFavoritesLogin)
505 self.removeFromFavoritesReal()
508 def removeFromPlaylistReal(self):
510 if interface.removeFromPlaylist(self["list"].getCurrent()[0]):
511 self.list.remove(self["list"].getCurrent())
512 self["list"].setList(self.list)
514 self.session.open(MessageBox, _("Error removing video from playlist:\n%s" %
515 e), MessageBox.TYPE_ERROR)
518 def removeFromPlaylistLogin(self, loginState):
519 if loginState == YouTubeUserListScreen.LOGIN_SUCCESS:
520 self.removeFromPlaylistReal()
521 elif loginState == YouTubeUserListScreen.LOGIN_FAILED:
522 self.session.open(MessageBox, _("Login not successful"), MessageBox.TYPE_INFO)
527 def removeFromPlaylist(self):
528 if not interface.isLoggedIn():
529 self.login(self.removeFromPlaylistLogin)
531 self.removeFromPlaylistReal()
534 def playlistChoosen(self, playlist):
535 if playlist is not None:
537 interface.addToPlaylist(playlist, self["list"].getCurrent()[0])
539 self.session.open(MessageBox, _("Error adding video to playlist:\n%s" %
540 e), MessageBox.TYPE_ERROR)
543 def addToPlaylistReal(self):
544 dlg = self.session.openWithCallback(self.playlistChoosen, YouTubePlaylistScreen)
548 def addToPlaylistLogin(self, loginState):
549 if loginState == YouTubeUserListScreen.LOGIN_SUCCESS:
550 self.addToPlaylistReal()
551 elif loginState == YouTubeUserListScreen.LOGIN_FAILED:
552 self.session.open(MessageBox, _("Login not successful"), MessageBox.TYPE_INFO)
557 def addToPlaylist(self):
558 if not interface.isLoggedIn():
559 self.login(self.addToPlaylistLogin)
561 self.addToPlaylistReal()
564 # http://cacan.blog385.com/index.php/2008/05/09/youtube-high-quality-hacks/
565 # add the &fmt=6 onto the end:
567 # http://youtube.com/watch?v=CQzUsTFqtW0&fmt=6
569 # If the YouTube video just sits there loading then that
570 # is a sign that the video has not been converted to the
571 # higher resolution yet. To really see the difference you
572 # should view the video in full screen mode by clicking
573 # the button in the bottom-right corner of the player.
575 # Note: Alternatively you can add &fmt=18 and it will play
576 # the high-resolution version when available, otherwise it
577 # will play the regular version. Here?s a Greasemonkey
578 # script that will automatically add &fmt=18 onto the end
579 # of each YouTube URL.
582 youTubeEntry = self["list"].getCurrent()[0]
583 mrl = youTubeEntry.getVideoUrl("&fmt=18")
584 ############## To be resolved
585 #Traceback (most recent call last):
586 # File "/usr/lib/enigma2/python/Components/ActionMap.py", line 46, in action
587 # res = self.actions[action]()
588 # File "/usr/lib/enigma2/python/Plugins/Extensions/YouTubePlayer/YouTubeList.py", line 425, in tryToPlay
590 # File "/usr/lib/enigma2/python/Plugins/Extensions/YouTubePlayer/YouTubeList.py", line 543, in play
591 # mrl = youTubeEntry.getVideoUrl("&fmt=18")
592 # File "/usr/lib/enigma2/python/Plugins/Extensions/YouTubePlayer/YouTubeInterface.py", line 216, in getVideoUrl
593 # conn.request("GET", "/v/" + quote(self.getYouTubeId()))
594 # File "/usr/lib/python2.5/httplib.py", line 862, in request
595 # self._send_request(method, url, body, headers)
596 # File "/usr/lib/python2.5/httplib.py", line 885, in _send_request
598 # File "/usr/lib/python2.5/httplib.py", line 856, in endheaders
599 # self._send_output()
600 # File "/usr/lib/python2.5/httplib.py", line 728, in _send_output
602 # File "/usr/lib/python2.5/httplib.py", line 695, in send
604 # File "/usr/lib/python2.5/httplib.py", line 663, in connect
605 # socket.SOCK_STREAM):
606 #socket.gaierror: (2, 'temporary failure in name resolution.')
610 entries.append((_("Show video detail info"), [self.showVideoInfo, False]))
611 if self["list"].getCurrent()[0].belongsToFavorites():
612 entries.append((_("Remove from favorites"), [self.removeFromFavorites, False]))
614 entries.append((_("Add to favorites"), [self.addToFavorites, False]))
616 if self["list"].getCurrent()[0].isPlaylistEntry():
617 entries.append((_("Remove from playlist"), [self.removeFromPlaylist, False]))
619 entries.append((_("Add to playlist"), [self.addToPlaylist, False]))
620 entries.append((_("Get related videos"), [self.getRelated, True]))
621 entries.append((_("Get video responses"), [self.getResponses, True]))
623 self.currentServer.play(self.session, mrl, youTubeEntry.getTitle(), self,
624 player = boundFunction(YouTubePlayer, contextMenuEntries = entries, infoCallback = self.showVideoInfo, name = self["list"].getCurrent()[0].getTitle()))
626 print "[YTB] No valid flv-mrl found"
629 def getNextFile(self):
630 i = self["list"].getSelectedIndex() + 1
631 if i < len(self.list):
632 self["list"].moveToIndex(i)
633 youTubeEntry = self["list"].getCurrent()[0]
634 return youTubeEntry.getVideoUrl("&fmt=18"), youTubeEntry.getTitle()
638 def getPrevFile(self):
639 i = self["list"].getSelectedIndex() - 1
641 self["list"].moveToIndex(i)
642 youTubeEntry = self["list"].getCurrent()[0]
643 return youTubeEntry.getVideoUrl("&fmt=18"), youTubeEntry.getTitle()
647 def openContextMenu(self):
648 contextMenuList = YouTubeEntryContextMenuList()
649 contextMenuList.appendEntry((_("Show video detail info"), self.showVideoInfo))
650 if self["list"].getCurrent()[0].belongsToFavorites():
651 contextMenuList.appendEntry((_("Remove from favorites"), self.removeFromFavorites))
653 contextMenuList.appendEntry((_("Add to favorites"), self.addToFavorites))
654 if self["list"].getCurrent()[0].isPlaylistEntry():
655 contextMenuList.appendEntry((_("Remove from playlist"), self.removeFromPlaylist))
657 contextMenuList.appendEntry((_("Add to playlist"), self.addToPlaylist))
658 contextMenuList.appendEntry((_("Get related videos"), self.getRelated))
659 contextMenuList.appendEntry((_("Get video responses"), self.getResponses))
660 self.session.openWithCallback(self.menuActionCoosen, YouTubeEntryContextMenu, contextMenuList, self["list"].getCurrent()[0].getTitle())
663 def menuActionCoosen(self, function):
664 if function is not None:
668 def searchAgain(self):
669 Screen.close(self, True)
673 Screen.close(self, False)