mirror of
https://github.com/onyx-and-iris/slobs-cli.git
synced 2025-08-07 20:21:55 +00:00
Compare commits
No commits in common. "23d3118e6acac10506d2b1990aa1fb3e725df234" and "f282f86e72dead74b416903f1f7804cb4d016ca4" have entirely different histories.
23d3118e6a
...
f282f86e72
@ -5,14 +5,6 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
# [0.10.0] - 2025-06-13
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- scene commands are prone to raise ProtocolErrors if called too quickly in succession. To make these errors a little more user friendly the following changes have been made:
|
|
||||||
- They print error messages to stderr
|
|
||||||
- They return exit code 2
|
|
||||||
|
|
||||||
# [0.9.0] - 2025-06-12
|
# [0.9.0] - 2025-06-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
"""module for package metadata."""
|
"""module for package metadata."""
|
||||||
|
|
||||||
__version__ = '0.10.0'
|
__version__ = '0.9.3'
|
||||||
|
@ -86,8 +86,8 @@ async def mute(ctx: click.Context, source_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@audio.command()
|
@audio.command()
|
||||||
@ -117,8 +117,8 @@ async def unmute(ctx: click.Context, source_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@audio.command()
|
@audio.command()
|
||||||
@ -151,8 +151,8 @@ async def toggle(ctx: click.Context, source_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@audio.command()
|
@audio.command()
|
||||||
@ -182,5 +182,5 @@ async def status(ctx: click.Context, source_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""module for custom exceptions in Slobs CLI."""
|
"""module for custom exceptions in Slobs CLI."""
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
import asyncclick as click
|
import asyncclick as click
|
||||||
|
|
||||||
|
|
||||||
@ -16,31 +14,3 @@ class SlobsCliError(click.ClickException):
|
|||||||
def show(self):
|
def show(self):
|
||||||
"""Display the error message in red."""
|
"""Display the error message in red."""
|
||||||
click.secho(f'Error: {self.message}', fg='red', err=True)
|
click.secho(f'Error: {self.message}', fg='red', err=True)
|
||||||
|
|
||||||
|
|
||||||
class SlobsCliProtocolError(SlobsCliError):
|
|
||||||
"""Converts pyslobs ProtocolError to a SlobsCliProtocolError."""
|
|
||||||
|
|
||||||
def __init__(self, message: str):
|
|
||||||
"""Initialize the SlobsCliProtocolError with a message."""
|
|
||||||
protocol_message_to_dict = json.loads(
|
|
||||||
str(message).replace('"', '\\"').replace("'", '"')
|
|
||||||
)
|
|
||||||
super().__init__(
|
|
||||||
protocol_message_to_dict.get('message', 'Unable to parse error message')
|
|
||||||
)
|
|
||||||
self.exit_code = 2
|
|
||||||
self.protocol_code = protocol_message_to_dict.get('code', 'Unknown error code')
|
|
||||||
|
|
||||||
def show(self):
|
|
||||||
"""Display the protocol error message in red."""
|
|
||||||
match self.protocol_code:
|
|
||||||
case -32600:
|
|
||||||
click.secho(
|
|
||||||
'Oops! Looks like we hit a rate limit for this command. Please try again later.',
|
|
||||||
fg='red',
|
|
||||||
err=True,
|
|
||||||
)
|
|
||||||
case _:
|
|
||||||
# Fall back to the base error display for unknown protocol codes
|
|
||||||
super().show()
|
|
||||||
|
@ -38,8 +38,8 @@ async def start(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@record.command()
|
@record.command()
|
||||||
@ -67,8 +67,8 @@ async def stop(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@record.command()
|
@record.command()
|
||||||
|
@ -37,8 +37,8 @@ async def start(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@replaybuffer.command()
|
@replaybuffer.command()
|
||||||
@ -65,8 +65,8 @@ async def stop(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@replaybuffer.command()
|
@replaybuffer.command()
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
import asyncclick as click
|
import asyncclick as click
|
||||||
from anyio import create_task_group
|
from anyio import create_task_group
|
||||||
from pyslobs import ProtocolError, ScenesService, TransitionsService
|
from pyslobs import ScenesService, TransitionsService
|
||||||
from terminaltables3 import AsciiTable
|
from terminaltables3 import AsciiTable
|
||||||
|
|
||||||
from .cli import cli
|
from .cli import cli
|
||||||
from .errors import SlobsCliError, SlobsCliProtocolError
|
from .errors import SlobsCliError
|
||||||
|
|
||||||
|
|
||||||
@cli.group()
|
@cli.group()
|
||||||
@ -56,14 +56,9 @@ async def list(ctx: click.Context, id: bool = False):
|
|||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
try:
|
async with create_task_group() as tg:
|
||||||
async with create_task_group() as tg:
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(_run)
|
||||||
tg.start_soon(_run)
|
|
||||||
except* ProtocolError as excgroup:
|
|
||||||
p_error = next(iter(excgroup.exceptions))
|
|
||||||
raisable = SlobsCliProtocolError(str(p_error))
|
|
||||||
raise raisable
|
|
||||||
|
|
||||||
|
|
||||||
@scene.command()
|
@scene.command()
|
||||||
@ -82,14 +77,9 @@ async def current(ctx: click.Context, id: bool = False):
|
|||||||
)
|
)
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
try:
|
async with create_task_group() as tg:
|
||||||
async with create_task_group() as tg:
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(_run)
|
||||||
tg.start_soon(_run)
|
|
||||||
except* ProtocolError as excgroup:
|
|
||||||
p_error = next(iter(excgroup.exceptions))
|
|
||||||
raisable = SlobsCliProtocolError(str(p_error))
|
|
||||||
raise raisable
|
|
||||||
|
|
||||||
|
|
||||||
@scene.command()
|
@scene.command()
|
||||||
@ -148,16 +138,12 @@ async def switch(
|
|||||||
break
|
break
|
||||||
else: # If no scene by the given name was found
|
else: # If no scene by the given name was found
|
||||||
conn.close()
|
conn.close()
|
||||||
raise SlobsCliError(f'Scene "{scene_name}" not found.')
|
raise SlobsCliError(f"Scene '{scene_name}' not found.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with create_task_group() as tg:
|
async with create_task_group() as tg:
|
||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
except* ProtocolError as excgroup:
|
|
||||||
p_error = next(iter(excgroup.exceptions))
|
|
||||||
raisable = SlobsCliProtocolError(str(p_error))
|
|
||||||
raise raisable
|
|
||||||
|
@ -88,8 +88,8 @@ async def load(ctx: click.Context, scenecollection_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@scenecollection.command()
|
@scenecollection.command()
|
||||||
@ -136,8 +136,8 @@ async def delete(ctx: click.Context, scenecollection_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@scenecollection.command()
|
@scenecollection.command()
|
||||||
@ -169,5 +169,5 @@ async def rename(ctx: click.Context, scenecollection_name: str, new_name: str):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
@ -37,8 +37,8 @@ async def start(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@stream.command()
|
@stream.command()
|
||||||
@ -65,8 +65,8 @@ async def stop(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@stream.command()
|
@stream.command()
|
||||||
|
@ -35,8 +35,8 @@ async def enable(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@studiomode.command()
|
@studiomode.command()
|
||||||
@ -61,8 +61,8 @@ async def disable(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
|
||||||
|
|
||||||
@studiomode.command()
|
@studiomode.command()
|
||||||
@ -129,5 +129,5 @@ async def force_transition(ctx: click.Context):
|
|||||||
tg.start_soon(conn.background_processing)
|
tg.start_soon(conn.background_processing)
|
||||||
tg.start_soon(_run)
|
tg.start_soon(_run)
|
||||||
except* SlobsCliError as excgroup:
|
except* SlobsCliError as excgroup:
|
||||||
raisable = next(iter(excgroup.exceptions))
|
for e in excgroup.exceptions:
|
||||||
raise raisable
|
raise e
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Test cases for scene commands in slobs_cli."""
|
"""Test cases for scene commands in slobs_cli."""
|
||||||
|
|
||||||
import anyio
|
|
||||||
import pytest
|
import pytest
|
||||||
from asyncclick.testing import CliRunner
|
from asyncclick.testing import CliRunner
|
||||||
|
|
||||||
@ -16,7 +15,6 @@ async def test_scene_list():
|
|||||||
assert 'slobs-test-scene-1' in result.output
|
assert 'slobs-test-scene-1' in result.output
|
||||||
assert 'slobs-test-scene-2' in result.output
|
assert 'slobs-test-scene-2' in result.output
|
||||||
assert 'slobs-test-scene-3' in result.output
|
assert 'slobs-test-scene-3' in result.output
|
||||||
await anyio.sleep(0.2) # Avoid rate limiting issues
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
@ -25,19 +23,7 @@ async def test_scene_current():
|
|||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
result = await runner.invoke(cli, ['scene', 'switch', 'slobs-test-scene-2'])
|
result = await runner.invoke(cli, ['scene', 'switch', 'slobs-test-scene-2'])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
await anyio.sleep(0.2) # Avoid rate limiting issues
|
|
||||||
|
|
||||||
result = await runner.invoke(cli, ['scene', 'current'])
|
result = await runner.invoke(cli, ['scene', 'current'])
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert 'Current active scene: slobs-test-scene-2' in result.output
|
assert 'Current active scene: slobs-test-scene-2' in result.output
|
||||||
await anyio.sleep(0.2) # Avoid rate limiting issues
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
|
||||||
async def test_scene_invalid_switch():
|
|
||||||
"""Test switching to an invalid scene."""
|
|
||||||
runner = CliRunner()
|
|
||||||
result = await runner.invoke(cli, ['scene', 'switch', 'invalid-scene'])
|
|
||||||
assert result.exit_code != 0
|
|
||||||
assert 'Scene "invalid-scene" not found' in result.output
|
|
||||||
await anyio.sleep(0.2) # Avoid rate limiting issues
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user