From d2a2be60f0dc9f3cfa5292b36e7ebf2c0ec27213 Mon Sep 17 00:00:00 2001 From: Peder Bergebakken Sundt Date: Sun, 4 Mar 2018 04:08:20 +0100 Subject: [PATCH] Make webui more responsive, add playlist metadata using youtube-dl --- grzegorz_clients/api.py | 5 ++-- grzegorz_clients/remi_ui.py | 59 +++++++++++++++++++++++++++++-------- grzegorz_clients/utils.py | 38 ++++++++++++++++++++---- 3 files changed, 82 insertions(+), 20 deletions(-) diff --git a/grzegorz_clients/api.py b/grzegorz_clients/api.py index c60120f..4702c41 100644 --- a/grzegorz_clients/api.py +++ b/grzegorz_clients/api.py @@ -17,6 +17,7 @@ def request_post(func): @wraps(func) def new_func(*args, **kwargs): url, data = func(*args, **kwargs) + if type(data) is dict: data = json.dumps(data) response = requests.post(f"{BASE_URL}/{url}", data=data) data = json.loads(response.text) if "error" not in data or data["error"] != False: @@ -38,9 +39,9 @@ def request_get(func): # methods: @request_post -def load_path(path:str): +def load_path(path:str, data:dict=None): args = urlencode(locals()) - return f"load?{args}", None + return f"load?{args}", data @request_get def is_playing(): diff --git a/grzegorz_clients/remi_ui.py b/grzegorz_clients/remi_ui.py index f6723b1..2f1719d 100644 --- a/grzegorz_clients/remi_ui.py +++ b/grzegorz_clients/remi_ui.py @@ -8,6 +8,8 @@ from . import api #globals: COLOR_BLUE = "rgb(33, 150, 243)" COLOR_BLUE_SHADOW = "rgba(33, 150, 243, 0.75)" +COLOR_LIGHT_BLUE = "#e3f2fd" +COLOR_GRAY = "#212529" WIDTH = 512 class RemiApp(App): @@ -110,16 +112,24 @@ class RemiApp(App): value = self.input.field.get_text() self.input.field.set_text("") - # (TODO): - # title, length = utils.get_youtube_metadata(value) - api.load_path(value) + self.input.field.set_enabled(False) + self.input.submit.set_enabled(False) + try: + data = get_youtube_metadata(value) + finally: + self.input.field.set_enabled(True) + self.input.submit.set_enabled(True) + + api.load_path(value, data) @call_as_thread def change_seek(self, widget, value): api.seek_percent(value) @call_as_thread def change_volume(self, widget, value): api.set_volume(value) - + def on_table_row_click(self, row_widget, playlist_item): + print(playlist_item) + # playback steps: @call_as_thread def playback_update(self, times_called=[0]): @@ -127,11 +137,15 @@ class RemiApp(App): self.set_playing(is_playing) if is_playing: - playback_pos = api.get_playback_pos() - playback_pos = playback_pos["current"] / playback_pos["total"] * 100 - if self.playback.seek_slider.get_value() != playback_pos: - self.playback.seek_slider.set_value(playback_pos) - + try: + playback_pos = api.get_playback_pos() + except api.APIError: + playback_pos = None + if playback_pos: + slider_pos = playback_pos["current"] / playback_pos["total"] * 100 + if self.playback.seek_slider.get_value() != slider_pos: + self.playback.seek_slider.set_value(slider_pos) + if times_called[0] % 5 == 0: volume = api.get_volume() if volume > 100: volume = 100 @@ -147,15 +161,34 @@ class RemiApp(App): table = [] for item in playlist: + name = playlist_item["filename"] + length = "--:--" + if "data" in playlist_item: + if "title" in playlist_item["data"]: + name = playlist_item["data"]["title"] + if "length" in playlist_item["data"]: + length = playlist_item["data"]["length"] + table.append([ - item["index"], - item["filename"], - "05:00" + ("#"*("current" in item)) + playlist_item["index"], + name, + length, ]) self.playlist.table.empty(keep_title=True) self.playlist.table.append_from_list(table) - + + for row_widget, playlist_item in zip( + map(self.playlist.table.get_child, self.playlist.table._render_children_list[1:]), + playlist): + if "current" in playlist_item: + row_widget.style["background-color"] = COLOR_LIGHT_BLUE + else: + row_widget.style["color"] = "#444"#COLOR_GRAY + row_widget.set_on_click_listener(self.on_table_row_click, playlist_item) + for item_widget in map(row_widget.get_child, row_widget._render_children_list): + pass + #helpers def set_playing(self, is_playing:bool): self.playback.play.set_text('' if is_playing else '') diff --git a/grzegorz_clients/utils.py b/grzegorz_clients/utils.py index 9f8a5aa..68b3658 100644 --- a/grzegorz_clients/utils.py +++ b/grzegorz_clients/utils.py @@ -1,21 +1,49 @@ from functools import wraps +from urllib.parse import urlsplit, urlunsplit, parse_qs, urlencode import threading import youtube_dl +from youtube_dl.utils import DownloadError class Namespace(object): pass -def get_youtube_metadata(url, ydl = youtube_dl.YoutubeDL()): - #todo: check if url is valid +def filter_query_params(url, allowed=[]): + split_url = urlsplit(url) - #todo, stop it from doung the whole playlist - resp = ydl.extract_info(url, download=False) + qs = parse_qs(split_url.query) + print(qs) + for key in list(qs.keys()): + if key not in allowed: + del qs[key] + + return urlunsplit(( + split_url.scheme, + split_url.netloc, + split_url.path, + urlencode(qs, doseq=True), + split_url.fragment, + )) + +def get_youtube_metadata(url, ydl = youtube_dl.YoutubeDL()): + if urlsplit(url).netloc.lower() in ("www.youtube.com", "youtube.com", "youtub.be"): + #Stop it from doing the whole playlist + url = filter_query_params(url, allowed=["v"]) + + print(url) + + try: + resp = ydl.extract_info(url, download=False) + except DownloadError: + return None #print resp.keys() title = resp.get('title') length = resp.get('duration') #print( title, "%i:%.2i" % (length//60, length%60)) - return title, "%i:%.2i" % (length//60, length%60) + return {"title":title, "length":seconds_to_timestamp(length)} + +def seconds_to_timestamp(s): + return "%i:%.2i" % (s//60, s%60) # decorator: def call_as_thread(func): # This will discard any return value!