class YoutubeSkill(MycroftSkill): def __init__(self): super(YoutubeSkill, self).__init__(name="YoutubeSkill") self.nextpage_url = None self.previouspage_url = None self.live_category = None self.recentList = deque() self.recentPageObject = {} self.nextSongList = None self.lastSong = None self.videoPageObject = {} self.isTitle = None self.newsCategoryList = {} self.musicCategoryList = {} self.techCategoryList = {} self.polCategoryList = {} self.gamingCategoryList = {} self.searchCategoryList = {} self.storeDB = dirname(__file__) + '-recent.db' self.recent_db = JsonStorage(self.storeDB) self.ytkey = base64.b64decode("QUl6YVN5RE9tSXhSemI0RzFhaXFzYnBaQ3IwQTlFN1NrT0pVRURr") pafy.set_api_key(self.ytkey) def initialize(self): self.load_data_files(dirname(__file__)) self.bus.on('youtube-skill.aiix.home', self.launcherId) youtubepause = IntentBuilder("YoutubePauseKeyword"). \ require("YoutubePauseKeyword").build() self.register_intent(youtubepause, self.youtubepause) youtuberesume = IntentBuilder("YoutubeResumeKeyword"). \ require("YoutubeResumeKeyword").build() self.register_intent(youtuberesume, self.youtuberesume) youtubesearchpage = IntentBuilder("YoutubeSearchPageKeyword"). \ require("YoutubeSearchPageKeyword").build() self.register_intent(youtubesearchpage, self.youtubesearchpage) youtubelauncherId = IntentBuilder("YoutubeLauncherId"). \ require("YoutubeLauncherIdKeyword").build() self.register_intent(youtubelauncherId, self.launcherId) self.add_event('aiix.youtube-skill.playvideo_id', self.play_event) self.gui.register_handler('YoutubeSkill.SearchLive', self.searchLive) self.gui.register_handler('YoutubeSkill.NextPage', self.searchNextPage) self.gui.register_handler('YoutubeSkill.PreviousPage', self.searchPreviousPage) self.gui.register_handler('YoutubeSkill.NextAutoPlaySong', self.nextSongForAutoPlay) self.gui.register_handler('YoutubeSkill.RefreshWatchList', self.refreshWatchList) self.gui.register_handler('YoutubeSkill.ClearDB', self.clear_db) def launcherId(self, message): self.show_homepage({}) def getListSearch(self, text): query = quote(text) url = "https://www.youtube.com/results?search_query=" + quote(query) response = urlopen(url) html = response.read() a_tag = SoupStrainer('a') soup = BeautifulSoup(html, 'html.parser', parse_only=a_tag) for vid in soup.findAll(attrs={'class': 'yt-uix-tile-link'}): if "googleads" not in vid['href'] and not vid['href'].startswith( u"/user") and not vid['href'].startswith(u"/channel"): id = vid['href'].split("v=")[1].split("&")[0] return id def moreRandomListSearch(self, text): LOG.info(text) query = quote(text) try: querySplit = text.split() LOG.info(querySplit) searchQuery = "*," + quote(querySplit[0]) + quote(querySplit[1]) + ",*" except: LOG.info("fail") searchQuery = "*," + quote(query) + ",*" LOG.info(searchQuery) return searchQuery def searchLive(self, message): videoList = [] videoList.clear() videoPageObject = {} try: query = message.data["Query"] LOG.info("I am in search Live") self.searchCategoryList["videoList"] = self.build_category_list(quote(query)) self.gui["searchListBlob"] = self.searchCategoryList self.gui["previousAvailable"] = False self.gui["nextAvailable"] = True self.gui["bgImage"] = quote(query) self.gui.show_page("YoutubeLiveSearch.qml", override_idle=True) except: LOG.debug("error") def searchNextPage(self, message): getCategory = message.data["Category"] LOG.info(getCategory) if getCategory == "News": LOG.info("In Category News") newsAdditionalPages = self.process_additional_pages("news") self.newsCategoryList['videoList'] = self.build_category_list_from_url("https://www.youtube.com" + newsAdditionalPages[0]) self.gui["newsNextAvailable"] = False self.gui["newsListBlob"] = self.newsCategoryList if getCategory == "Music": LOG.info("In Category Music") musicAdditionalPages = self.process_additional_pages("music") self.musicCategoryList['videoList'] = self.build_category_list_from_url("https://www.youtube.com" + musicAdditionalPages[0]) self.gui["musicNextAvailable"] = False self.gui["musicListBlob"] = self.musicCategoryList if getCategory == "Technology": LOG.info("In Category Technology") technologyAdditionalPages = self.process_additional_pages("technology") self.techCategoryList['videoList'] = self.build_category_list_from_url("https://www.youtube.com" + technologyAdditionalPages[0]) self.gui["techNextAvailable"] = False self.gui["techListBlob"] = self.techCategoryList if getCategory == "Politics": LOG.info("In Category Politics") politicsAdditionalPages = self.process_additional_pages("politics") self.polCategoryList['videoList'] = self.build_category_list_from_url("https://www.youtube.com" + politicsAdditionalPages[0]) self.gui["polNextAvailable"] = False self.gui["polListBlob"] = self.polCategoryList if getCategory == "Gaming": LOG.info("In Category Gaming") gamingAdditionalPages = self.process_additional_pages("gaming") self.gamingCategoryList['videoList'] = self.build_category_list_from_url("https://www.youtube.com" + gamingAdditionalPages[0]) self.gui["gamingNextAvailable"] = False self.gui["gamingListBlob"] = self.gamingCategoryList if getCategory == "Search": LOG.info("In Search") def searchPreviousPage(self, message): getCategory = message.data["Category"] LOG.info(getCategory) if getCategory == "News": LOG.info("In Category News") newsAdditionalPages = self.process_additional_pages("news") self.newsCategoryList['videoList'] = self.build_category_list_from_url(newsAdditionalPages[1]) self.gui["newsNextAvailable"] = True self.gui["newsListBlob"] = self.newsCategoryList if getCategory == "Music": LOG.info("In Category Music") musicAdditionalPages = self.process_additional_pages("music") self.musicCategoryList['videoList'] = self.build_category_list_from_url(musicAdditionalPages[1]) self.gui["musicNextAvailable"] = True self.gui["musicListBlob"] = self.musicCategoryList if getCategory == "Technology": LOG.info("In Category Technology") technologyAdditionalPages = self.process_additional_pages("technology") self.techCategoryList['videoList'] = self.build_category_list_from_url(technologyAdditionalPages[1]) self.gui["techNextAvailable"] = True self.gui["techListBlob"] = self.techCategoryList if getCategory == "Politics": LOG.info("In Category Politics") politicsAdditionalPages = self.process_additional_pages("politics") self.polCategoryList['videoList'] = self.build_category_list_from_url(politicsAdditionalPages[1]) self.gui["polNextAvailable"] = True self.gui["polListBlob"] = self.polCategoryList if getCategory == "Gaming": LOG.info("In Category Gaming") gamingAdditionalPages = self.process_additional_pages("gaming") self.gamingCategoryList['videoList'] = self.build_category_list_from_url(gamingAdditionalPages[1]) self.gui["gamingNextAvailable"] = True self.gui["gamingListBlob"] = self.gamingCategoryList if getCategory == "Search": LOG.info("In Search") def getTitle(self, text): query = quote(text) url = "https://www.youtube.com/results?search_query=" + quote(query) response = urlopen(url) html = response.read() soup = BeautifulSoup(html) for vid in soup.findAll(attrs={'class': 'yt-uix-tile-link'}): if "googleads" not in vid['href'] and not vid['href'].startswith( u"/user") and not vid['href'].startswith(u"/channel"): videoTitle = vid['title'] return videoTitle @intent_file_handler('youtube.intent') def youtube(self, message): self.stop() self.gui.clear() self.enclosure.display_manager.remove_active() utterance = message.data['videoname'].lower() self.youtube_play_video(utterance) def youtube_play_video(self, utterance): self.gui["setTitle"] = "" self.gui["video"] = "" self.gui["status"] = "stop" self.gui["currenturl"] = "" self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["videoThumb"] = "" url = "https://www.youtube.com/results?search_query=" + quote(utterance) response = urlopen(url) html = response.read() a_tag = SoupStrainer('a') soup = BeautifulSoup(html, 'html.parser', parse_only=a_tag) self.gui["video"] = "" self.gui["status"] = "stop" self.gui["currenturl"] = "" self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["videoThumb"] = "" self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) rfind = soup.findAll(attrs={'class': 'yt-uix-tile-link'}) try: vid = str(rfind[0].attrs['href']) veid = "https://www.youtube.com{0}".format(vid) LOG.info(veid) getvid = vid.split("v=")[1].split("&")[0] except: vid = str(rfind[1].attrs['href']) veid = "https://www.youtube.com{0}".format(vid) LOG.info(veid) getvid = vid.split("v=")[1].split("&")[0] thumb = "https://img.youtube.com/vi/{0}/maxresdefault.jpg".format(getvid) self.gui["videoThumb"] = thumb self.lastSong = veid video = pafy.new(veid) playstream = video.streams[0] playurl = playstream.url self.gui["status"] = str("play") self.gui["video"] = str(playurl) self.gui["currenturl"] = str(vid) self.gui["currenttitle"] = video.title self.gui["setTitle"] = video.title self.gui["viewCount"] = video.viewcount self.gui["publishedDate"] = video.published self.gui["videoAuthor"] = video.username self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["nextSongTitle"] = "" self.gui["nextSongImage"] = "" self.gui["nextSongID"] = "" self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) self.gui["currenttitle"] = self.getTitle(utterance) if 'recentList' in self.recent_db.keys(): recentVideoList = self.recent_db['recentList'] else: recentVideoList = [] recentVideoList.insert(0, {"videoID": getvid, "videoTitle": video.title, "videoImage": video.thumb}) self.recent_db['recentList'] = recentVideoList self.recent_db.store() self.gui["recentListBlob"] = self.recent_db self.youtubesearchpagesimple(utterance) self.isTitle = video.title self.gui["recentListBlob"] = self.recent_db def youtubepause(self, message): self.gui["status"] = str("pause") self.gui.show_page("YoutubePlayer.qml") def youtuberesume(self, message): self.gui["status"] = str("play") self.gui.show_page("YoutubePlayer.qml") def youtubesearchpage(self, message): self.stop() videoList = [] videoList.clear() videoPageObject = {} utterance = message.data.get('utterance').lower() utterance = utterance.replace( message.data.get('YoutubeSearchPageKeyword'), '') vid = self.getListSearch(utterance) url = "https://www.youtube.com/results?search_query=" + vid response = urlopen(url) html = response.read() videoList = self.process_soup_additional(html) videoPageObject['videoList'] = videoList self.gui["videoListBlob"] = videoPageObject self.gui["recentListBlob"] = self.recent_db self.gui.show_page("YoutubeSearch.qml") def youtubesearchpagesimple(self, query): LOG.info(query) videoList = [] videoList.clear() videoPageObject = {} vid = self.moreRandomListSearch(query) url = "https://www.youtube.com/results?search_query=" + vid response = urlopen(url) html = response.read() videoList = self.process_soup_additional(html) videoPageObject['videoList'] = videoList self.gui["videoListBlob"] = videoPageObject self.gui["recentListBlob"] = self.recent_db def show_homepage(self, message): LOG.info("I AM IN HOME PAGE FUNCTION") self.gui.clear() self.enclosure.display_manager.remove_active() self.gui["loadingStatus"] = "" self.gui.show_page("YoutubeLogo.qml") self.process_home_page() def process_home_page(self): LOG.info("I AM IN HOME PROCESS PAGE FUNCTION") self.gui.show_page("YoutubeLogo.qml") self.gui["loadingStatus"] = "Fetching News" self.newsCategoryList['videoList'] = self.build_category_list("news") self.gui["loadingStatus"] = "Fetching Music" self.musicCategoryList['videoList'] = self.build_category_list("music") self.gui.clear() self.enclosure.display_manager.remove_active() self.show_search_page() self.techCategoryList['videoList'] = self.build_category_list("technology") self.gui["techListBlob"] = self.techCategoryList self.polCategoryList['videoList'] = self.build_category_list("politics") self.gui["polListBlob"] = self.polCategoryList self.gamingCategoryList['videoList'] = self.build_category_list("gaming") self.gui["gamingListBlob"] = self.gamingCategoryList LOG.info("I AM NOW IN REMOVE LOGO PAGE FUNCTION") def show_search_page(self): LOG.info("I AM NOW IN SHOW SEARCH PAGE FUNCTION") LOG.info(self.techCategoryList) self.gui["newsListBlob"] = self.newsCategoryList self.gui["newsNextAvailable"] = True self.gui["musicListBlob"] = self.musicCategoryList self.gui["musicNextAvailable"] = True self.gui["techListBlob"] = self.techCategoryList self.gui["techNextAvailable"] = True self.gui["polListBlob"] = self.polCategoryList self.gui["polNextAvailable"] = True self.gui["gamingListBlob"] = self.gamingCategoryList self.gui["gamingNextAvailable"] = True self.gui["searchListBlob"] = "" self.gui["previousAvailable"] = False self.gui["nextAvailable"] = True self.gui["bgImage"] = self.live_category self.gui.show_page("YoutubeLiveSearch.qml", override_idle=True) def play_event(self, message): urlvideo = "http://www.youtube.com/watch?v={0}".format(message.data['vidID']) self.lastSong = message.data['vidID'] video = pafy.new(urlvideo) playstream = video.getbest(preftype="mp4", ftypestrict=True) playurl = playstream.url self.speak("Playing") self.gui["video"] = str(playurl) self.gui["status"] = str("play") self.gui["currenturl"] = str(message.data['vidID']) self.gui["currenttitle"] = str(message.data['vidTitle']) self.gui["setTitle"] = video.title self.gui["viewCount"] = video.viewcount self.gui["publishedDate"] = video.published self.gui["videoAuthor"] = video.username self.gui["nextSongTitle"] = "" self.gui["nextSongImage"] = "" self.gui["nextSongID"] = "" videoTitleSearch = str(message.data['vidTitle']).join(str(message.data['vidTitle']).split()[:-1]) self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) thumb = "https://img.youtube.com/vi/{0}/maxresdefault.jpg".format(message.data['vidID']) if 'recentList' in self.recent_db.keys(): recentVideoList = self.recent_db['recentList'] else: recentVideoList = [] recentVideoList.insert(0, {"videoID": str(message.data['vidID']), "videoTitle": str(message.data['vidTitle']), "videoImage": video.thumb}) self.recent_db['recentList'] = recentVideoList self.recent_db.store() self.gui["recentListBlob"] = self.recent_db self.isTitle = video.title def stop(self): self.enclosure.bus.emit(Message("metadata", {"type": "stop"})) pass def process_soup(self, htmltype): videoList = [] videoList.clear() soup = BeautifulSoup(htmltype) for vid in soup.findAll(attrs={'class': 'yt-uix-tile-link'}): if "googleads" not in vid['href'] and not vid['href'].startswith( u"/user") and not vid['href'].startswith(u"/channel"): LOG.info(vid) videoID = vid['href'].split("v=")[1].split("&")[0] videoTitle = vid['title'] videoImage = "https://i.ytimg.com/vi/{0}/hqdefault.jpg".format(videoID) videoList.append({"videoID": videoID, "videoTitle": videoTitle, "videoImage": videoImage}) if len(videoList) > 1: self.nextSongList = videoList[1] else: self.nextSongList = videoList[0] return videoList def process_soup_additional(self, htmltype): videoList = [] videoList.clear() soup = BeautifulSoup(htmltype) getVideoDetails = zip(soup.findAll(attrs={'class': 'yt-uix-tile-link'}), soup.findAll(attrs={'class': 'yt-lockup-byline'}), soup.findAll(attrs={'class': 'yt-lockup-meta-info'}), soup.findAll(attrs={'class': 'video-time'})) for vid in getVideoDetails: if "googleads" not in vid[0]['href'] and not vid[0]['href'].startswith( u"/user") and not vid[0]['href'].startswith(u"/channel"): videoID = vid[0]['href'].split("v=")[1].split("&")[0] videoTitle = vid[0]['title'] videoImage = "https://i.ytimg.com/vi/{0}/hqdefault.jpg".format(videoID) videoChannel = vid[1].contents[0].string videoUploadDate = vid[2].contents[0].string videoDuration = vid[3].contents[0].string if "watching" in vid[2].contents[0].string: videoViews = "Live" else: try: videoViews = vid[2].contents[1].string except: videoViews = "Playlist" videoList.append({"videoID": videoID, "videoTitle": videoTitle, "videoImage": videoImage, "videoChannel": videoChannel, "videoViews": videoViews, "videoUploadDate": videoUploadDate, "videoDuration": videoDuration}) #if len(videoList) > 1: #self.nextSongList = videoList[1] #else: #self.nextSongList = videoList[0] return videoList def process_additional_pages(self, category): url = "https://www.youtube.com/results?search_query={0}".format(category) response = urlopen(url) html = response.read() soup = BeautifulSoup(html) buttons = soup.findAll('a', attrs={'class':"yt-uix-button vve-check yt-uix-sessionlink yt-uix-button-default yt-uix-button-size-default"}) try: nPage = buttons[0]['href'] except: nPage = self.process_additional_pages_fail(category) pPage = url addPgObj = [nPage, pPage] return addPgObj def process_additional_pages_fail(self, category): url = None if category == "news": url = "/results?search_query=world+news" if category == "music": url = "/results?search_query=latest+music" if category == "technology": url = "/results?search_query=latest+tech" if category == "politics": url = "/results?search_query=latest+politics" if category == "gaming": url = "/results?search_query=latest+games" return url def nextSongForAutoPlay(self): self.gui["nextSongTitle"] = self.nextSongList["videoTitle"] self.gui["nextSongImage"] = self.nextSongList["videoImage"] self.gui["nextSongID"] = self.nextSongList["videoID"] def refreshWatchList(self, message): try: self.youtubesearchpagesimple(message.data["title"]) except: self.youtubesearchpagesimple(self.isTitle) @intent_file_handler('youtube-repeat.intent') def youtube_repeat_last(self): video = pafy.new(self.lastSong) thumb = video.thumb playstream = video.streams[0] playurl = playstream.url self.gui["status"] = str("play") self.gui["video"] = str(playurl) self.gui["currenturl"] = "" self.gui["currenttitle"] = video.title self.gui["setTitle"] = video.title self.gui["viewCount"] = video.viewcount self.gui["publishedDate"] = video.published self.gui["videoAuthor"] = video.username self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["nextSongTitle"] = "" self.gui["nextSongImage"] = "" self.gui["nextSongID"] = "" self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) self.youtubesearchpagesimple(video.title) self.isTitle = video.title def build_category_list(self, category): url = "https://www.youtube.com/results?search_query={0}".format(category) response = urlopen(url) html = response.read() videoList = self.process_soup_additional(html) return videoList def build_category_list_from_url(self, link): url = link print(url) response = urlopen(url) html = response.read() videoList = self.process_soup_additional(html) return videoList def clear_db(self): LOG.info("In DB Clear") self.recent_db.clear() self.recent_db.store() self.gui["recentListBlob"] = ""
class PixabaySkill(MycroftSkill): # The constructor of the skill, which calls MycroftSkill's constructor def __init__(self): super(PixabaySkill, self).__init__(name="PixabaySkill") def initialize(self): self.add_event('pixabay-skill.aiix.home', self.handle_pixabay_homescreen) self.gui.register_handler("pixabay.show.image", self.handle_pixabay_show_image) self.gui.register_handler("pixabay.show.video", self.handle_pixabay_show_video) self.gui.register_handler("pixabay.gallery.next", self.handle_gallery_next_page) self.gui.register_handler("pixabay.gallery.previous", self.handle_gallery_previous_page) self.gui.register_handler("pixabay.idle.set_idle", self.handle_set_idlescreen_type) self.gui.register_handler("pixabay.idle.enableTime", self.handle_idle_enable_time) self.gui.register_handler("pixabay.idle.disableTime", self.handle_idle_disable_time) self.gui.register_handler("pixabay.idle.updateTime", self.handle_idle_update_time) self.gui.register_handler("pixabay.idle.removeConfigPage", self.handle_remove_configure_idle_screen) self.entKey = base64.b64decode( "MTcyMjI5NDctYTlmNTQxNmQ2ODhkNDVmNmJkZmY4ZWEzYQ==") self.video = Video(self.entKey) self.image = Image(self.entKey) self.shownPageNumber = None self.numberOfAvailablePages = None self.previousQuery = None self.currentType = None self.currentDir = dirname(dirname(abspath(__file__))) self.wantedDir = "pixabayData" self.dataPath = join(self.currentDir, self.wantedDir) self.videoPath = join(self.dataPath, "video.mp4") # Set All Paths try: os.mkdir(self.dataPath) except OSError as error: print("Directory Already Exist Skipping") self.storeDB = join(self.dataPath, 'pixabay-idle.db') self.idle_db = JsonStorage(self.storeDB) self.configDB = join(self.dataPath, 'pixabay-config.db') self.idle_config_db = JsonStorage(self.configDB) # Make Import For TimeData try: time_date_path = "/opt/mycroft/skills/mycroft-date-time.mycroftai/__init__.py" time_date_id = "datetimeskill" datetimeskill = load_skill_module(time_date_path, time_date_id) from datetimeskill import TimeSkill self.dt_skill = TimeSkill() except: print("Failed To Import DateTime Skill") def handle_pixabay_homescreen(self, message): self.handle_pixabay_display("Homescreen") @intent_file_handler("PixabaySearchImage.intent") def handle_pixabay_search_image_type(self, message): query = message.data["query"] self.previousQuery = query self.shownPageNumber = 1 self.currentType = "Image" ims = self.image.search(q=query, lang='en', image_type='photo', orientation='vertical', category='all', safesearch='true', order='latest', page=1, per_page=6) totalImages = ims['total'] totalHits = ims['totalHits'] self.handle_number_of_pages(totalImages, totalHits) self.gui["currentPageNumber"] = self.shownPageNumber self.gui["showMoreAvailable"] = self.handle_show_more_available( self.shownPageNumber) self.gui["imageGalleryModel"] = ims['hits'] self.handle_pixabay_display("ImageGallery") def handle_pixabay_show_image(self, message): self.gui["imageURL"] = message.data["largeImageURL"] self.handle_pixabay_display("Image") @intent_file_handler("PixabaySearchVideo.intent") def handle_pixabay_search_video_type(self, message): query = message.data["query"] self.previousQuery = query self.shownPageNumber = 1 self.currentType = "Video" vis = self.video.search(q=query, lang='en', video_type='all', category='all', page=1, per_page=6) totalImages = vis['total'] totalHits = vis['totalHits'] print(totalImages) print(totalHits) self.handle_number_of_pages(totalImages, totalHits) self.gui["currentPageNumber"] = self.shownPageNumber self.gui["showMoreAvailable"] = self.handle_show_more_available( self.shownPageNumber) self.gui["videoGalleryModel"] = vis['hits'] addr = vis['hits'] print(addr) self.handle_pixabay_display("VideoGallery") def handle_pixabay_show_video(self, message): orignalurl = message.data['videourl'] videoURL = self.handle_pixabay_extract_video(orignalurl) self.gui["videoURL"] = videoURL self.handle_pixabay_display("Video") def handle_pixabay_extract_video(self, videoURL): extractvideofromloc = requests.get(videoURL, allow_redirects=False) actualVideoUrl = extractvideofromloc.headers['location'] r = requests.get(actualVideoUrl, stream=True) with open(self.videoPath, 'wb') as f: for chunk in r.iter_content(chunk_size=1024 * 1024): if chunk: f.write(chunk) videoURL = self.videoPath return videoURL def handle_pixabay_display(self, state): if state is "Image": self.gui["setMessage"] = "" self.gui.show_page("Image.qml", override_idle=True) elif state is "Video": self.gui["setMessage"] = "" self.gui.show_page("Video.qml", override_idle=True) elif state is "Homescreen": self.gui.show_page("Homepage.qml", override_idle=True) else: self.gui["pageState"] = state self.gui.show_page("pixabayLoader.qml", override_idle=True) def handle_gallery_next_page(self, message): galleryType = message.data["galleryType"] pageNumber = message.data["currentPageNumber"] if pageNumber < self.numberOfAvailablePages: pageNumber = self.shownPageNumber + 1 self.shownPageNumber = pageNumber if galleryType == "Image": ims = self.image.search(q=self.previousQuery, lang='en', image_type='all', orientation='vertical', category='all', safesearch='true', order='latest', page=self.shownPageNumber, per_page=6) self.gui["currentPageNumber"] = self.shownPageNumber self.gui[ "showMoreAvailable"] = self.handle_show_more_available( self.shownPageNumber) self.gui["imageGalleryModel"] = ims['hits'] elif galleryType == "Video": vis = self.video.search(q=self.previousQuery, lang='en', video_type='all', category='all', page=self.shownPageNumber, per_page=6) self.gui["currentPageNumber"] = self.shownPageNumber self.gui[ "showMoreAvailable"] = self.handle_show_more_available( self.shownPageNumber) self.gui["videoGalleryModel"] = vis['hits'] self.handle_pixabay_display("VideoGallery") else: print("Valid Type Not Found") def handle_gallery_previous_page(self, message): galleryType = message.data["galleryType"] pageNumber = message.data["currentPageNumber"] if pageNumber > 1: pageNumber = self.shownPageNumber - 1 self.shownPageNumber = pageNumber if galleryType == "Image": ims = self.image.search(q=self.previousQuery, lang='en', image_type='all', orientation='all', category='all', safesearch='true', order='latest', page=self.shownPageNumber, per_page=6) self.gui["currentPageNumber"] = self.shownPageNumber self.gui[ "showMoreAvailable"] = self.handle_show_more_available( self.shownPageNumber) self.gui["imageGalleryModel"] = ims['hits'] elif galleryType == "Video": vis = self.video.search(q=self.previousQuery, lang='en', video_type='all', category='all', page=self.shownPageNumber, per_page=6) self.gui["currentPageNumber"] = self.shownPageNumber self.gui[ "showMoreAvailable"] = self.handle_show_more_available( self.shownPageNumber) self.gui["videoGalleryModel"] = vis['hits'] self.handle_pixabay_display("VideoGallery") else: print("Valid Type Not Found") @intent_handler( IntentBuilder("HandleAudioGalleryNext").require( "PixabayGalleryNextKeyword").build()) def handle_audio_gallery_next(self): currentPageNumber = self.shownPageNumber self.handle_gallery_next_page( Message( "data", { "currentPageNumber": currentPageNumber, "galleryType": self.currentType })) @intent_handler( IntentBuilder("HandleAudioGalleryNext").require( "PixabayGalleryPreviousKeyword").build()) def handle_audio_gallery_previous(self): currentPageNumber = self.shownPageNumber self.handle_gallery_previous_page( Message( "data", { "currentPageNumber": currentPageNumber, "galleryType": self.currentType })) def handle_number_of_pages(self, total, totalhits): if total > totalhits: orgNumPage = totalhits / 6 if orgNumPage > 10: self.numberOfAvailablePages = 10 return 10 else: orgNumPage = totalhits / 6 self.numberOfAvailablePages = orgNumPage return orgNumPage elif total < totalhits: orgNumPage = total / 6 if orgNumPage > 10: self.numberOfAvailablePages = 10 return 10 else: orgNumPage = totalhits / 6 self.numberOfAvailablePages = orgNumPage return orgNumPage elif total == totalhits: orgNumPage = total / 6 if orgNumPage > 10: self.numberOfAvailablePages = 10 return 10 else: self.numberOfAvailablePages = orgNumPage return orgNumPage def handle_show_more_available(self, currentPage): if currentPage < self.numberOfAvailablePages: return True else: return False def handle_set_idlescreen_type(self, message): idleType = message.data["idleType"] self.idle_db.clear() if idleType == "Image": idleImageURL = message.data["idleImageURL"] imageType = idleImageURL.split('.')[-1] imagePath = join(self.dataPath, str("pixabay-idle" + "." + imageType)) self.extract_image_for_idle(idleImageURL, imagePath) self.gui["idleType"] = "ImageIdle" self.gui["idleGenericURL"] = imagePath self.idle_db["idleInfo"] = { "idleType": "ImageIdle", "idleGenericURL": imagePath } self.idle_db.store() self.gui["setMessage"] = "New Homescreen Set" if idleType == "Video": idleVideoURL = message.data["idleVideoURL"] self.gui["idleType"] = "VideoIdle" self.gui["idleGenericURL"] = idleVideoURL self.idle_db["idleInfo"] = { "idleType": "VideoIdle", "idleGenericURL": idleVideoURL } self.idle_db.store() self.gui["setMessage"] = "New Homescreen Set" def handle_idlescreen_first_run(self): # Check If Idle Screen DB Exist and Not Empty # Retrive and Set Idle Screen if Available # If idle unset, get random and store if 'idleInfo' in self.idle_db.keys(): self.gui["idleType"] = self.idle_db["idleInfo"]["idleType"] self.gui["idleGenericURL"] = self.idle_db["idleInfo"][ "idleGenericURL"] else: imageURL = self.generate_random_idle() imageType = imageURL.split('.')[-1] imagePath = join(self.dataPath, str("pixabay-idle" + "." + imageType)) self.extract_image_for_idle(imageURL, imagePath) self.idle_db["idleInfo"] = { "idleType": "ImageIdle", "idleGenericURL": imagePath } self.idle_db.store() self.gui["idleType"] = "ImageIdle" self.gui["idleGenericURL"] = imagePath if 'showTime' in self.idle_config_db.keys(): if self.idle_config_db["showTime"] == True: self.gui["showTime"] = True self.gui[ 'time_string'] = self.dt_skill.get_display_current_time() else: self.gui["showTime"] = False self.gui["time_string"] = "" else: self.gui["showTime"] = False self.gui["time_string"] = "" def generate_random_idle(self): ims = self.image.search(q="galaxy", lang='en', image_type='photo', orientation='vertical', category='all', safesearch='true', order='latest', page=4, per_page=6) randomImageUrl = ims['hits'][4]["largeImageURL"] return randomImageUrl @resting_screen_handler('Pixabay') def handle_idle(self, message): self.gui.clear() self.log.debug('Activating Time/Date resting page') self.handle_idlescreen_first_run() self.gui.show_page('pixabayIdleLoader.qml') def extract_image_for_idle(self, url, localpath): try: image = requests.get(url) except OSError: return False if image.status_code == 200: with open(localpath, "wb") as k: k.write(image.content) else: print("Saving Image Failed") def handle_idle_enable_time(self): self.speak("I am enabling time") self.idle_config_db["showTime"] = True self.gui["showTime"] = True self.idle_config_db.store() # Send Time Data Here First self.handle_idle_update_time() def handle_idle_disable_time(self): self.speak("I am disabling time") self.idle_config_db["showTime"] = False self.gui["showTime"] = False self.idle_config_db.store() def handle_idle_update_time(self): self.gui['time_string'] = self.dt_skill.get_display_current_time() @intent_handler( IntentBuilder("PixabayIdleConfigure").require( "PixabayIdleConfigure").build()) def handle_configure_idle_screen(self): self.gui.show_page("ConfigurePixabayIdle.qml") def handle_remove_configure_idle_screen(self): self.gui.remove_page("ConfigurePixabayIdle.qml") def stop(self): pass
from json_database.exceptions import DatabaseNotCommitted from os.path import exists save_path = "my_dict.conf" my_config = JsonStorage(save_path) my_config["lang"] = "pt" my_config["secondary_lang"] = "en" my_config["email"] = "*****@*****.**" # my_config is a python dict assert isinstance(my_config, dict) # save to file my_config.store() my_config["lang"] = "pt-pt" # revert to previous saved file my_config.reload() assert my_config["lang"] == "pt" # clear all fields my_config.clear() assert my_config == {} # load from a specific path my_config.load_local(save_path)
class YoutubeSkill(MycroftSkill): def __init__(self): super(YoutubeSkill, self).__init__(name="YoutubeSkill") self.nextpage_url = None self.previouspage_url = None self.live_category = None self.recentList = deque() self.recentPageObject = {} self.nextSongList = None self.lastSong = None self.videoPageObject = {} self.isTitle = None self.trendCategoryList = {} self.newsCategoryList = {} self.musicCategoryList = {} self.techCategoryList = {} self.polCategoryList = {} self.gamingCategoryList = {} self.searchCategoryList = {} self.recentCategoryList = {} self.recentWatchListObj = {} self.storeDB = dirname(__file__) + '-recent.db' self.recent_db = JsonStorage(self.storeDB) self.ytkey = base64.b64decode( "QUl6YVN5RE9tSXhSemI0RzFhaXFzYnBaQ3IwQTlFN1NrT0pVRURr") pafy.set_api_key(self.ytkey) self.quackAPIWorker = "J0dvb2dsZWJvdC8yLjEgKCtodHRwOi8vd3d3Lmdvb2dsZS5jb20vYm90Lmh0bWwpJw==" self.quackagent = {'User-Agent': base64.b64decode(self.quackAPIWorker)} self.yts = YoutubeSearcher() def initialize(self): self.load_data_files(dirname(__file__)) self.bus.on('youtube-skill.aiix.home', self.launcherId) youtubepause = IntentBuilder("YoutubePauseKeyword"). \ require("YoutubePauseKeyword").build() self.register_intent(youtubepause, self.youtubepause) youtuberesume = IntentBuilder("YoutubeResumeKeyword"). \ require("YoutubeResumeKeyword").build() self.register_intent(youtuberesume, self.youtuberesume) youtubesearchpage = IntentBuilder("YoutubeSearchPageKeyword"). \ require("YoutubeSearchPageKeyword").build() self.register_intent(youtubesearchpage, self.youtubesearchpage) youtubelauncherId = IntentBuilder("YoutubeLauncherId"). \ require("YoutubeLauncherIdKeyword").build() self.register_intent(youtubelauncherId, self.launcherId) self.add_event('aiix.youtube-skill.playvideo_id', self.play_event) self.gui.register_handler('YoutubeSkill.SearchLive', self.searchLive) self.gui.register_handler('YoutubeSkill.NextPage', self.searchNextPage) self.gui.register_handler('YoutubeSkill.PreviousPage', self.searchPreviousPage) self.gui.register_handler('YoutubeSkill.NextAutoPlaySong', self.nextSongForAutoPlay) self.gui.register_handler('YoutubeSkill.RefreshWatchList', self.refreshWatchList) self.gui.register_handler('YoutubeSkill.ClearDB', self.clear_db) self.gui.register_handler('YoutubeSkill.ReplayLast', self.youtube_repeat_last) def launcherId(self, message): self.show_homepage({}) @intent_file_handler('youtubeopenapp.intent') def launch_home_and_search_category(self, message): self.speak("Loading Up Youtube For You") self.show_homepage({}) def getListSearch(self, text): query = quote(text) url = "https://www.youtube.com/results?search_query=" + quote(query) response = requests.get(url, headers=self.quackagent) html = response.text a_tag = SoupStrainer('a') soup = BeautifulSoup(html, 'html.parser', parse_only=a_tag) for vid in soup.findAll(attrs={'class': 'yt-uix-tile-link'}): if "googleads" not in vid['href'] and not vid['href'].startswith( u"/user") and not vid['href'].startswith(u"/channel"): id = vid['href'].split("v=")[1].split("&")[0] return id def moreRandomListSearch(self, text): LOG.info(text) query = quote(text) try: querySplit = text.split() LOG.info(querySplit) searchQuery = "*," + quote(querySplit[0]) + quote( querySplit[1]) + ",*" except: LOG.info("fail") searchQuery = "*," + quote(query) + ",*" LOG.info(searchQuery) return searchQuery def searchLive(self, message): videoList = [] videoList.clear() videoPageObject = {} try: query = message.data["Query"] LOG.info("I am in search Live") self.searchCategoryList["videoList"] = self.build_category_list( quote(query)) self.gui["searchListBlob"] = self.searchCategoryList self.gui["previousAvailable"] = False self.gui["nextAvailable"] = True self.gui["bgImage"] = quote(query) self.gui.show_page("YoutubeLiveSearch.qml", override_idle=True) except: LOG.debug("error") def searchNextPage(self, message): getCategory = message.data["Category"] LOG.info(getCategory) if getCategory == "News": LOG.info("In Category News") newsAdditionalPages = self.process_additional_pages("news") self.newsCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com" + newsAdditionalPages[0]) self.gui["newsNextAvailable"] = False self.gui["newsListBlob"] = self.newsCategoryList if getCategory == "Music": LOG.info("In Category Music") musicAdditionalPages = self.process_additional_pages("music") self.musicCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com" + musicAdditionalPages[0]) self.gui["musicNextAvailable"] = False self.gui["musicListBlob"] = self.musicCategoryList if getCategory == "Technology": LOG.info("In Category Technology") technologyAdditionalPages = self.process_additional_pages( "technology") self.techCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com" + technologyAdditionalPages[0]) self.gui["techNextAvailable"] = False self.gui["techListBlob"] = self.techCategoryList if getCategory == "Politics": LOG.info("In Category Politics") politicsAdditionalPages = self.process_additional_pages("politics") self.polCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com" + politicsAdditionalPages[0]) self.gui["polNextAvailable"] = False self.gui["polListBlob"] = self.polCategoryList if getCategory == "Gaming": LOG.info("In Category Gaming") gamingAdditionalPages = self.process_additional_pages("gaming") self.gamingCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com" + gamingAdditionalPages[0]) self.gui["gamingNextAvailable"] = False self.gui["gamingListBlob"] = self.gamingCategoryList if getCategory == "Search": LOG.info("In Search") def searchPreviousPage(self, message): getCategory = message.data["Category"] LOG.info(getCategory) if getCategory == "News": LOG.info("In Category News") newsAdditionalPages = self.process_additional_pages("news") self.newsCategoryList[ 'videoList'] = self.build_category_list_from_url( newsAdditionalPages[1]) self.gui["newsNextAvailable"] = True self.gui["newsListBlob"] = self.newsCategoryList if getCategory == "Music": LOG.info("In Category Music") musicAdditionalPages = self.process_additional_pages("music") self.musicCategoryList[ 'videoList'] = self.build_category_list_from_url( musicAdditionalPages[1]) self.gui["musicNextAvailable"] = True self.gui["musicListBlob"] = self.musicCategoryList if getCategory == "Technology": LOG.info("In Category Technology") technologyAdditionalPages = self.process_additional_pages( "technology") self.techCategoryList[ 'videoList'] = self.build_category_list_from_url( technologyAdditionalPages[1]) self.gui["techNextAvailable"] = True self.gui["techListBlob"] = self.techCategoryList if getCategory == "Politics": LOG.info("In Category Politics") politicsAdditionalPages = self.process_additional_pages("politics") self.polCategoryList[ 'videoList'] = self.build_category_list_from_url( politicsAdditionalPages[1]) self.gui["polNextAvailable"] = True self.gui["polListBlob"] = self.polCategoryList if getCategory == "Gaming": LOG.info("In Category Gaming") gamingAdditionalPages = self.process_additional_pages("gaming") self.gamingCategoryList[ 'videoList'] = self.build_category_list_from_url( gamingAdditionalPages[1]) self.gui["gamingNextAvailable"] = True self.gui["gamingListBlob"] = self.gamingCategoryList if getCategory == "Search": LOG.info("In Search") def getTitle(self, text): query = quote(text) url = "https://www.youtube.com/results?search_query=" + quote(query) response = requests.get(url, headers=self.quackagent) html = response.text soup = BeautifulSoup(html) for vid in soup.findAll(attrs={'class': 'yt-uix-tile-link'}): if "googleads" not in vid['href'] and not vid['href'].startswith( u"/user") and not vid['href'].startswith(u"/channel"): videoTitle = vid['title'] return videoTitle @intent_file_handler('youtube.intent') def youtube(self, message): self.stop() self.gui.clear() self.enclosure.display_manager.remove_active() utterance = message.data['videoname'].lower() self.youtube_play_video(utterance) def youtube_play_video(self, utterance): self.gui["setTitle"] = "" self.gui["video"] = "" self.gui["status"] = "stop" self.gui["currenturl"] = "" self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["videoThumb"] = "" url = "https://www.youtube.com/results?search_query=" + quote( utterance) response = requests.get(url, headers=self.quackagent) html = response.text a_tag = SoupStrainer('a') soup = BeautifulSoup(html, 'html.parser', parse_only=a_tag) self.gui["video"] = "" self.gui["status"] = "stop" self.gui["currenturl"] = "" self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["videoThumb"] = "" video_query_str = str(quote(utterance)) print(video_query_str) abc = self.yts.search_youtube(video_query_str, render="videos") vid = abc['videos'][0]['url'] ydl = youtube_dl.YoutubeDL({'outtmpl': '%(id)s%(ext)s'}) with ydl: ytresult = ydl.extract_info( vid, download=False # We just want to extract the info ) if 'entries' in ytresult: ytvideo = ytresult['entries'][0] else: ytvideo = ytresult stream_url = self.process_ytl_stream(ytvideo["formats"]) getvid = vid.split("v=")[1].split("&")[0] thumb = "https://img.youtube.com/vi/{0}/0.jpg".format(getvid) self.gui["videoThumb"] = thumb self.lastSong = vid self.gui["status"] = str("play") self.gui["video"] = str(stream_url) self.gui["currenturl"] = str(vid) self.gui["currenttitle"] = abc['videos'][0]['title'] self.gui["setTitle"] = abc['videos'][0]['title'] self.gui["viewCount"] = abc['videos'][0]['views'] self.gui["publishedDate"] = abc['videos'][0]['published_time'] self.gui["videoAuthor"] = abc['videos'][0]['channel_name'] self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["nextSongBlob"] = "" self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) self.gui["currenttitle"] = self.getTitle(utterance) LOG.info("Video Published On") recentVideoDict = { "videoID": getvid, "videoTitle": abc['videos'][0]['title'], "videoImage": thumb, "videoChannel": abc['videos'][0]['channel_name'], "videoViews": abc['videos'][0]['views'], "videoUploadDate": abc['videos'][0]['published_time'], "videoDuration": abc['videos'][0]['length'] } self.buildHistoryModel(recentVideoDict) self.gui["recentListBlob"] = self.recent_db self.youtubesearchpagesimple(getvid) self.isTitle = abc['videos'][0]['title'] self.gui["recentListBlob"] = self.recent_db def process_ytl_stream(self, streams): _videostreams = [] for z in range(len(streams)): if streams[z].get("vcodec") != "none": if streams[z].get("acodec") != "none": _videostreams.append(streams[z]) for a in range(len(_videostreams)): if _videostreams[a]["format_note"] == "720p": return _videostreams[a]["url"] elif _videostreams[a]["format_note"] == "480p": return _videostreams[a]["url"] elif _videostreams[a]["format_note"] == "360p": return _videostreams[a]["url"] elif _videostreams[a]["format_note"] == "240p": return _videostreams[a]["url"] elif _videostreams[a]["format_note"] == "144p": return _videostreams[a]["url"] def youtubepause(self, message): self.gui["status"] = str("pause") self.gui.show_page("YoutubePlayer.qml") def youtuberesume(self, message): self.gui["status"] = str("play") self.gui.show_page("YoutubePlayer.qml") def youtubesearchpage(self, message): self.stop() videoList = [] videoList.clear() videoPageObject = {} utterance = message.data.get('utterance').lower() utterance = utterance.replace( message.data.get('YoutubeSearchPageKeyword'), '') vid = self.getListSearch(utterance) url = "https://www.youtube.com/results?search_query=" + vid response = requests.get(url, headers=self.quackagent) html = response.text videoList = self.process_soup_additional(html) videoPageObject['videoList'] = videoList self.gui["videoListBlob"] = videoPageObject self.gui["recentListBlob"] = self.recent_db self.gui.show_page("YoutubeSearch.qml") def youtubesearchpagesimple(self, query): LOG.info(query) videoList = [] videoList.clear() videoPageObject = {} yts = YoutubeSearcher() vidslist = yts.watchlist_search(video_id=query) for x in range(len(vidslist['watchlist_videos'])): videoID = vidslist['watchlist_videos'][x]['videoId'] videoTitle = vidslist['watchlist_videos'][x]['title'] videoImage = "https://img.youtube.com/vi/{0}/0.jpg".format(videoID) videoUploadDate = vidslist['watchlist_videos'][x]['published_time'] videoDuration = vidslist['watchlist_videos'][x]['length'] videoViews = vidslist['watchlist_videos'][x]['views'] videoChannel = vidslist['watchlist_videos'][x]['channel_name'] videoList.append({ "videoID": videoID, "videoTitle": videoTitle, "videoImage": videoImage, "videoChannel": videoChannel, "videoViews": videoViews, "videoUploadDate": videoUploadDate, "videoDuration": videoDuration }) videoPageObject['videoList'] = videoList self.gui["videoListBlob"] = videoPageObject self.gui["recentListBlob"] = self.recent_db def show_homepage(self, message): LOG.info("I AM IN HOME PAGE FUNCTION") self.gui.clear() self.enclosure.display_manager.remove_active() self.gui["loadingStatus"] = "" self.gui.show_page("YoutubeLogo.qml") self.process_home_page() def process_home_page(self): LOG.info("I AM IN HOME PROCESS PAGE FUNCTION") self.gui["loadingStatus"] = "Fetching Trends" self.trendCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com/feed/trending") if self.trendCategoryList['videoList']: LOG.info("Trends Not Empty") else: LOG.info("Trying To Rebuild Trends List") self.trendCategoryList[ 'videoList'] = self.build_category_list_from_url( "https://www.youtube.com/feed/trending") self.gui["loadingStatus"] = "Fetching News" self.newsCategoryList['videoList'] = self.build_category_list("news") if self.newsCategoryList['videoList']: LOG.info("News Not Empty") else: LOG.info("Trying To Rebuild News List") self.newsCategoryList['videoList'] = self.build_category_list( "news") self.build_recent_watch_list(20) self.gui.clear() self.enclosure.display_manager.remove_active() self.show_search_page() self.musicCategoryList['videoList'] = self.build_category_list("music") if self.musicCategoryList['videoList']: LOG.info("Music Not Empty") else: LOG.info("Trying To Rebuild Music List") self.musicCategoryList['videoList'] = self.build_category_list( "music") self.gui["musicListBlob"] = self.musicCategoryList self.techCategoryList['videoList'] = self.build_category_list( "technology") if self.techCategoryList['videoList']: LOG.info("Tech Not Empty") else: LOG.info("Trying To Rebuild Tech List") self.techCategoryList['videoList'] = self.build_category_list( "technology") self.gui["techListBlob"] = self.techCategoryList self.polCategoryList['videoList'] = self.build_category_list( "politics") if self.polCategoryList['videoList']: LOG.info("Pol Not Empty") else: LOG.info("Trying To Rebuild Pol List") self.polCategoryList['videoList'] = self.build_category_list( "politics") self.gui["polListBlob"] = self.polCategoryList self.gamingCategoryList['videoList'] = self.build_category_list( "gaming") if self.gamingCategoryList['videoList']: LOG.info("Gaming Not Empty") else: LOG.info("Trying To Rebuild Pol List") self.gamingCategoryList['videoList'] = self.build_category_list( "gaming") self.gui["gamingListBlob"] = self.gamingCategoryList LOG.info("I AM NOW IN REMOVE LOGO PAGE FUNCTION") def show_search_page(self): LOG.info("I AM NOW IN SHOW SEARCH PAGE FUNCTION") LOG.info(self.techCategoryList) self.gui["recentHomeListBlob"] = self.recentWatchListObj self.gui["recentListBlob"] = self.recent_db self.gui["trendListBlob"] = self.trendCategoryList self.gui["newsListBlob"] = self.newsCategoryList self.gui["newsNextAvailable"] = True self.gui["musicListBlob"] = self.musicCategoryList self.gui["musicNextAvailable"] = True self.gui["techListBlob"] = self.techCategoryList self.gui["techNextAvailable"] = True self.gui["polListBlob"] = self.polCategoryList self.gui["polNextAvailable"] = True self.gui["gamingListBlob"] = self.gamingCategoryList self.gui["gamingNextAvailable"] = True self.gui["searchListBlob"] = "" self.gui["previousAvailable"] = False self.gui["nextAvailable"] = True self.gui["bgImage"] = self.live_category self.gui.show_page("YoutubeLiveSearch.qml", override_idle=True) def play_event(self, message): urlvideo = "http://www.youtube.com/watch?v={0}".format( message.data['vidID']) self.lastSong = message.data['vidID'] video = pafy.new(urlvideo) playstream = video.getbest(preftype="mp4", ftypestrict=True) playurl = playstream.url self.speak("Playing") self.gui["video"] = str(playurl) self.gui["status"] = str("play") self.gui["currenturl"] = str(message.data['vidID']) self.gui["currenttitle"] = str(message.data['vidTitle']) self.gui["setTitle"] = video.title self.gui["viewCount"] = video.viewcount self.gui["publishedDate"] = video.published self.gui["videoAuthor"] = video.username self.gui["nextSongBlob"] = "" videoTitleSearch = str(message.data['vidTitle']).join( str(message.data['vidTitle']).split()[:-1]) self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) thumb = "https://img.youtube.com/vi/{0}/maxresdefault.jpg".format( message.data['vidID']) recentVideoDict = { "videoID": message.data['vidID'], "videoTitle": message.data['vidTitle'], "videoImage": message.data['vidImage'], "videoChannel": message.data['vidChannel'], "videoViews": message.data['vidViews'], "videoUploadDate": message.data['vidUploadDate'], "videoDuration": message.data['vidDuration'] } self.buildHistoryModel(recentVideoDict) self.gui["recentListBlob"] = self.recent_db self.isTitle = video.title def stop(self): self.enclosure.bus.emit(Message("metadata", {"type": "stop"})) pass def process_soup(self, htmltype): videoList = [] videoList.clear() soup = BeautifulSoup(htmltype) for vid in soup.findAll(attrs={'class': 'yt-uix-tile-link'}): if "googleads" not in vid['href'] and not vid['href'].startswith( u"/user") and not vid['href'].startswith(u"/channel"): LOG.info(vid) videoID = vid['href'].split("v=")[1].split("&")[0] videoTitle = vid['title'] videoImage = "https://i.ytimg.com/vi/{0}/hqdefault.jpg".format( videoID) videoList.append({ "videoID": videoID, "videoTitle": videoTitle, "videoImage": videoImage }) if len(videoList) > 1: self.nextSongList = videoList[1] else: self.nextSongList = videoList[0] return videoList def process_soup_additional(self, htmltype): videoList = [] videoList.clear() soup = BeautifulSoup(htmltype) getVideoDetails = zip( soup.findAll(attrs={'class': 'yt-uix-tile-link'}), soup.findAll(attrs={'class': 'yt-lockup-byline'}), soup.findAll(attrs={'class': 'yt-lockup-meta-info'}), soup.findAll(attrs={'class': 'video-time'})) for vid in getVideoDetails: if "googleads" not in vid[0][ 'href'] and not vid[0]['href'].startswith( u"/user") and not vid[0]['href'].startswith( u"/channel") and not vid[0]['href'].startswith( '/news') and not vid[0]['href'].startswith( '/music' ) and not vid[0]['href'].startswith( '/technology' ) and not vid[0]['href'].startswith( '/politics' ) and not vid[0]['href'].startswith('/gaming'): videoID = vid[0]['href'].split("v=")[1].split("&")[0] videoTitle = vid[0]['title'] videoImage = "https://i.ytimg.com/vi/{0}/hqdefault.jpg".format( videoID) videoChannel = vid[1].contents[0].string videoUploadDate = vid[2].contents[0].string videoDuration = vid[3].contents[0].string if "watching" in vid[2].contents[0].string: videoViews = "Live" else: try: videoViews = vid[2].contents[1].string except: videoViews = "Playlist" videoList.append({ "videoID": videoID, "videoTitle": videoTitle, "videoImage": videoImage, "videoChannel": videoChannel, "videoViews": videoViews, "videoUploadDate": videoUploadDate, "videoDuration": videoDuration }) return videoList def process_additional_pages(self, category): url = "https://www.youtube.com/results?search_query={0}".format( category) response = requests.get(url, headers=self.quackagent) html = response.text soup = BeautifulSoup(html) buttons = soup.findAll( 'a', attrs={ 'class': "yt-uix-button vve-check yt-uix-sessionlink yt-uix-button-default yt-uix-button-size-default" }) try: nPage = buttons[0]['href'] except: nPage = self.process_additional_pages_fail(category) pPage = url addPgObj = [nPage, pPage] return addPgObj def process_additional_pages_fail(self, category): url = None if category == "news": url = "/results?search_query=world+news" if category == "music": url = "/results?search_query=latest+music" if category == "technology": url = "/results?search_query=latest+tech" if category == "politics": url = "/results?search_query=latest+politics" if category == "gaming": url = "/results?search_query=latest+games" return url def nextSongForAutoPlay(self): self.gui["nextSongBlob"] = self.nextSongList def refreshWatchList(self, message): print("Currently Disabled, Skipping Step") #try: #print("todo") #self.youtubesearchpagesimple(self.lastSong) #except: #self.youtubesearchpagesimple(self.lastSong) @intent_file_handler('youtube-repeat.intent') def youtube_repeat_last(self): video = pafy.new(self.lastSong) thumb = video.thumb playstream = video.streams[0] playurl = playstream.url self.gui["status"] = str("play") self.gui["video"] = str(playurl) self.gui["currenturl"] = "" self.gui["currenttitle"] = video.title self.gui["setTitle"] = video.title self.gui["viewCount"] = video.viewcount self.gui["publishedDate"] = video.published self.gui["videoAuthor"] = video.username self.gui["videoListBlob"] = "" self.gui["recentListBlob"] = "" self.gui["nextSongTitle"] = "" self.gui["nextSongImage"] = "" self.gui["nextSongID"] = "" self.gui.show_pages(["YoutubePlayer.qml", "YoutubeSearch.qml"], 0, override_idle=True) self.youtubesearchpagesimple(self.lastSong) self.isTitle = video.title def build_category_list(self, category): LOG.info("Building For Category" + category) videoList = [] yts = YoutubeSearcher() vidslist = yts.search_youtube(category, render="videos") for x in range(len(vidslist['videos'])): videoID = vidslist['videos'][x]['videoId'] videoTitle = vidslist['videos'][x]['title'] videoImage = vidslist['videos'][x]['thumbnails'][0]['url'] vidImgFix = str(videoImage).split("?")[0] videoUploadDate = vidslist['videos'][x]['published_time'] videoDuration = vidslist['videos'][x]['length'] videoViews = vidslist['videos'][x]['views'] videoChannel = vidslist['videos'][x]['channel_name'] videoList.append({ "videoID": videoID, "videoTitle": videoTitle, "videoImage": vidImgFix, "videoChannel": videoChannel, "videoViews": videoViews, "videoUploadDate": videoUploadDate, "videoDuration": videoDuration }) return videoList def build_category_list_from_url(self, category): videoList = [] yts = YoutubeSearcher() vidslist = yts.page_search(page_type=category) for x in range(len(vidslist['page_videos'])): videoID = vidslist['page_videos'][x]['videoId'] videoTitle = vidslist['page_videos'][x]['title'] videoImage = vidslist['page_videos'][x]['thumbnails'][0]['url'] vidImgFix = str(videoImage).split("?")[0] videoUploadDate = vidslist['page_videos'][x]['published_time'] videoDuration = vidslist['page_videos'][x]['length'] videoViews = vidslist['page_videos'][x]['views'] videoChannel = vidslist['page_videos'][x]['channel_name'] videoList.append({ "videoID": videoID, "videoTitle": videoTitle, "videoImage": vidImgFix, "videoChannel": videoChannel, "videoViews": videoViews, "videoUploadDate": videoUploadDate, "videoDuration": videoDuration }) return videoList def clear_db(self): LOG.info("In DB Clear") self.recent_db.clear() self.recent_db.store() self.gui["recentListBlob"] = "" def buildHistoryModel(self, dictItem): LOG.info("In Build History Model") if 'recentList' in self.recent_db.keys(): myCheck = self.checkIfHistoryItem(dictItem) if myCheck == True: LOG.info("In true") LOG.info(dictItem) self.moveHistoryEntry(dictItem) elif myCheck == False: LOG.info("In false") LOG.info(dictItem) self.addHistoryEntry(dictItem) else: recentListItem = [] recentListItem.insert(0, dictItem) self.recent_db['recentList'] = recentListItem LOG.info("In Build History Recent Not Found Creating") self.recent_db.store() self.build_recent_watch_list(20) self.gui["recentHomeListBlob"] = self.recentWatchListObj def checkIfHistoryItem(self, dictItem): hasHistoryItem = False for dict_ in [ x for x in self.recent_db['recentList'] if x["videoID"] == dictItem["videoID"] ]: hasHistoryItem = True return hasHistoryItem def moveHistoryEntry(self, dictItem): res = [ i for i in self.recent_db['recentList'] if not (i['videoID'] == dictItem["videoID"]) ] self.recent_db['recentList'] = res self.recent_db['recentList'].insert(0, dictItem) self.recent_db.store() self.build_recent_watch_list(20) self.gui["recentHomeListBlob"] = self.recentWatchListObj def addHistoryEntry(self, dictItem): self.recent_db['recentList'].insert(0, dictItem) self.recent_db.store() self.build_recent_watch_list(20) self.gui["recentHomeListBlob"] = self.recentWatchListObj def build_recent_watch_list(self, count): if 'recentList' in self.recent_db.keys(): recentWatchListRaw = self.recent_db['recentList'] recentWatchListModded = recentWatchListRaw[0:count] self.recentWatchListObj['recentList'] = recentWatchListModded else: emptyList = [] self.recentWatchListObj['recentList'] = emptyList def build_upload_date(self, update): now = datetime.datetime.now() + datetime.timedelta(seconds=60 * 3.4) date = dateutil.parser.parse(update) naive = date.replace(tzinfo=None) dtstring = timeago.format(naive, now) return dtstring def add_view_string(self, viewcount): val = viewcount count = re.sub("(\d)(?=(\d{3})+(?!\d))", r"\1,", "%d" % val) views = count + " views" LOG.info(views) return views def process_soup_watchlist(self, html): videoList = [] videoList.clear() soup = BeautifulSoup(html) currentVideoSection = soup.find('div', attrs={'class': 'watch-sidebar'}) getVideoDetails = zip( currentVideoSection.findAll(attrs={'class': 'yt-uix-sessionlink'}), currentVideoSection.findAll(attrs={'class': 'attribution'}), currentVideoSection.findAll( attrs={'class': 'yt-uix-simple-thumb-wrap'}), currentVideoSection.findAll(attrs={'class': 'video-time'}), currentVideoSection.findAll(attrs={'class': 'view-count'})) for vid in getVideoDetails: if "googleads" not in vid[0][ 'href'] and not vid[0]['href'].startswith( u"/user") and not vid[0]['href'].startswith( u"/channel") and not vid[0]['href'].startswith( '/news') and not vid[0]['href'].startswith( '/music' ) and not vid[0]['href'].startswith( '/technology' ) and not vid[0]['href'].startswith( '/politics' ) and not vid[0]['href'].startswith( '/gaming') and "title" in vid[0].attrs: videoID = vid[0]['href'].split("v=")[1].split("&")[0] videoTitle = vid[0]['title'] videoImage = "https://i.ytimg.com/vi/{0}/hqdefault.jpg".format( videoID) videoChannel = vid[1].contents[0].string videoUploadDate = " " videoDuration = vid[3].contents[0].string videoViews = vid[4].text videoList.append({ "videoID": videoID, "videoTitle": videoTitle, "videoImage": videoImage, "videoChannel": videoChannel, "videoViews": videoViews, "videoUploadDate": videoUploadDate, "videoDuration": videoDuration }) return videoList
class OVOSGuiControl(MycroftSkill): """ The OVOSGuiControl skill handles much of the gui activities related to Mycroft's core functionality. This includes showing "speaking" faces as well as more complicated things such as switching to the selected resting face and handling system signals. # TODO move most things to enclosure / HAL. Only voice interaction should reside in the Skill. """ def __init__(self): super().__init__("OVOSGuiControl") self.settings["auto_brightness"] = False self.settings["use_listening_beep"] = True self.has_show_page = False # resets with each handler self.override_animations = False self.resting_screen = None self.auto_brightness = None def initialize(self): """Perform initalization. Registers messagebus handlers and sets default gui values. """ self.resting_screen = RestingScreen(self.bus, self.gui, self.log, self.settings) self.brightness_dict = self.translate_namedvalues("brightness.levels") self.gui["volume"] = 0 # Prepare GUI Viseme structure self.gui["viseme"] = {"start": 0, "visemes": []} store_conf = join(self.file_system.path, 'skill_conf.json') if not self.file_system.exists("skill_conf.json"): self.skill_conf = JsonStorage(store_conf) self.skill_conf["selected_backend"] = "unknown" self.skill_conf.store() else: self.skill_conf = JsonStorage(store_conf) try: # Handle network connection events self.add_event("mycroft.internet.connected", self.handle_internet_connected) # Handle the 'busy' visual self.bus.on("mycroft.skill.handler.start", self.on_handler_started) self.bus.on("recognizer_loop:sleep", self.on_handler_sleep) self.bus.on("mycroft.awoken", self.on_handler_awoken) self.bus.on("enclosure.mouth.reset", self.on_handler_mouth_reset) self.bus.on("recognizer_loop:audio_output_end", self.on_handler_mouth_reset) self.bus.on("enclosure.mouth.viseme_list", self.on_handler_speaking) self.bus.on("gui.page.show", self.on_gui_page_show) self.bus.on("gui.page_interaction", self.on_gui_page_interaction) self.bus.on("mycroft.skills.initialized", self.reset_face) self.bus.on("ovos.pairing.process.completed", self.start_homescreen_process) self.bus.on("ovos.pairing.set.backend", self.set_backend_type) self.bus.on("mycroft.mark2.register_idle", self.resting_screen.on_register) self.add_event("mycroft.mark2.reset_idle", self.resting_screen.restore) # TODO move resting screen to Enclosure # TODO consolidate bus message format # - this message is set to be consistent with a handler below. self.add_event("mycroft.device.show.idle", self.resting_screen.show) # Handle device settings events self.add_event("mycroft.device.settings", self.handle_device_settings) # Handle GUI release events self.add_event("mycroft.gui.screen.close", self.handle_remove_namespace) # Use Legacy for QuickSetting delegate self.gui.register_handler("mycroft.device.settings", self.handle_device_settings) self.gui.register_handler("mycroft.device.settings.homescreen", self.handle_device_homescreen_settings) self.gui.register_handler('mycroft.device.settings.ssh', self.handle_device_ssh_settings) self.gui.register_handler("mycroft.device.settings.restart", self.handle_device_restart_action) self.gui.register_handler("mycroft.device.settings.poweroff", self.handle_device_poweroff_action) self.gui.register_handler("mycroft.device.show.idle", self.resting_screen.show) # Handle idle selection self.gui.register_handler("mycroft.device.set.idle", self.resting_screen.set) # System events self.add_event("system.reboot", self.handle_system_reboot) self.add_event("system.shutdown", self.handle_system_shutdown) # Show loading screen while starting up skills. # self.gui['state'] = 'loading' # self.gui.show_page('all.qml') # Collect Idle screens and display if skill is restarted self.device_paired = is_paired() self.device_backend = self.skill_conf["selected_backend"] if not self.device_backend == "local": if self.device_paired: self.resting_screen.collect() else: self.resting_screen.collect() self.bus.emit(Message("ovos.shell.status.ok")) except Exception: LOG.exception("In OVOSGuiControl Skill") # Update use of wake-up beep self._sync_wake_beep_setting() self.settings_change_callback = self.on_websettings_changed ################################################################### # System events def handle_system_reboot(self, _): self.speak_dialog("rebooting", wait=True) subprocess.call(["/usr/bin/systemctl", "reboot"]) def handle_system_shutdown(self, _): subprocess.call(["/usr/bin/systemctl", "poweroff"]) def handle_remove_namespace(self, message): self.log.info("Got Clear Namespace Event In Mark 2 Skill") get_skill_namespace = message.data.get("skill_id", "") if get_skill_namespace: self.bus.emit( Message("gui.clear.namespace", {"__from": get_skill_namespace})) self.resting_screen.cancel_override() self.cancel_scheduled_event("IdleCheck") ################################################################### # Idle screen mechanism def set_backend_type(self, message): backend = message.data.get("backend", "unknown") if not backend == "unknown": self.skill_conf["selected_backend"] = backend self.skill_conf.store() self.device_backend = self.skill_conf["selected_backend"] def start_homescreen_process(self, _): self.device_paired = is_paired() self.resting_screen.collect() def reset_face(self, _): """Triggered after skills are initialized. Sets switches from resting "face" to a registered resting screen. """ time.sleep(1) if self.device_paired or self.device_backend == "local": self.resting_screen.collect() def stop(self, _=None): """Clear override_idle and stop visemes.""" self.log.debug("Stop received") self.resting_screen.stop() self.gui["viseme"] = {"start": 0, "visemes": []} return False def shutdown(self): """Cleanly shutdown the Skill removing any manual event handlers""" # Gotta clean up manually since not using add_event() self.bus.remove("mycroft.skill.handler.start", self.on_handler_started) self.bus.remove("recognizer_loop:sleep", self.on_handler_sleep) self.bus.remove("mycroft.awoken", self.on_handler_awoken) self.bus.remove("enclosure.mouth.reset", self.on_handler_mouth_reset) self.bus.remove("recognizer_loop:audio_output_end", self.on_handler_mouth_reset) self.bus.remove("enclosure.mouth.viseme_list", self.on_handler_speaking) self.bus.remove("gui.page.show", self.on_gui_page_show) self.bus.remove("gui.page_interaction", self.on_gui_page_interaction) self.bus.remove("mycroft.mark2.register_idle", self.resting_screen.on_register) self.bus.remove("ovos.pairing.process.completed", self.start_homescreen_process) self.bus.remove("ovos.pairing.set.backend", self.set_backend_type) ##################################################################### # Manage "busy" visual def on_handler_started(self, message): handler = message.data.get("handler", "") # Ignoring handlers from this skill and from the background clock if "OVOSGuiControl" in handler: return if "TimeSkill.update_display" in handler: return def on_gui_page_interaction(self, _): """ Reset idle timer to 30 seconds when page is flipped. """ self.log.debug("Resetting idle counter to 30 seconds") self.start_idle_event(30) def on_gui_page_show(self, message): self.log.info(message.data.get("__from", "")) if "skill-ovos-mycroftgui" not in message.data.get("__from", ""): # Some skill other than the handler is showing a page self.has_show_page = True # If a skill overrides the animations do not show any override_animations = message.data.get("__animations", False) if override_animations: # Disable animations self.log.debug("Disabling all animations for page") self.override_animations = True else: self.log.debug("Displaying all animations for page") self.override_animations = False # If a skill overrides the idle do not switch page override_idle = message.data.get("__idle") if override_idle is True: # Disable idle screen self.log.debug("Cancelling Idle screen") self.cancel_idle_event() self.resting_screen.override(message) elif isinstance(override_idle, int) and override_idle is not False: self.log.info("Overriding idle timer to" " {} seconds".format(override_idle)) self.resting_screen.override(None) self.start_idle_event(override_idle) elif message.data["page"] and not message.data["page"][0].endswith( "idle.qml"): # Check if the show_page deactivates a previous idle override # This is only possible if the page is from the same skill self.log.info("Cancelling idle override") if override_idle is False and compare_origin( message, self.resting_screen.override_idle[0]): # Remove the idle override page if override is set to false self.resting_screen.cancel_override() # Set default idle screen timer self.start_idle_event(30) def on_handler_mouth_reset(self, _): """ Restore viseme to a smile. """ pass def on_handler_sleep(self, _): """ Show resting face when going to sleep. """ self.gui["state"] = "resting" self.gui.show_page("all.qml") def on_handler_awoken(self, _): """ Show awake face when sleep ends. """ self.gui["state"] = "awake" self.gui.show_page("all.qml") def on_handler_complete(self, message): """ When a skill finishes executing clear the showing page state. """ handler = message.data.get("handler", "") # Ignoring handlers from this skill and from the background clock if "OVOSGuiControl" in handler: return if "TimeSkill.update_display" in handler: return self.has_show_page = False try: if self.hourglass_info[handler] == -1: self.enclosure.reset() del self.hourglass_info[handler] except Exception: # There is a slim chance the self.hourglass_info might not # be populated if this skill reloads at just the right time # so that it misses the mycroft.skill.handler.start but # catches the mycroft.skill.handler.complete pass ##################################################################### # Manage "speaking" visual def on_handler_speaking(self, message): """Show the speaking page if no skill has registered a page to be shown in it's place. """ if self.device_paired or self.device_backend == "local": self.gui["viseme"] = message.data if not self.has_show_page: self.gui["state"] = "speaking" self.gui.show_page("all.qml") # Show idle screen after the visemes are done (+ 2 sec). viseme_time = message.data["visemes"][-1][1] + 5 self.start_idle_event(viseme_time) ##################################################################### # Manage resting screen visual state def cancel_idle_event(self): """Cancel the event monitoring current system idle time.""" self.resting_screen.next = 0 self.cancel_scheduled_event("IdleCheck") def start_idle_event(self, offset=60, weak=False): """Start an event for showing the idle screen. Arguments: offset: How long until the idle screen should be shown weak: set to true if the time should be able to be overridden """ with self.resting_screen.lock: if time.monotonic() + offset < self.resting_screen.next: self.log.info("No update, before next time") return self.log.debug("Starting idle event") try: if not weak: self.resting_screen.next = time.monotonic() + offset # Clear any existing checker self.cancel_scheduled_event("IdleCheck") time.sleep(0.5) self.schedule_event(self.resting_screen.show, int(offset), name="IdleCheck") self.log.debug("Showing idle screen in " "{} seconds".format(offset)) except Exception as e: self.log.exception(repr(e)) ##################################################################### # Manage network def handle_internet_connected(self, _): """ System came online later after booting. """ self.enclosure.mouth_reset() ##################################################################### # Web settings def on_websettings_changed(self): """ Update use of wake-up beep. """ self._sync_wake_beep_setting() def _sync_wake_beep_setting(self): """ Update "use beep" global config from skill settings. """ config = Configuration.get() use_beep = self.settings.get("use_listening_beep", False) if not config["confirm_listening"] == use_beep: # Update local (user) configuration setting new_config = {"confirm_listening": use_beep} user_config = LocalConf(USER_CONFIG) user_config.merge(new_config) user_config.store() self.bus.emit(Message("configuration.updated")) ##################################################################### # Brightness intent interaction def percent_to_level(self, percent): """Converts the brigtness value from percentage to a value the Arduino can read Arguments: percent (int): interger value from 0 to 100 return: (int): value form 0 to 30 """ return int(float(percent) / float(100) * 30) def parse_brightness(self, brightness): """Parse text for brightness percentage. Arguments: brightness (str): string containing brightness level Returns: (int): brightness as percentage (0-100) """ try: # Handle "full", etc. name = normalize(brightness) if name in self.brightness_dict: return self.brightness_dict[name] if "%" in brightness: brightness = brightness.replace("%", "").strip() return int(brightness) if "percent" in brightness: brightness = brightness.replace("percent", "").strip() return int(brightness) i = int(brightness) if i < 0 or i > 100: return None if i < 30: # Assmume plain 0-30 is "level" return int((i * 100.0) / 30.0) # Assume plain 31-100 is "percentage" return i except Exception: return None # failed in an int() conversion def set_screen_brightness(self, level, speak=True): """Actually change screen brightness. Arguments: level (int): 0-30, brightness level speak (bool): when True, speak a confirmation """ # TODO CHANGE THE BRIGHTNESS if speak: percent = int(float(level) * float(100) / float(30)) self.speak_dialog("brightness.set", data={"val": str(percent) + "%"}) def _set_brightness(self, brightness): # brightness can be a number or word like "full", "half" percent = self.parse_brightness(brightness) if percent is None: self.speak_dialog("brightness.not.found.final") elif int(percent) == -1: self.handle_auto_brightness(None) else: self.auto_brightness = False self.set_screen_brightness(self.percent_to_level(percent)) @intent_handler("brightness.intent") def handle_brightness(self, message): """Intent handler to set custom screen brightness. Arguments: message (dict): messagebus message from intent parser """ brightness = message.data.get( "brightness", None) or self.get_response("brightness.not.found") if brightness: self._set_brightness(brightness) def _get_auto_time(self): """Get dawn, sunrise, noon, sunset, and dusk time. Returns: times (dict): dict with associated (datetime, level) """ tz_code = self.location["timezone"]["code"] lat = self.location["coordinate"]["latitude"] lon = self.location["coordinate"]["longitude"] ast_loc = astral.Location() ast_loc.timezone = tz_code ast_loc.lattitude = lat ast_loc.longitude = lon user_set_tz = timezone(tz_code).localize(datetime.now()).strftime("%Z") device_tz = time.tzname if user_set_tz in device_tz: sunrise = ast_loc.sun()["sunrise"] noon = ast_loc.sun()["noon"] sunset = ast_loc.sun()["sunset"] else: secs = int(self.location["timezone"]["offset"]) / -1000 sunrise = (arrow.get(ast_loc.sun()["sunrise"]).shift( seconds=secs).replace(tzinfo="UTC").datetime) noon = (arrow.get(ast_loc.sun()["noon"]).shift( seconds=secs).replace(tzinfo="UTC").datetime) sunset = (arrow.get(ast_loc.sun()["sunset"]).shift( seconds=secs).replace(tzinfo="UTC").datetime) return { "Sunrise": (sunrise, 20), # high "Noon": (noon, 30), # full "Sunset": (sunset, 5), # dim } def schedule_brightness(self, time_of_day, pair): """Schedule auto brightness with the event scheduler. Arguments: time_of_day (str): Sunrise, Noon, Sunset pair (tuple): (datetime, brightness) """ d_time = pair[0] brightness = pair[1] now = arrow.now() arw_d_time = arrow.get(d_time) data = (time_of_day, brightness) if now.timestamp > arw_d_time.timestamp: d_time = arrow.get(d_time).shift(hours=+24) self.schedule_event( self._handle_screen_brightness_event, d_time, data=data, name=time_of_day, ) else: self.schedule_event( self._handle_screen_brightness_event, d_time, data=data, name=time_of_day, ) @intent_handler("brightness.auto.intent") def handle_auto_brightness(self, _): """brightness varies depending on time of day Arguments: message (Message): messagebus message from intent parser """ self.auto_brightness = True auto_time = self._get_auto_time() nearest_time_to_now = (float("inf"), None, None) for time_of_day, pair in auto_time.items(): self.schedule_brightness(time_of_day, pair) now = arrow.now().timestamp timestamp = arrow.get(pair[0]).timestamp if abs(now - timestamp) < nearest_time_to_now[0]: nearest_time_to_now = (abs(now - timestamp), pair[1], time_of_day) self.set_screen_brightness(nearest_time_to_now[1], speak=False) def _handle_screen_brightness_event(self, message): """Wrapper for setting screen brightness from eventscheduler Arguments: message (Message): messagebus message """ if self.auto_brightness: time_of_day = message.data[0] level = message.data[1] self.cancel_scheduled_event(time_of_day) self.set_screen_brightness(level, speak=False) pair = self._get_auto_time()[time_of_day] self.schedule_brightness(time_of_day, pair) ##################################################################### # Device Settings @intent_handler("device.settings.intent") def handle_device_settings(self, message): """ Display device settings page. """ self.gui["state"] = "settings/settingspage" self.gui.show_page("all.qml") @intent_handler("device.homescreen.settings.intent") def handle_device_homescreen_settings(self, message): """ display homescreen settings page """ screens = [{ "screenName": s, "screenID": self.resting_screen.screens[s] } for s in self.resting_screen.screens] self.gui["idleScreenList"] = {"screenBlob": screens} self.gui["selectedScreen"] = self.gui["selected"] self.gui["state"] = "settings/homescreen_settings" self.gui.show_page("all.qml") @intent_handler('device.ssh.settings.intent') def handle_device_ssh_settings(self, message): """ Display ssh settings page. """ self.gui['state'] = 'settings/ssh_settings' self.gui.show_page('all.qml') def handle_device_set_ssh(self, message): """ Set ssh settings """ enable_ssh = message.data.get("enable_ssh", False) if enable_ssh: ssh_enable() elif not enable_ssh: ssh_disable() def handle_device_restart_action(self, message): """ Device restart action. """ self.log.info("Going Down For Restart") system_reboot() def handle_device_poweroff_action(self, message): """ Device poweroff action. """ self.log.info("Powering Off") system_shutdown()