import os from twisted.web.client import downloadPage from enigma import ePicLoad, eServiceReference, eTimer, getDesktop from Components.ActionMap import ActionMap from Components.AVSwitch import AVSwitch from Components.config import config, ConfigDirectory, ConfigSelection, \ ConfigSubsection, ConfigText, ConfigYesNo, getConfigListEntry from Components.ConfigList import ConfigListScreen from Components.Label import Label from Components.Pixmap import Pixmap from Components.ScrollLabel import ScrollLabel from Components.Sources.List import List from Components.Sources.StaticText import StaticText from Components.Task import job_manager from Plugins.Plugin import PluginDescriptor from Screens.ChoiceBox import ChoiceBox from Screens.InfoBar import InfoBar, MoviePlayer from Screens.MessageBox import MessageBox from Screens.Screen import Screen from Tools.BoundFunction import boundFunction from Tools.Directories import resolveFilename, SCOPE_HDD, SCOPE_PLUGINS from Tools.LoadPixmap import LoadPixmap from . import _ config.plugins.YouTube = ConfigSubsection() config.plugins.YouTube.saveHistory = ConfigYesNo(default = True) config.plugins.YouTube.searchResult = ConfigSelection( [('4', '4'), ('8', '8'), ('16', '16'), ('24', '24'), ('50', '50') ], '24') config.plugins.YouTube.searchRegion = ConfigSelection( [('', _('All')), ('AU', _('Australia')), ('BR', _('Brazil')), ('CA', _('Canada')), ('CZ', _('Czech Republic')), ('FR', _('France')), ('DE', _('Germany')), ('GB', _('Great Britain')), ('NL', _('Holland')), ('HK', _('Hong Kong')), ('IN', _('India')), ('IE', _('Ireland')), ('IL', _('Israel')), ('IT', _('Italy')), ('JP', _('Japan')), ('LV', _('Latvia')), ('MX', _('Mexico')), ('NZ', _('New Zealand')), ('PL', _('Poland')), ('RU', _('Russia')), ('KR', _('South Korea')), ('ES', _('Spain')), ('SE', _('Sweden')), ('TW', _('Taiwan')), ('TH', _('Thailand')), ('US', _('United States')) ], '') config.plugins.YouTube.searchLanguage = ConfigSelection( [('', _('All')), ('au', _('Australia')), ('br', _('Brazil')), ('ca', _('Canada')), ('cz', _('Czech Republic')), ('fr', _('France')), ('de', _('Germany')), ('gb', _('Great Britain')), ('nl', _('Holland')), ('hk', _('Hong Kong')), ('in', _('India')), ('ie', _('Ireland')), ('il', _('Israel')), ('it', _('Italy')), ('jp', _('Japan')), ('lv', _('Latvia')), ('mx', _('Mexico')), ('nz', _('New Zealand')), ('pl', _('Poland')), ('ru', _('Russia')), ('kr', _('South Korea')), ('es', _('Spain')), ('se', _('Sweden')), ('tw', _('Taiwan')), ('th', _('Thailand')), ('us', _('United States')) ], '') config.plugins.YouTube.searchOrder = ConfigSelection( [('relevance', _('Relevance')), ('date', _('Created date')), ('rating', _('Rating')), ('title', _('Title')), ('viewCount', _('View count')) ], 'relevance') config.plugins.YouTube.safeSearch = ConfigSelection(default = 'moderate', choices = [ ('moderate', _('Moderate')), ('none', _('No')), ('strict', _('Yes'))]) config.plugins.YouTube.maxResolution = ConfigSelection(default = '22', choices = [ ('38', '3072p'), ('37', '1080p'), ('22', '720p'), ('35', '480p'), ('18', '360p'), ('5', '240p'), ('17', '144p')]) config.plugins.YouTube.onMovieEof = ConfigSelection(default = 'quit', choices = [ ('quit', _('Return to list')), ('ask', _('Ask user')), ('playnext', _('Play next')), ('repeat', _('Repeat'))]) config.plugins.YouTube.onMovieStop = ConfigSelection(default = 'ask', choices = [ ('ask', _('Ask user')), ('quit', _('Return to list'))]) config.plugins.YouTube.login = ConfigYesNo(default = False) config.plugins.YouTube.downloadDir = ConfigDirectory(default=resolveFilename(SCOPE_HDD)) config.plugins.YouTube.searchHistory = ConfigText(default='') config.plugins.YouTube.refreshToken = ConfigText(default='') API_KEY = 'AIzaSyCyIlbb0FIwoieEZ9RTShMVkRMisu-ZX0k' YOUTUBE_API_CLIENT_ID = '411447027255-vbgs05u1o3m8mpjs2vcd04afrg60drba.apps.googleusercontent.com' YOUTUBE_API_CLIENT_SECRET = 'fYE-8T3qf4DrLPLv3NTgvjna' class YouTubePlayer(MoviePlayer): def __init__(self, session, service, current): MoviePlayer.__init__(self, session, service) self.skinName = 'MoviePlayer' self.current = current self.servicelist = InfoBar.instance and InfoBar.instance.servicelist def leavePlayer(self): if config.plugins.YouTube.onMovieStop.value == 'ask': title = _('Stop playing this movie?') list = ( (_('Yes'), 'quit'), (_('Yes, but play next video'), 'playnext'), (_('Yes, but play previous video'), 'playprev'), (_('No, but play video again'), 'repeat'), (_('No'), 'continue') ) self.session.openWithCallback(self.leavePlayerConfirmed, ChoiceBox, title = title, list = list) else: self.close() def leavePlayerConfirmed(self, answer): if answer and answer[1] != 'continue': self.close(answer) def doEofInternal(self, playing): self.close([None, config.plugins.YouTube.onMovieEof.value]) def getPluginList(self): from Components.PluginComponent import plugins list = [] for p in plugins.getPlugins(where = PluginDescriptor.WHERE_EXTENSIONSMENU): if p.name != _('YouTube'): list.append(((boundFunction(self.getPluginName, p.name), boundFunction(self.runPlugin, p), lambda: True), None)) return list def openCurEventView(self): self.session.open(YouTubeInfo, current = self.current) def showSecondInfoBar(self): self.hide() self.hideTimer.stop() self.openCurEventView() def showMovies(self): pass def openServiceList(self): if hasattr(self, "toggleShow"): self.toggleShow() class YouTubeMain(Screen): screenWidth = getDesktop(0).size().width() if screenWidth and screenWidth == 1280: skin = """ {"template": [ MultiContentEntryPixmapAlphaTest(pos=(0,0), \ size=(100,72), png=2), # Thumbnail MultiContentEntryText(pos=(110,1), size=(575,52), \ font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER|RT_WRAP, text=3), # Title MultiContentEntryText(pos=(120, 50), size=(200,22), \ font=1, flags=RT_HALIGN_LEFT, text=4), # Views MultiContentEntryText(pos=(360,50), size=(200,22), \ font=1, flags=RT_HALIGN_LEFT, text=5), # Duration ], "fonts": [gFont("Regular",20), gFont("Regular",16)], "itemHeight": 72} # Thumbnail size in list """ elif screenWidth and screenWidth == 1920: skin = """ {"template": [ MultiContentEntryPixmapAlphaTest(pos=(0,0), \ size=(150,108), png=2), # Thumbnail MultiContentEntryText(pos=(165,1), size=(862,78), \ font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER|RT_WRAP, text=3), # Title MultiContentEntryText(pos=(180, 75), size=(300,33), \ font=1, flags=RT_HALIGN_LEFT, text=4), # Views MultiContentEntryText(pos=(540,75), size=(300,33), \ font=1, flags=RT_HALIGN_LEFT, text=5), # Duration ], "fonts": [gFont("Regular",30), gFont("Regular",24)], "itemHeight": 108} # Thumbnail size in list """ else: skin = """ {"template": [ MultiContentEntryPixmapAlphaTest(pos=(0,0), \ size=(100,72), png=2), # Thumbnail MultiContentEntryText(pos=(110,1), size=(475,52), \ font=0, flags=RT_HALIGN_LEFT|RT_VALIGN_CENTER|RT_WRAP, text=3), # Title MultiContentEntryText(pos=(120, 50), size=(200,22), \ font=1, flags=RT_HALIGN_LEFT, text=4), # Views MultiContentEntryText(pos=(360,50), size=(200,22), \ font=1, flags=RT_HALIGN_RIGHT, text=5), # Duration ], "fonts": [gFont("Regular",20), gFont("Regular",16)], "itemHeight": 72} # Thumbnail size in list """ def __init__(self, session): Screen.__init__(self, session) self.setTitle(_('YouTube')) self['info'] = Pixmap() self['info'].hide() self['red'] = Pixmap() self['red'].hide() self['green'] = Pixmap() self['green'].hide() self['menu'] = Pixmap() self['menu'].hide() self['key_red'] = StaticText('') self['key_green'] = StaticText('') self['actions'] = ActionMap(['WizardActions', 'ColorActions', 'MovieSelectionActions'], { 'back': self.cancel, 'ok': self.ok, 'red': self.cancel, 'green': self.ok, 'up': self.selectPrevious, 'down': self.selectNext, 'contextMenu': self.openMenu, 'showEventInfo': self.showEventInfo }, -2) text = _('YouTube starting. Please wait...') self['text'] = Label() self['text'].setText(text) self['list'] = List([]) self['thumbnail'] = Pixmap() self['thumbnail'].hide() self.splitTaimer = eTimer() self.splitTaimer.timeout.callback.append(self.splitTaimerStop) self.thumbnailTaimer = eTimer() self.thumbnailTaimer.timeout.callback.append(self.updateThumbnails) self.picloads = {} self.thumbnails = {} self.sc = AVSwitch().getFramebufferScale() self.list = 'main' self.action = 'startup' self.value = [None, None, ''] self.prevIndex = [] self.prevEntryList = [] self.entryList = [] self.ytdl = None self.youtube = None self.nextPageToken = None self.prevPageToken = None self.isAuth = False self.activeDownloads = 0 self.searchResult = config.plugins.YouTube.searchResult.value self.pageStartIndex = 1 self.pageEndIndex = int(self.searchResult) self.onLayoutFinish.append(self.layoutFinish) self.onClose.append(self.cleanVariables) def layoutFinish(self): self.thumbSize = [self['thumbnail'].instance.size().width(), self['thumbnail'].instance.size().height()] defThumbnail = resolveFilename(SCOPE_PLUGINS, 'Extensions/YouTube/icons/icon.png') self.decodeThumbnail('default', defThumbnail) self.splitTaimer.start(1) def cleanVariables(self): del self.thumbnailTaimer del self.splitTaimer self.ytdl = None self.youtube = None self.thumbnails = None self.entryList = None self.prevEntryList = None def createDefEntryList(self, entry_list, append): if not append: self.entryList = [] for Id, Title in entry_list: self.entryList.append(( Id, # Id None, # Thumbnail url None, # Thumbnail Title, # Title '', # Views '', # Duration None, # Video url None, # Description None, # Likes None, # Dislikes None, # Big thumbnail url None, # Channel Id '', # Published )) def createMainList(self): self.list = 'main' self.value = [None, None, ''] self.text = _('Choose what you want to do') self.createDefEntryList([ ['Search', _('Search')], ['PubFeeds', _('Public feeds')] ], False) if config.plugins.YouTube.login.value and \ config.plugins.YouTube.refreshToken.value != '': self.createDefEntryList([['MyFeeds', _('My feeds')]], True) self.setEntryList() def createSearchList(self): self.list = 'search' self.value = [None, None, ''] self.text = _('Search') self.createDefEntryList([ ['Searchvideo', _('Search videos')], ['Searchchannel', _('Search channels')], ['Searchplaylist', _('Search playlists')], ['Searchbroadcasts', _('Search live broadcasts')] ], False) self.setEntryList() def createFeedList(self): self.list = 'feeds' self.value = [None, None, ''] self.text = _('Public feeds') self.createDefEntryList([ ['top_rated', _('Top rated')], ['most_viewed', _('Most viewed')], ['most_recent', _('Recent')], ['HD_videos', _('HD videos')], ['embedded_videos', _('Embedded in webpages')], ['episodes', _('Shows')], ['movies', _('Movies')] ], False) self.setEntryList() def createMyFeedList(self): self.list = 'myfeeds' self.value = [None, None, ''] self.text = _('My feeds') self.createDefEntryList([ ['my_subscriptions', _('My Subscriptions')], ['my_watch_later', _('Watch Later')], ['my_history', _('History')], ['my_liked_videos', _('Liked videos')], ['my_favorites', _('Favorites')], ['my_uploads', _('Uploads')], ['my_playlists', _('Playlists')] ], False) self.setEntryList() def screenCallback(self, value = None, action = None): self.value = value self.action = action if action == 'OpenSearch': text = _('Download search results. Please wait...') elif action in ['playVideo', 'downloadVideo']: text = _('Extract video url. Please wait...') else: text = _('Download feed entries. Please wait...') self['text'].setText(text) self['list'].setList([]) self['key_red'].setText('') self['key_green'].setText('') self['red'].hide() self['green'].hide() self['menu'].hide() self['info'].hide() self.splitTaimer.start(1) def splitTaimerStop(self): self.splitTaimer.stop() if self.action == 'startup': from YouTubeVideoUrl import YouTubeVideoUrl self.ytdl = YouTubeVideoUrl() self.createBuild() self.createMainList() for job in job_manager.getPendingJobs(): self.activeDownloads += 1 elif self.action in ['playVideo', 'downloadVideo']: videoUrl = self.value[6] if not videoUrl: # remember video url videoUrl, urlError = self.getVideoUrl() if urlError: self.session.open(MessageBox, _('There was an error in extract video url:\n%s') % urlError, MessageBox.TYPE_INFO, timeout = 8) else: count = 0 for entry in self.entryList: if entry[0] == self.value[0]: self.entryList[count] = ( entry[0], # Id entry[1], # Thumbnail url entry[2], # Thumbnail entry[3], # Title entry[4], # Views entry[5], # Duration videoUrl, # Video url entry[7], # Description entry[8], # Likes entry[9], # Dislikes entry[10], # Big thumbnail url entry[11], # Channel Id entry[12], # Published ) break count += 1 if videoUrl: if self.action == 'playVideo': service = eServiceReference(4097, 0, videoUrl) service.setName(self.value[3]) current = [self.value[3], self.value[4], self.value[5], self.value[7], self.value[8], self.value[9], self.value[10], self.value[12]] print "[YouTube] Play:", videoUrl self.session.openWithCallback(self.playCallback, YouTubePlayer, service = service, current = current) else: self.videoDownload(videoUrl, self.value[3]) self.setEntryList() self.setPreviousList() else: self.setEntryList() self.setPreviousList() self.value = [None, None, ''] else: entryList = self.createEntryList() self.value[2] = '' if not entryList: self.session.open(MessageBox, _('There was an error in creating entry list!\nMaybe try other feeds...'), MessageBox.TYPE_INFO, timeout = 8) self.setEntryList() self.setPreviousList() self.prevEntryList.pop() else: self.entryList = entryList self.text = self.value[1] self.setEntryList() def setEntryList(self): self['text'].setText(self.text) self['list'].setList(self.entryList) self['red'].show() self['green'].show() self['menu'].show() self['key_red'].setText(_('Exit')) self['key_green'].setText(_('Open')) if self.list == 'videolist': self['info'].show() self.createThumbnails() def createThumbnails(self): for entry in self.entryList: if entry[2]: # If thumbnail created continue entryId = entry[0] if entryId in self.thumbnails: self.updateThumbnails() else: url = entry[1] if not url: image = resolveFilename(SCOPE_PLUGINS, 'Extensions/YouTube/icons/' + entryId + '.png') self.decodeThumbnail(entryId, image) else: image = os.path.join('/tmp/', str(entryId) + '.jpg') downloadPage(url, image)\ .addCallback(boundFunction(self.downloadFinished, entryId))\ .addErrback(boundFunction(self.downloadFailed, entryId)) def downloadFinished(self, entryId, result): image = os.path.join('/tmp/', str(entryId) + '.jpg') self.decodeThumbnail(entryId, image) def downloadFailed(self, entryId, result): print "[YouTube] Thumbnail download failed, use default for", entryId self.decodeThumbnail(entryId) def decodeThumbnail(self, entryId, image = None): if not image or not os.path.exists(image): print "[YouTube] Thumbnail not exists, use default for", entryId self.thumbnails[entryId] = True self.thumbnailTaimer.start(1) else: self.picloads[entryId] = ePicLoad() self.picloads[entryId].PictureData.get()\ .append(boundFunction(self.FinishDecode, entryId, image)) self.picloads[entryId].setPara(( self.thumbSize[0], self.thumbSize[1], self.sc[0], self.sc[1], False, 1, '#00000000')) self.picloads[entryId].startDecode(image) def FinishDecode(self, entryId, image, picInfo = None): ptr = self.picloads[entryId].getData() if ptr: self.thumbnails[entryId] = ptr del self.picloads[entryId] if image[:4] == '/tmp': os.remove(image) self.thumbnailTaimer.start(1) def updateThumbnails(self): self.thumbnailTaimer.stop() if len(self.picloads) != 0: self.thumbnailTaimer.start(1) else: count = 0 for entry in self.entryList: if not entry[2] and entry[0] in self.thumbnails: thumbnail = self.thumbnails[entry[0]] if thumbnail is True: thumbnail = self.thumbnails['default'] self.entryList[count] = ( entry[0], # Id entry[1], # Thumbnail url thumbnail, # Thumbnail entry[3], # Title entry[4], # Views entry[5], # Duration entry[6], # Video url entry[7], # Description entry[8], # Likes entry[9], # Dislikes entry[10], # Big thumbnail url entry[11], # Channel Id entry[12], # Published ) count += 1 self['list'].updateList(self.entryList) def selectNext(self): if self['list'].index + 1 < len(self.entryList): # not last enrty in entry list self['list'].selectNext() else: if self.nextPageToken: # call next serch results if it exist self.pageStartIndex = self.pageEndIndex + 1 self.pageEndIndex += int(self.searchResult) self.setNextEntries() else: self['list'].setIndex(0) def selectPrevious(self): if self['list'].index > 0: # not first enrty in entry list self['list'].selectPrevious() else: if self.prevPageToken: # call previous serch results if it exist self.pageEndIndex = self.pageStartIndex - 1 self.pageStartIndex -= int(self.searchResult) self.setPrevEntries() else: self['list'].setIndex(len(self.entryList) - 1) def playCallback(self, action=None): self.setEntryList() self.setPreviousList() if action: action = action[1] if action == 'quit': pass elif action == 'repeat': self.ok() elif action == 'ask': self.rememberCurList() title = _('What do you want to do?') list = ( (_('Quit'), 'quit'), (_('Play next video'), 'playnext'), (_('Play previous video'), 'playprev'), (_('Play video again'), 'repeat') ) self.session.openWithCallback(self.playCallback, ChoiceBox, title = title, list = list) else: if action == 'playnext': self.selectNext() elif action == 'playprev': self.selectPrevious() self.playTaimer = eTimer() self.playTaimer.timeout.callback.append(self.playTaimerStop) self.playTaimer.start(1) def playTaimerStop(self): self.playTaimer.stop() del self.playTaimer self.ok() def setPreviousList(self): lastInex = self.prevIndex[len(self.prevIndex) - 1] self['list'].setIndex(lastInex[0]) self.list = lastInex[1] self.text = lastInex[2] self.nextPageToken = lastInex[3] self.prevPageToken = lastInex[4] self['text'].setText(self.text) self.prevIndex.pop() def rememberCurList(self): self.prevIndex.append([self['list'].index, self.list, self.text, self.nextPageToken, self.prevPageToken]) def ok(self): current = self['list'].getCurrent() if current and current[0]: print "[YouTube] Selected:", current[0] self.rememberCurList() if self.list == 'videolist': self.screenCallback(current, 'playVideo') else: self.prevEntryList.append(self.entryList) if current[0] == 'Search': self.createSearchList() elif current[0] == 'PubFeeds': self.createFeedList() elif current[0] == 'MyFeeds': self.createMyFeedList() elif self.list == 'search': from YouTubeSearch import YouTubeSearch self.session.openWithCallback(self.searchScreenCallback, YouTubeSearch) elif self.list == 'feeds': self.screenCallback([current[0], current[3], self.value[2]], 'OpenFeeds') elif self.list == 'myfeeds': self.screenCallback([current[0], current[3], self.value[2]], 'OpenMyFeeds') elif self.list == 'playlist': self.screenCallback([current[0], current[3], self.value[2]], 'OpenPlayList') elif self.list == 'channel': self.screenCallback([current[0], current[3], self.value[2]], 'OpenChannelList') def searchScreenCallback(self, searchValue = None): if not searchValue: # cancel in search self.cancel() else: self.searchResult = config.plugins.YouTube.searchResult.value self.screenCallback([self['list'].getCurrent()[0][6:], searchValue, ''], 'OpenSearch') def getVideoUrl(self): try: videoUrl = self.ytdl.extract(self.value[0]) except Exception as e: print '[YouTube] Error in extract info:', e return None, e if videoUrl: return videoUrl, None print '[YouTube] Video url not found' return None, 'Video url not found!' def convertDate(self, duration): time = ':' + duration.replace('P', '')\ .replace('W', '-').replace('D', ' ').replace('T', '')\ .replace('H', ':').replace('M', ':').replace('S', '') if 'S' not in duration: time += '00' elif time[-2] == ':': time = time[:-1] + '0' + time[-1] if 'M' not in duration: time = time[:-2] + '00' + time[-3:] elif time[-5] == ':': time = time[:-4] + '0' + time[-4:] return time[1:] def createBuild(self): refreshToken = config.plugins.YouTube.refreshToken.value if not self.youtube or (not self.isAuth and refreshToken and config.plugins.YouTube.login.value): from YouTubeApi import YouTubeApi self.youtube = YouTubeApi( client_id = YOUTUBE_API_CLIENT_ID, client_secret = YOUTUBE_API_CLIENT_SECRET, developer_key = API_KEY, refresh_token = refreshToken) if self.youtube.access_token: self.isAuth = True else: self.isAuth = False def createEntryList(self): self.createBuild() order = 'date' searchType = 'video' q = '' videoEmbeddable = videoDefinition = videoType = eventType = '' if self.action == 'OpenSearch': order = config.plugins.YouTube.searchOrder.value if self.value[0] == 'broadcasts': eventType = 'live' else: searchType = self.value[0] if ' (' in self.value[1]: self.value[1] = self.value[1].rsplit(' (', 1)[0] q = self.value[1] elif self.action == 'OpenFeeds': if self.value[0] == 'top_rated': order = 'rating' elif self.value[0] == 'most_viewed': order = 'viewCount' elif self.value[0] == 'HD_videos': videoDefinition = 'high' elif self.value[0] == 'embedded_videos': videoEmbeddable = 'true' elif self.value[0] == 'episodes': videoType = 'episode' elif self.value[0] == 'movies': videoType = 'movie' elif self.action == 'OpenMyFeeds': if not self.isAuth: return None elif self.value[0] == 'my_watch_later': playlist = 'watchLater' elif self.value[0] == 'my_history': playlist = 'watchHistory' elif self.value[0] == 'my_liked_videos': playlist = 'likes' elif self.value[0] == 'my_favorites': playlist = 'favorites' elif self.value[0] == 'my_uploads': playlist = 'uploads' videos = [] if self.action == 'OpenMyFeeds': if self.value[0] == 'my_subscriptions': self.list = 'playlist' searchResponse = self.youtube.subscriptions_list( maxResults = self.searchResult, pageToken = self.value[2] ) self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) for result in searchResponse.get('items', []): try: Id = 'UU' + result['snippet']['resourceId']['channelId'][2:] except: Id = None try: Thumbnail = str(result['snippet']['thumbnails']['high']['url']) except: Thumbnail = None try: Title = str(result['snippet']['title']) except: Title = '' try: Subscription = result['id'] except: Subscription = '' videos.append((Id, Thumbnail, None, Title, '', '', Subscription, None, None, None, None, None, '')) if len(videos) > 1: videos.insert(0, ('recent_subscr', None, None, _('Recent'), '', '', None, None, None, None, None, None, '')) return videos elif self.value[0] == 'my_playlists': self.list = 'playlist' searchResponse = self.youtube.playlists_list( maxResults = self.searchResult, pageToken = self.value[2] ) self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) for result in searchResponse.get('items', []): try: Id = result['id'] except: Id = None try: Thumbnail = str(result['snippet']['thumbnails']['default']['url']) except: Thumbnail = None try: Title = str(result['snippet']['title']) except: Title = '' videos.append((Id, Thumbnail, None, Title, '', '', None, None, None, None, None, None, '')) return videos else: # all other my data channel = '' searchResponse = self.youtube.channels_list( maxResults = self.searchResult, pageToken = self.value[2] ) self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) for result in searchResponse.get('items', []): channel = result['contentDetails']['relatedPlaylists'][playlist] videos = self.videoIdFromPlaylist(channel) return self.extractVideoIdList(videos) elif self.action == 'OpenPlayList': if self.value[0] == 'recent_subscr': for subscription in self.entryList: if subscription[0] != 'recent_subscr': videos += self.videoIdFromPlaylist(subscription[0]) if videos: videos = sorted(self.extractVideoIdList(videos), key=lambda k: k[12], reverse=True) # sort by date del videos[int(self.searchResult):] # leaves only searchResult long list self.nextPageToken = '' self.prevPageToken = '' self.setSearchResults(int(self.searchResult)) return videos else: videos = self.videoIdFromPlaylist(self.value[0]) if not videos: # if channel list from subscription searchResponse = self.youtube.search_list( part = 'id,snippet', channelId = 'UC' + self.value[0][2:], maxResults = self.searchResult, pageToken = self.value[2] ) return self.createList(searchResponse, True) return self.extractVideoIdList(videos) elif self.action == 'OpenChannelList': videos = self.videoIdFromChannellist(self.value[0]) return self.extractVideoIdList(videos) else: # search or pub feeds searchResponse = self.youtube.search_list_full( videoEmbeddable = videoEmbeddable, safeSearch = config.plugins.YouTube.safeSearch.value, eventType = eventType, videoType = videoType, videoDefinition = videoDefinition, order = order, part = 'id,snippet', q = q, relevanceLanguage = config.plugins.YouTube.searchLanguage.value, s_type = searchType, regionCode = config.plugins.YouTube.searchRegion.value, maxResults = self.searchResult, pageToken = self.value[2] ) if searchType != 'video': videos = self.createList(searchResponse, False) return videos self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) for result in searchResponse.get('items', []): videos.append(result['id']['videoId']) return self.extractVideoIdList(videos) def extractVideoIdList(self, videos): if len(videos) == 0: return None self.list = 'videolist' searchResponse = self.youtube.videos_list(v_id=','.join(videos)) videos = [] for result in searchResponse.get('items', []): try: Id = result['id'] except: Id = None try: Thumbnail = str(result['snippet']['thumbnails']['default']['url']) except: Thumbnail = None try: Title = str(result['snippet']['title']) except: Title = '' try: Views = str(result['statistics']['viewCount']) + _(' views') except: Views = '' try: Duration = _('Duration: ') + self.convertDate(str(result['contentDetails']['duration'])) except: Duration = '' try: Description = str(result['snippet']['description']) except: Description = '' try: Likes = str(result['statistics']['likeCount']) + _(' likes') except: Likes = '' try: Dislikes = str(result['statistics']['dislikeCount']) + _(' dislikes') except: Dislikes = '' try: ThumbnailUrl = str(result['snippet']['thumbnails']['medium']['url']) except: ThumbnailUrl = None try: ChannelId = result['snippet']['channelId'] except: ChannelId = None try: PublishedAt = _('Published at: ') + str(result['snippet']['publishedAt'])\ .replace('T', ' ').split('.')[0] except: PublishedAt = '' try: liveBroadcast = result['snippet']['liveBroadcastContent'] except: liveBroadcast = None if liveBroadcast == 'live': # if live broadcast insert in top of list videos.insert(0, (Id, Thumbnail, None, Title, Views, Duration, None, Description, Likes, Dislikes, ThumbnailUrl, ChannelId, PublishedAt)) else: videos.append((Id, Thumbnail, None, Title, Views, Duration, None, Description, Likes, Dislikes, ThumbnailUrl, ChannelId, PublishedAt)) return videos def videoIdFromPlaylist(self, channel): videos = [] searchResponse = self.youtube.playlistItems_list( maxResults = self.searchResult, playlistId = channel, pageToken = self.value[2] ) self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) for result in searchResponse.get('items', []): try: videos.append(result['snippet']['resourceId']['videoId']) except: pass return videos def videoIdFromChannellist(self, channel): videos = [] searchResponse = self.youtube.search_list( part = 'id', channelId = channel, maxResults = self.searchResult, pageToken = self.value[2] ) self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) for result in searchResponse.get('items', []): try: videos.append(result['id']['videoId']) except: pass return videos def createList(self, searchResponse, subscription): videos = [] self.nextPageToken = searchResponse.get('nextPageToken') self.prevPageToken = searchResponse.get('prevPageToken') self.setSearchResults(searchResponse.get('pageInfo', {}).get('totalResults', 0)) kind = self.list for result in searchResponse.get('items', []): try: kind = result['id']['kind'].split('#')[1] except: kind = self.list try: Id = result['id'][kind + 'Id'] except: Id = None try: Thumbnail = str(result['snippet']['thumbnails']['default']['url']) except: Thumbnail = None try: Title = str(result['snippet']['title']) except: Title = '' videos.append((Id, Thumbnail, None, Title, '', '', None, None, None, None, None, None, '')) if subscription and len(videos) > 1: videos.insert(0, ('recent_subscr', None, None, _('Recent'), '', '', None, None, None, None, None, None, '')) self.list = kind return videos def setSearchResults(self, totalResults): if not self.prevPageToken: self.pageStartIndex = 1 self.pageEndIndex = int(self.searchResult) if totalResults > 0: if self.pageEndIndex > totalResults: self.pageEndIndex = totalResults if ' (' in self.value[1]: self.value[1] = self.value[1].rsplit(' (', 1)[0] self.value[1] = self.value[1][:40] + _(' (%d-%d of %d)') % \ (self.pageStartIndex, self.pageEndIndex, totalResults) def cancel(self): entryListIndex = len(self.prevEntryList) - 1 if len(self.prevIndex) == 0 or entryListIndex < 0: self.close() else: self.entryList = self.prevEntryList[entryListIndex] self.prevEntryList.pop() self.setEntryList() self.setPreviousList() self['info'].hide() def openMenu(self): if self.list == 'main': self.session.openWithCallback(self.configScreenCallback, YouTubeSetup) else: title = _('What do you want to do?') clist = ((_('YouTube setup'), 'setup'),) if self.nextPageToken: clist += ((_('Next %s entries') % self.searchResult, 'next'),) if self.prevPageToken: clist += ((_('Previous %s entries') % self.searchResult, 'prev'),) if self.isAuth: if self.list == 'videolist': clist += ((_('Rate video'), 'rate'), (_('Subscribe this video channel'), 'subscribe_video'),) if self.value[0] == 'my_favorites': clist += ((_('Remove from favorites'), 'rem_favorite'),) else: clist += ((_('Add to favorites'), 'add_favorite'),) elif self.list == 'channel' and self.prevIndex[1][1] != 'myfeeds': clist += ((_('Subscribe'), 'subscribe'),) elif self.list == 'playlist' and self.prevIndex[1][1] == 'myfeeds' and \ len(self.prevIndex) == 2: clist += ((_('Unsubscribe'), 'unsubscribe'),) if self.list == 'videolist': clist += ((_('Search'), 'search'), (_('Download video'), 'download'),) if self.activeDownloads > 0: clist += ((_('Active video downloads'), 'download_list'),) clist += ((_('Close YouTube'), 'close'),) self.session.openWithCallback(self.menuCallback, ChoiceBox, title = title, list = clist) def menuCallback(self, answer): if answer: msg = None clist = None if answer[1] == 'setup': self.session.openWithCallback(self.configScreenCallback, YouTubeSetup) elif answer[1] == 'next': self.setNextEntries() elif answer[1] == 'prev': self.setPrevEntries() elif answer[1] == 'rate': clist = ((_('I like this'), 'like'), (_('I dislike this'), 'dislike'), (_('Remove my rating'), 'none'),) elif answer[1] == 'subscribe': current = self['list'].getCurrent()[0] msg = self.subscribeChannel(current) elif answer[1] == 'subscribe_video': current = self['list'].getCurrent()[11] msg = self.subscribeChannel(current) elif answer[1] == 'rem_favorite': current = self['list'].getCurrent()[0] msg = self.removeFromFavorites(current) elif answer[1] == 'add_favorite': current = self['list'].getCurrent()[0] msg = self.addToFavorites(current) elif answer[1] == 'unsubscribe': msg = self.unsubscribeChannel() elif answer[1] == 'search': clist = ((_('Search for similar'), 'similar'), (_('Videos from this video channel'), 'channel_videos'),) elif answer[1] == 'similar': term = self['list'].getCurrent()[3][:40] self.screenCallback(['video', term, ''], 'OpenSearch') elif answer[1] == 'channel_videos': current = self['list'].getCurrent() self.screenCallback([current[11], current[3][:40], ''], 'OpenChannelList') elif answer[1] == 'download': current = self['list'].getCurrent() if current[6]: self.videoDownload(current[6], current[3]) else: self.rememberCurList() self.screenCallback(current, 'downloadVideo') elif answer[1] == 'download_list': from YouTubeDownload import YouTubeDownloadList self.session.open(YouTubeDownloadList) elif answer[1] == 'close': self.close() else: msg = self.rateVideo(answer[1]) if msg: self.session.open(MessageBox, msg, MessageBox.TYPE_INFO, timeout = 3) elif clist: title = _('What do you want to do?') self.session.openWithCallback(self.menuCallback, ChoiceBox, title = title, list = clist) def configScreenCallback(self, callback=None): self.searchResult = config.plugins.YouTube.searchResult.value if self.list == 'main': # maybe autentification changed self.createMainList() def subscribeChannel(self, channelId): if self.youtube.subscriptions_insert(channelId = channelId): return _('Subscribed!') return _('There was an error!') def unsubscribeChannel(self): subscribtionId = self['list'].getCurrent()[6] if subscribtionId and self.youtube.subscriptions_delete(subscribtionId): # update subscriptions list newEntryList = [] for entry in self.entryList: if entry[6] != subscribtionId: newEntryList.append(entry) self.entryList = newEntryList self['list'].updateList(self.entryList) return _('Unsubscribed!') return _('There was an error!') def myFavoritesId(self): playlistId = '' searchResponse = self.youtube.playlists_list( maxResults = '50', pageToken = '' ) for result in searchResponse.get('items', []): try: if result['snippet']['title'] == 'Favorites': playlistId = str(result['id']) break except: pass return playlistId def removeFromFavorites(self, videoId): tmpId = self.myFavoritesId() if tmpId: searchResponse = self.youtube.playlistItems_list( maxResults = '50', playlistId = tmpId, pageToken = '' ) tmpId = None for result in searchResponse.get('items', []): try: if videoId == result['snippet']['resourceId']['videoId']: tmpId = result['id'] break except: pass if tmpId and self.youtube.playlistItems_delete(videoId = tmpId): # update favorites list newEntryList = [] for entry in self.entryList: if entry[0] != videoId: newEntryList.append(entry) self.entryList = newEntryList self['list'].updateList(self.entryList) return _('Removed!') return _('There was an error!') def addToFavorites(self, videoId): playlistId = self.myFavoritesId() if playlistId and self.youtube.playlistItems_insert( playlistId = playlistId, videoId = videoId ): return _('Added!') return _('There was an error!') def rateVideo(self, rating): videoId = self['list'].getCurrent()[0] if self.youtube.videos_rate(videoId = videoId, rating = rating): text = { 'like': _('Liked!'), 'dislike': _('Disliked!'), 'none': _('Rating removed!') } return text[rating] else: return _('There was an error!') def showEventInfo(self): if self.list == 'videolist': current = self['list'].getCurrent() current = [current[3], current[4], current[5], current[7], current[8], current[9], current[10], current[12]] self.session.open(YouTubeInfo, current = current) def videoDownload(self, url, title): downloadDir = config.plugins.YouTube.downloadDir.value if downloadDir[0] == "'": downloadDir = downloadDir[2:-2] if not os.path.exists(downloadDir): msg = _('Sorry, download directory not exist!\nPlease specify in the settings existing directory.') else: outputfile = os.path.join(downloadDir, title.replace('/', '') + '.mp4') if os.path.exists(outputfile): msg = _('Sorry, this file already exists:\n%s') % title else: from YouTubeDownload import downloadJob job_manager.AddJob(downloadJob(url, outputfile, title[:20], self.downloadStop)) self.activeDownloads += 1 msg = _('Video download started!') self.session.open(MessageBox, msg, MessageBox.TYPE_INFO, timeout = 5) def downloadStop(self): self.activeDownloads -= 1 def setPrevEntries(self): self.value[2] = self.prevPageToken self.usePageToken() def setNextEntries(self): self.value[2] = self.nextPageToken self.usePageToken() def usePageToken(self): text = self.text self.cancel() if self.list == 'search': self.rememberCurList() self.prevEntryList.append(self.entryList) self.screenCallback([self['list'].getCurrent()[0][6:], text, self.value[2]], 'OpenSearch') else: self.ok() class YouTubeInfo(Screen): screenWidth = getDesktop(0).size().width() if screenWidth and screenWidth == 1280: skin = """ """ elif screenWidth and screenWidth == 1920: skin = """ """ else: skin = """ """ def __init__(self, session, current): Screen.__init__(self, session) self.setTitle(_('YouTube info')) self['key_red'] = StaticText(_('Exit')) self['title'] = Label(current[0]) self['pic'] = Pixmap() self['description'] = ScrollLabel(current[3]) self['views'] = Label(current[1]) self['duration'] = Label(current[2]) self['likes'] = Label(current[4]) self['dislikes'] = Label(current[5]) self['published'] = Label(current[7]) self['actions'] = ActionMap(['ColorActions', 'InfobarShowHideActions', 'DirectionActions'], { 'red': self.close, 'toggleShow': self.close, 'hide': self.close, 'infoButton': self.close, 'up': self['description'].pageUp, 'down': self['description'].pageDown }, -2) self.picloads = None self.ThumbnailUrl = current[6] self.onLayoutFinish.append(self.LayoutFinish) def LayoutFinish(self): if self.ThumbnailUrl: downloadPage(self.ThumbnailUrl, '/tmp/hqdefault.jpg')\ .addCallback(self.downloadFinished) def downloadFinished(self, result): image = '/tmp/hqdefault.jpg' if os.path.exists(image): sc = AVSwitch().getFramebufferScale() self.picloads = ePicLoad() self.picloads.PictureData.get().append(self.FinishDecode) self.picloads.setPara(( self['pic'].instance.size().width(), self['pic'].instance.size().height(), sc[0], sc[1], False, 1, '#00000000')) self.picloads.startDecode(image) def FinishDecode(self, picInfo = None): ptr = self.picloads.getData() if ptr: self["pic"].instance.setPixmap(ptr.__deref__()) del self.picloads os.remove('/tmp/hqdefault.jpg') class YouTubeSetup(ConfigListScreen, Screen): def __init__(self, session): Screen.__init__(self, session) self.session = session self.skinName = ['YouTubeSetup', 'Setup'] self['key_red'] = StaticText(_('Cancel')) self['key_green'] = StaticText(_('Ok')) self['description'] = Label('') self['setupActions'] = ActionMap(['SetupActions', 'ColorActions'], { 'cancel': self.keyCancel, 'red': self.keyCancel, 'ok': self.ok, 'green': self.ok }, -2) self.mbox = None self.login = config.plugins.YouTube.login.value configlist = [] ConfigListScreen.__init__(self, configlist, session=session, on_change = self.checkLoginSatus) configlist.append(getConfigListEntry(_('Save search result:'), config.plugins.YouTube.saveHistory, _('Save your search result in the history, when search completed.'))) configlist.append(getConfigListEntry(_('Search results:'), config.plugins.YouTube.searchResult, _('How many search results will be returned.\nIf greater value then longer time will be needed for thumbnail download.'))) configlist.append(getConfigListEntry(_('Search region:'), config.plugins.YouTube.searchRegion, _('Return search results for the specified country.'))) configlist.append(getConfigListEntry(_('Search language:'), config.plugins.YouTube.searchLanguage, _('Return search results that are most relevant to the specified language.'))) configlist.append(getConfigListEntry(_('Sort search results by:'), config.plugins.YouTube.searchOrder, _('Order in which search results will be displayed.'))) configlist.append(getConfigListEntry(_('Exclude restricted content:'), config.plugins.YouTube.safeSearch, _('Try to exclude all restricted content from the search result.'))) configlist.append(getConfigListEntry(_('Maximum video resolution:'), config.plugins.YouTube.maxResolution, _('What maximum resolution used when playing video, if available.\nIf you have a slow Internet connection, you can use a lower resolution.'))) configlist.append(getConfigListEntry(_('When video ends:'), config.plugins.YouTube.onMovieEof, _('What to do when the video ends.'))) configlist.append(getConfigListEntry(_('When playback stop:'), config.plugins.YouTube.onMovieStop, _('What to do when stop playback in videoplayer.'))) configlist.append(getConfigListEntry(_('Login on startup:'), config.plugins.YouTube.login, _('Log in to your YouTube account when plugin starts.\nThis needs to approve in the Google home page!'))) configlist.append(getConfigListEntry(_('Download directory:'), config.plugins.YouTube.downloadDir, _('Specify the directory where save downloaded video files.'))) self['config'].list = configlist self['config'].l.setList(configlist) self.onLayoutFinish.append(self.layoutFinished) def layoutFinished(self): self.setTitle(_('YouTube setup')) def ok(self): if self["config"].getCurrent()[1] == config.plugins.YouTube.downloadDir: from YouTubeDownload import YouTubeDirBrowser downloadDir = config.plugins.YouTube.downloadDir.value if downloadDir[0] == "'": downloadDir = downloadDir[2:-2] self.session.openWithCallback(self.downloadPath, YouTubeDirBrowser, downloadDir) else: self.keySave() def downloadPath(self, res): if res: config.plugins.YouTube.downloadDir.setValue(res) def checkLoginSatus(self): if self.login != config.plugins.YouTube.login.value: self.login = config.plugins.YouTube.login.value if self.login: self.startAutentification() def startAutentification(self): if config.plugins.YouTube.refreshToken.value != '': self.session.openWithCallback(self.startupCallback, MessageBox, _('You already authorized access for this plugin to your YouTube account.\nDo you want to do it again to update access data?')) else: self.startupCallback(True) def startupCallback(self, answer): if answer: self.session.openWithCallback(self.warningCallback, MessageBox, _('To perform authentication will need in a web browser open Google home page, and enter the code!\nDo you currently have Internet access on the other device and we can continue?')) def warningCallback(self, answer): if not answer: self.login = config.plugins.YouTube.login.value = False else: from OAuth import OAuth self.splitTaimer = eTimer() self.splitTaimer.timeout.callback.append(self.splitTaimerStop) self.oauth = OAuth(YOUTUBE_API_CLIENT_ID, YOUTUBE_API_CLIENT_SECRET) userCode = str(self.oauth.get_user_code()) msg = _('Go to %s\nAnd enter the code %s') % (str(self.oauth.verification_url), userCode) print "[YouTube] ", msg self.mbox = self.session.open(MessageBox, msg, MessageBox.TYPE_INFO) self.splitTaimer.start(9000) def splitTaimerStop(self): self.splitTaimer.stop() # Here we waiting until the user enter a code refreshToken, retryInterval = self.oauth.get_new_token() if not refreshToken: self.splitTaimer.start(retryInterval * 1000) else: print "[YouTube] Get refresh token" if self.mbox: self.mbox.close() config.plugins.YouTube.refreshToken.value = refreshToken config.plugins.YouTube.refreshToken.save() del self.splitTaimer self.mbox = None self.oauth = None