File indexing completed on 2024-04-28 09:19:47
0001 import json 0002 import timeago, datetime 0003 import dateutil.parser 0004 import peertube 0005 from youtube_dl import YoutubeDL 0006 from peertube.rest import ApiException 0007 from mycroft.skills.core import MycroftSkill, intent_handler, intent_file_handler 0008 from mycroft.messagebus.message import Message 0009 from mycroft.util.log import LOG 0010 from xdg import XDG_CACHE_HOME, XDG_DATA_HOME 0011 from json_database import JsonStorage 0012 0013 __author__ = 'aix' 0014 0015 class PeerTubeSkill(MycroftSkill): 0016 def __init__(self): 0017 super(PeerTubeSkill, self).__init__(name="PeerTubeSkill") 0018 self.default_host = "https://peertube.co.uk/api/v1" 0019 self.configured_host = None 0020 cache_path = str(XDG_CACHE_HOME.absolute()) 0021 if cache_path: 0022 self.peer_instance_config = JsonStorage(cache_path + "/peertube-instance-config.conf") 0023 else: 0024 self.peer_instance_config = JsonStorage(str(self.root_dir).rsplit("/", 1)[0] + "peertube-instance-config.conf") 0025 0026 0027 if "model" in self.peer_instance_config: 0028 self.peer_instance_host = self.peer_instance_config["model"] 0029 self.configured_host = self.peer_instance_config["model"]["hosturl"] 0030 else: 0031 self.peer_instance_host = None 0032 0033 if self.configured_host is not None: 0034 self.peer_config = peertube.Configuration( 0035 host = self.configured_host) 0036 else: 0037 self.peer_config = peertube.Configuration( 0038 host = self.default_host) 0039 0040 self.api_client = peertube.ApiClient(configuration=self.peer_config) 0041 self.search_instance = peertube.SearchApi(self.api_client) 0042 self.video_instance = peertube.VideoApi(self.api_client) 0043 0044 def initialize(self): 0045 self.bus.on('peertube-skill.aiix.home', self.display_homepage) 0046 self.gui.register_handler('PeerTube.SearchQuery', self.search_page_query) 0047 self.gui.register_handler('PeerTube.WatchVideo', self.watch_video_via_query) 0048 self.gui.register_handler('PeerTube.SettingsPage', self.peer_settings_page) 0049 self.gui.register_handler('PeerTube.ConfigureHost', self.peer_configure_host) 0050 0051 @intent_file_handler("PtOpenApp.intent") 0052 def display_homepage(self, message): 0053 self.gui.clear() 0054 self.gui.show_page("PeerTubeLoading.qml", override_idle=True, override_animations=True) 0055 self.build_categories() 0056 0057 def search_videos(self, query): 0058 # Global search for queries with PT api 0059 # JSONIFY return results 0060 try: 0061 search_pt = self.search_instance.search_videos_get(query) 0062 search_response = json.loads(json.dumps(search_pt.to_dict(), default=self.dt_converter)) 0063 return search_response 0064 0065 except ApiException as e: 0066 self.speak_dialog("Search.Failed", data={'query': message.data.get("search_query")}) 0067 0068 def search_page_query(self, message): 0069 # Receive query from GUI and return results for search 0070 user_query = message.data.get("search_query") 0071 get_response = self.search_videos(user_query) 0072 self.gui["search_results"] = get_response 0073 self.gui["search_completed"] = True 0074 0075 def build_categories(self): 0076 # 1: Music, 2: Films, 3: Vehicles, 4: Art, 5: Sports, 6: Travels, 7: Gaming, 8: People, 0077 # 9: Comedy, 10: Entertainment, 11: News & Politics, 12: How To, 13: Education, 14: Activism, 0078 # 15: Science & Technology 0079 0080 try: 0081 # Build only five categories 0082 news_category_list = self.video_instance.videos_get(category_one_of=11, count=10) 0083 music_category_list = self.video_instance.videos_get(category_one_of=1, count=10) 0084 entertainment_category_list = self.video_instance.videos_get(category_one_of=10, count=10) 0085 technology_category_list = self.video_instance.videos_get(category_one_of=15, count=10) 0086 gaming_category_list = self.video_instance.videos_get(category_one_of=7, count=10) 0087 0088 LOG.info(json.dumps(news_category_list.to_dict(), default=self.dt_converter)) 0089 0090 self.gui["news_category_list"] = json.loads(json.dumps(news_category_list.to_dict(), default=self.dt_converter)) 0091 self.gui["music_category_list"] = json.loads(json.dumps(music_category_list.to_dict(), default=self.dt_converter)) 0092 self.gui["entertainment_category_list"] = json.loads(json.dumps(entertainment_category_list.to_dict(), default=self.dt_converter)) 0093 self.gui["technology_category_list"] = json.loads(json.dumps(technology_category_list.to_dict(), default=self.dt_converter)) 0094 self.gui["gaming_category_list"] = json.loads(json.dumps(gaming_category_list.to_dict(), default=self.dt_converter)) 0095 self.gui["search_results"] = "" 0096 self.gui["search_completed"] = True 0097 0098 self.gui.remove_page("PeerTubeLoading.qml") 0099 self.gui.show_page("PeerTubeHomePage.qml", override_idle=True, override_animations=True) 0100 0101 except ApiException as e: 0102 self.gui.show_page("PeerTubeErrorPage.qml") 0103 self.speak_dialog("build_category_failed") 0104 0105 def watch_video_via_query(self, message): 0106 # Event invoked by interaction on homepage video tiles 0107 # We already have all the video listing data required 0108 video_idx0_host = message.data["account"]["host"] 0109 video_idx0_embed = message.data["embed_path"] 0110 video_embed_link = "https://" + video_idx0_host + video_idx0_embed 0111 try: 0112 # First run get static video url via youtube-dl as pt video api is unstable 0113 video_stream_link = self.process_stream(video_embed_link) 0114 if video_stream_link == False: 0115 # Try getting the static link manually from embedded object 0116 video_stream_link = str("https://" + video_idx0_host + "/static/webseed/" 0117 + message.data["uuid"] + "-480.mp4") 0118 0119 except ApiException as e: 0120 self.speak_dialog("search_failed") 0121 0122 self.gui["video_meta"] = message.data 0123 self.gui["video_stream"] = video_stream_link 0124 self.gui["video_status"] = "play" 0125 self.gui.show_page("PeerTubePlayer.qml", override_idle=True, override_animations=True) 0126 0127 @intent_file_handler("PtVoiceQuery.intent") 0128 def watch_video_via_voice(self, message): 0129 # Event invoked via voice query, triggered from anywhere 0130 # We don't have the video listing data required 0131 user_query = message.data['query'] 0132 print(user_query) 0133 videos_matched = self.search_videos(user_query) 0134 print(videos_matched) 0135 video_idx0_host = videos_matched['data'][0]["account"]["host"] 0136 video_idx0_embed = videos_matched['data'][0]["embed_path"] 0137 video_embed_link = "https://" + video_idx0_host + video_idx0_embed 0138 try: 0139 # First run get static video url via youtube-dl as pt video api is unstable 0140 video_stream_link = self.process_stream(video_embed_link) 0141 if video_stream_link == False: 0142 # Try getting the static link manually from embedded object 0143 video_stream_link = str("https://" + video_idx0_host + "/static/webseed/" 0144 + videos_matched['data'][0]["uuid"] + "-480.mp4") 0145 0146 except ApiException as e: 0147 self.speak_dialog("search_failed") 0148 0149 self.gui["video_meta"] = videos_matched['data'][0] 0150 self.gui["video_stream"] = video_stream_link 0151 self.gui["video_status"] = "play" 0152 self.gui.show_page("PeerTubePlayer.qml", override_idle=True, override_animations=True) 0153 0154 def peer_settings_page(self, message): 0155 if message.data.get("settings_open") == True: 0156 if self.configured_host is not None: 0157 self.gui["current_instance"] = self.configured_host 0158 else: 0159 self.gui["current_instance"] = self.default_host 0160 0161 instance_local_model = self.list_instances() 0162 self.gui["instances_model"] = instance_local_model 0163 gen_idx = [] 0164 for x in range(len(instance_local_model)): 0165 gen_idx.append(instance_local_model[x]["hosturl"]) 0166 0167 self.gui["instance_string_model"] = gen_idx 0168 self.gui.show_page("PeerTubeSettings.qml", override_idle=True, override_animations=True) 0169 elif message.data.get("settings_open") == False: 0170 self.gui.remove_page("PeerTubeSettings.qml") 0171 0172 def peer_configure_host(self, message): 0173 new_instance = message.data.get('selected_instance') 0174 self.peer_instance_config["model"] = {"hosturl": new_instance} 0175 self.peer_instance_config.store() 0176 self.configured_host = new_instance 0177 self.peer_config = peertube.Configuration( 0178 host = self.configured_host) 0179 self.api_client = peertube.ApiClient(configuration=self.peer_config) 0180 self.search_instance = peertube.SearchApi(self.api_client) 0181 self.video_instance = peertube.VideoApi(self.api_client) 0182 self.clean_built_models() 0183 0184 def process_stream(self, embedded_url): 0185 # First round of processing to extract video stream 0186 ydl = YoutubeDL() 0187 try: 0188 ydl_results = ydl.extract_info(embedded_url, download=False) 0189 return ydl_results['url'] 0190 except: 0191 return False 0192 0193 def dt_converter(self, o): 0194 if isinstance(o, datetime.datetime): 0195 return o.__str__() 0196 0197 def list_instances(self): 0198 instance_listing = [ 0199 {"hostname": "Peertube UK Instance", "hosturl": "https://peertube.co.uk/api/v1"}, 0200 {"hostname": "Peertube Austrian Instance", "hosturl": "https://peertube.at/api/v1"}, 0201 {"hostname": "Peertube Scandinavian Instance", "hosturl": "https://peertube.dk/api/v1"}, 0202 {"hostname": "Peertube European Instance", "hosturl": "https://peertube.be/api/v1"}, 0203 {"hostname": "Peertube Italian Instance", "hosturl": "https://peertube.uno/api/v1"} 0204 ] 0205 return instance_listing 0206 0207 def clean_built_models(self): 0208 self.gui["news_category_list"] = "" 0209 self.gui["music_category_list"] = "" 0210 self.gui["entertainment_category_list"] = "" 0211 self.gui["technology_category_list"] = "" 0212 self.gui["gaming_category_list"] = "" 0213 self.gui["search_results"] = "" 0214 self.gui["search_completed"] = True 0215 self.display_homepage({}) 0216 0217 def create_skill(): 0218 return PeerTubeSkill()