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 def getVideoUrl(self, youTubeEntry, fmt):
565 mrl = youTubeEntry.getVideoUrl(fmt)
567 self.session.open(MessageBox, _("Could not retrive video url:\n%s") % e, MessageBox.TYPE_ERROR)
571 # http://cacan.blog385.com/index.php/2008/05/09/youtube-high-quality-hacks/
572 # add the &fmt=6 onto the end:
574 # http://youtube.com/watch?v=CQzUsTFqtW0&fmt=6
576 # If the YouTube video just sits there loading then that
577 # is a sign that the video has not been converted to the
578 # higher resolution yet. To really see the difference you
579 # should view the video in full screen mode by clicking
580 # the button in the bottom-right corner of the player.
582 # Note: Alternatively you can add &fmt=18 and it will play
583 # the high-resolution version when available, otherwise it
584 # will play the regular version. Here?s a Greasemonkey
585 # script that will automatically add &fmt=18 onto the end
586 # of each YouTube URL.
589 youTubeEntry = self["list"].getCurrent()[0]
590 mrl = self.getVideoUrl(youTubeEntry, "&fmt=18")
591 ############## To be resolved
592 #Traceback (most recent call last):
593 # File "/usr/lib/enigma2/python/Components/ActionMap.py", line 46, in action
594 # res = self.actions[action]()
595 # File "/usr/lib/enigma2/python/Plugins/Extensions/YouTubePlayer/YouTubeList.py", line 425, in tryToPlay
597 # File "/usr/lib/enigma2/python/Plugins/Extensions/YouTubePlayer/YouTubeList.py", line 543, in play
598 # mrl = youTubeEntry.getVideoUrl("&fmt=18")
599 # File "/usr/lib/enigma2/python/Plugins/Extensions/YouTubePlayer/YouTubeInterface.py", line 216, in getVideoUrl
600 # conn.request("GET", "/v/" + quote(self.getYouTubeId()))
601 # File "/usr/lib/python2.5/httplib.py", line 862, in request
602 # self._send_request(method, url, body, headers)
603 # File "/usr/lib/python2.5/httplib.py", line 885, in _send_request
605 # File "/usr/lib/python2.5/httplib.py", line 856, in endheaders
606 # self._send_output()
607 # File "/usr/lib/python2.5/httplib.py", line 728, in _send_output
609 # File "/usr/lib/python2.5/httplib.py", line 695, in send
611 # File "/usr/lib/python2.5/httplib.py", line 663, in connect
612 # socket.SOCK_STREAM):
613 #socket.gaierror: (2, 'temporary failure in name resolution.')
617 entries.append((_("Show video detail info"), [self.showVideoInfo, False]))
618 if self["list"].getCurrent()[0].belongsToFavorites():
619 entries.append((_("Remove from favorites"), [self.removeFromFavorites, False]))
621 entries.append((_("Add to favorites"), [self.addToFavorites, False]))
623 if self["list"].getCurrent()[0].isPlaylistEntry():
624 entries.append((_("Remove from playlist"), [self.removeFromPlaylist, False]))
626 entries.append((_("Add to playlist"), [self.addToPlaylist, False]))
627 entries.append((_("Get related videos"), [self.getRelated, True]))
628 entries.append((_("Get video responses"), [self.getResponses, True]))
630 self.currentServer.play(self.session, mrl, youTubeEntry.getTitle(), self,
631 player = boundFunction(YouTubePlayer, contextMenuEntries = entries, infoCallback = self.showVideoInfo, name = self["list"].getCurrent()[0].getTitle()))
633 print "[YTB] No valid flv-mrl found"
636 def getNextFile(self):
637 i = self["list"].getSelectedIndex() + 1
638 if i < len(self.list):
639 self["list"].moveToIndex(i)
640 youTubeEntry = self["list"].getCurrent()[0]
641 return self.getVideoUrl(youTubeEntry, "&fmt=18"), youTubeEntry.getTitle()
645 def getPrevFile(self):
646 i = self["list"].getSelectedIndex() - 1
648 self["list"].moveToIndex(i)
649 youTubeEntry = self["list"].getCurrent()[0]
650 return self.getVideoUrl(youTubeEntry, "&fmt=18"), youTubeEntry.getTitle()
654 def openContextMenu(self):
655 contextMenuList = YouTubeEntryContextMenuList()
656 contextMenuList.appendEntry((_("Show video detail info"), self.showVideoInfo))
657 if self["list"].getCurrent()[0].belongsToFavorites():
658 contextMenuList.appendEntry((_("Remove from favorites"), self.removeFromFavorites))
660 contextMenuList.appendEntry((_("Add to favorites"), self.addToFavorites))
661 if self["list"].getCurrent()[0].isPlaylistEntry():
662 contextMenuList.appendEntry((_("Remove from playlist"), self.removeFromPlaylist))
664 contextMenuList.appendEntry((_("Add to playlist"), self.addToPlaylist))
665 contextMenuList.appendEntry((_("Get related videos"), self.getRelated))
666 contextMenuList.appendEntry((_("Get video responses"), self.getResponses))
667 self.session.openWithCallback(self.menuActionCoosen, YouTubeEntryContextMenu, contextMenuList, self["list"].getCurrent()[0].getTitle())
670 def menuActionCoosen(self, function):
671 if function is not None:
675 def searchAgain(self):
676 Screen.close(self, True)
680 Screen.close(self, False)