From 3059898e38a25ba63f0838a2e7b224a2631e55ab Mon Sep 17 00:00:00 2001 From: h7x4 Date: Fri, 17 May 2024 23:33:29 +0200 Subject: [PATCH 1/2] Improve error messages produced by http decorators --- grzegorz_clients/api.py | 77 +++++++++++++++++++++++++++++++---------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/grzegorz_clients/api.py b/grzegorz_clients/api.py index a4b8100..a926ce5 100644 --- a/grzegorz_clients/api.py +++ b/grzegorz_clients/api.py @@ -11,43 +11,84 @@ def set_endpoint(base_url:str): # Exceptions: class APIError(Exception): pass +def parse_message( + method: str, + url: str, + status: str, + function_name: str, + json_text: str +) -> dict[str, any]: + prefix = f"[{function_name}] {method} /{url} -> {status}:" + try: + data = json.loads(json_text) + except json.JSONDecodeError: + raise APIError(f"{prefix} Expected json response, got:\n{json_text}") + + if type(data) is not dict: + raise APIError(f"{prefix} Expected json response to be a dict, got:\n{json_text}") + + if "error" not in data: + raise APIError(f"{prefix} Missing json data 'error', got:\n{json_text}") + + if data["error"] != False: + raise APIError(f"{prefix} Got error {str(data['error'])}, got:\n{json_text}") + + if "success" not in data: + raise APIError(f"{prefix} Missing json data 'error', got:\n{json_text}") + + return data + # decorator: # (TODO): Add logging def request_delete(func): @wraps(func) def new_func(*args, **kwargs): url, data = func(*args, **kwargs) - if type(data) is dict: data = json.dumps(data) - response = requests.delete(f"{BASE_URL}/{url}", data=data) - response.raise_for_status() # raises HTTPError of any - data = json.loads(response.text) - if "error" not in data or data["error"] != False: - print(data) - raise APIError(data["error"]) + response = requests.delete(f"{BASE_URL}/{url}", json=data) + response.raise_for_status() # raises HTTPError, if any + data = parse_message( + "DELETE", + url, + response.status_code, + func.__name__, + response.text, + ) return data["success"] return new_func + 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) - response.raise_for_status() # raises HTTPError of any - data = json.loads(response.text) - if "error" not in data or data["error"] != False: - print(data) - raise APIError(data["error"]) + response = requests.post(f"{BASE_URL}/{url}", json=data) + response.raise_for_status() # raises HTTPError, if any + data = parse_message( + "POST", + url, + response.status_code, + func.__name__, + response.text, + ) return data["success"] return new_func + def request_get(func): @wraps(func) def new_func(*args, **kwargs): url = func(*args, **kwargs) response = requests.get(f"{BASE_URL}/{url}") - response.raise_for_status() # raises HTTPError of any - data = json.loads(response.text) - if "error" not in data or data["error"] != False: - raise APIError(data["errortext"]) + response.raise_for_status() # raises HTTPError, if any + data = parse_message( + "GET", + url, + response.status_code, + func.__name__, + response.text, + ) + + if "value" not in data: + raise APIError(f"[{func.__name__}] Missing json data 'value', got:\n{json.dumps(data)}") + return data["value"] return new_func -- 2.44.2 From a9e8330898767fb01ae9a8f65564ce3bf1e61699 Mon Sep 17 00:00:00 2001 From: Peder Bergebakken Sundt Date: Sat, 18 May 2024 00:10:26 +0200 Subject: [PATCH 2/2] Rework grzegorzctl interface --- grzegorz_clients/cli.py | 57 +++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/grzegorz_clients/cli.py b/grzegorz_clients/cli.py index 1fe332f..1912084 100644 --- a/grzegorz_clients/cli.py +++ b/grzegorz_clients/cli.py @@ -31,37 +31,62 @@ def print_json(obj): cli = typer.Typer(no_args_is_help=True) - -@cli.command(help="Add one ore more items to the playlist. [--play, --now]") -def add( - urls: list[str], - play: bool = False, - now: bool = False, - api_base: str = DEFAULT_API_BASE, +def _add( + urls : list[str], + put_pre : bool = False, + put_post : bool = False, + play : bool = False, + api_base : str = DEFAULT_API_BASE, ): api.set_endpoint(api_base) - if now: + if put_pre or put_post: pre = api.get_playlist() for url in urls: resp = api.load_path(url) rich.print(f"{url} : {resp!r}", file=sys.stderr) - if now: + if put_pre or put_post: + assert put_pre != put_post post = api.get_playlist() current_index, = [i.get("index", -1) for i in post if i.get("current", False)][:1] or [0] old_indices = set(i.get("index", -1) for i in pre) new_indices = set(i.get("index", -1) for i in post) - old_indices assert all(i > current_index for i in new_indices) - target = current_index - if not play: target += 1 + target = current_index if put_pre else current_index + 1 if target not in new_indices: for idx in sorted(new_indices): api.playlist_move(idx, target) target += 1 if play: - if now: api.playlist_goto(current_index) + assert put_pre or put_post + api.playlist_goto(current_index if put_pre else current_index + 1) + api.set_playing(True) + +@cli.command(help="Add one ore more items to the playlist") +def play( + urls: list[str], + pre: bool = False, + api_base: str = DEFAULT_API_BASE, +): + _add(urls, put_post=not pre, put_pre=pre, play=True, api_base=api_base) + +@cli.command(help="Add one ore more items to the playlist") +def next( + urls: list[str], + api_base: str = DEFAULT_API_BASE, +): + _add(urls, put_post=True, api_base=api_base) + +@cli.command(help="Add one ore more items to the playlist") +def queue( + urls: list[str], + play: bool = True, + api_base: str = DEFAULT_API_BASE, +): + _add(urls, api_base=api_base) + if play: api.set_playing(True) @cli.command(name="list", help="List the current playlist") @@ -71,8 +96,8 @@ def list_( api.set_endpoint(api_base) print_json(api.get_playlist()) -@cli.command(help="Set Playing") -def play( api_base: str = DEFAULT_API_BASE ): +@cli.command(help="Set Playing to True") +def resume( api_base: str = DEFAULT_API_BASE ): api.set_endpoint(api_base) # TODO: add logic to seek to start of song if at end of song AND at end of playlist? rich.print(api.set_playing(True), file=sys.stderr) @@ -83,7 +108,7 @@ def pause( api_base: str = DEFAULT_API_BASE ): rich.print(api.set_playing(False), file=sys.stderr) @cli.command(help="Goto next item in playlist") -def next( api_base: str = DEFAULT_API_BASE ): +def skip( api_base: str = DEFAULT_API_BASE ): api.set_endpoint(api_base) rich.print(api.playlist_next(), file=sys.stderr) @@ -129,7 +154,7 @@ def status( @cli.command(help="Set the playback volume") def set_volume( - volume: float, + volume: int, api_base: str = DEFAULT_API_BASE, ): api.set_endpoint(api_base) -- 2.44.2