error messages now have style bold red

error highlights are now yellow

normal highlights are now green

_validate_scene_name_and_item_name renamed to _validate_sources

its now a normal function and not a decorator

it also returns bool instead of raising typer.Exit()

patch bump
This commit is contained in:
onyx-and-iris 2025-06-06 20:55:35 +01:00
parent ddb92bb317
commit 81518a14ea
2 changed files with 92 additions and 96 deletions

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2025-present onyx-and-iris <code@onyxandiris.online> # SPDX-FileCopyrightText: 2025-present onyx-and-iris <code@onyxandiris.online>
# #
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
__version__ = "0.16.4" __version__ = "0.16.5"

View File

@ -1,6 +1,5 @@
"""module containing commands for manipulating items in scenes.""" """module containing commands for manipulating items in scenes."""
from collections.abc import Callable
from typing import Annotated, Optional from typing import Annotated, Optional
import obsws_python as obsws import obsws_python as obsws
@ -13,7 +12,7 @@ from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console() out_console = Console()
err_console = Console(stderr=True) err_console = Console(stderr=True, style='bold red')
@app.callback() @app.callback()
@ -37,7 +36,7 @@ def list_(
scene_name = ctx.obj.get_current_program_scene().scene_name scene_name = ctx.obj.get_current_program_scene().scene_name
if not validate.scene_in_scenes(ctx, scene_name): if not validate.scene_in_scenes(ctx, scene_name):
err_console.print(f"Scene '{scene_name}' not found.") err_console.print(f'Scene [yellow]{scene_name}[/yellow] not found.')
raise typer.Exit(1) raise typer.Exit(1)
resp = ctx.obj.get_scene_item_list(scene_name) resp = ctx.obj.get_scene_item_list(scene_name)
@ -55,7 +54,7 @@ def list_(
) )
if not items: if not items:
out_console.print(f"No items found in scene '{scene_name}'.") out_console.print(f'No items found in scene [green]{scene_name}[/green].')
raise typer.Exit() raise typer.Exit()
table = Table(title=f'Items in Scene: {scene_name}', padding=(0, 2)) table = Table(title=f'Items in Scene: {scene_name}', padding=(0, 2))
@ -98,37 +97,31 @@ def list_(
out_console.print(table) out_console.print(table)
def _validate_scene_name_and_item_name( def _validate_sources(
func: Callable,
):
"""Validate the scene name and item name."""
def wrapper(
ctx: typer.Context, ctx: typer.Context,
scene_name: str, scene_name: str,
item_name: str, item_name: str,
group: Optional[str] = None, group: Optional[str] = None,
): ) -> bool:
"""Validate the scene name and item name."""
if not validate.scene_in_scenes(ctx, scene_name): if not validate.scene_in_scenes(ctx, scene_name):
err_console.print(f"Scene '{scene_name}' not found.") err_console.print(f'Scene [yellow]{scene_name}[/yellow] not found.')
raise typer.Exit(1) return False
if group: if group:
if not validate.item_in_scene_item_list(ctx, scene_name, group): if not validate.item_in_scene_item_list(ctx, scene_name, group):
err_console.print( err_console.print(
f"Parent group '{group}' not found in scene '{scene_name}'." f'Group [yellow]{group}[/yellow] not found in scene [yellow]{scene_name}[/yellow].'
) )
raise typer.Exit(1) return False
else: else:
if not validate.item_in_scene_item_list(ctx, scene_name, item_name): if not validate.item_in_scene_item_list(ctx, scene_name, item_name):
err_console.print( err_console.print(
f"Item '{item_name}' not found in scene '{scene_name}'." f'Item [yellow]{item_name}[/yellow] not found in scene [yellow]{scene_name}[/yellow].'
) )
raise typer.Exit(1) return False
return func(ctx, scene_name, item_name, group) return True
return wrapper
def _get_scene_name_and_item_id( def _get_scene_name_and_item_id(
@ -143,7 +136,9 @@ def _get_scene_name_and_item_id(
scene_item_id = item.get('sceneItemId') scene_item_id = item.get('sceneItemId')
break break
else: else:
err_console.print(f"Item '{item_name}' not found in group '{group}'.") err_console.print(
f'Item [yellow]{item_name}[/yellow] not found in group [yellow]{group}[/yellow].'
)
raise typer.Exit(1) raise typer.Exit(1)
else: else:
try: try:
@ -151,9 +146,9 @@ def _get_scene_name_and_item_id(
except obsws.error.OBSSDKRequestError as e: except obsws.error.OBSSDKRequestError as e:
if e.code == 600: if e.code == 600:
err_console.print( err_console.print(
f"Item '{item_name}' not found in scene '{scene_name}'. Is the item in a group? " f'Item [yellow]{item_name}[/yellow] not found in scene [yellow]{scene_name}[/yellow]. Is the item in a group? '
'If so use the --group option to specify the parent group. ' 'If so use the --group option to specify the parent group. '
'See `obsws-cli sceneitem list` for a list of items in the scene.' 'Use `obsws-cli sceneitem list` for a list of items in the scene.'
) )
raise typer.Exit(1) raise typer.Exit(1)
else: else:
@ -163,17 +158,23 @@ def _get_scene_name_and_item_id(
return scene_name, scene_item_id return scene_name, scene_item_id
@_validate_scene_name_and_item_name
@app.command('show | sh') @app.command('show | sh')
def show( def show(
ctx: typer.Context, ctx: typer.Context,
scene_name: Annotated[str, typer.Argument(..., help='Scene name the item is in')], scene_name: Annotated[
str, typer.Argument(..., show_default=False, help='Scene name the item is in')
],
item_name: Annotated[ item_name: Annotated[
str, typer.Argument(..., help='Item name to show in the scene') str,
typer.Argument(..., show_default=False, help='Item name to show in the scene'),
], ],
group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None, group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None,
): ):
"""Show an item in a scene.""" """Show an item in a scene."""
if not _validate_sources(ctx, scene_name, item_name, group):
raise typer.Exit(1)
old_scene_name = scene_name
scene_name, scene_item_id = _get_scene_name_and_item_id( scene_name, scene_item_id = _get_scene_name_and_item_id(
ctx, scene_name, item_name, group ctx, scene_name, item_name, group
) )
@ -186,27 +187,35 @@ def show(
if group: if group:
out_console.print( out_console.print(
f"Item '{item_name}' in group '{group}' in scene '{scene_name}' has been shown." f'Item [green]{item_name}[/green] in group [green]{group}[/green] in scene [green]{old_scene_name}[/green] has been shown.'
) )
else: else:
# If not in a parent group, just show the scene name # If not in a parent group, just show the scene name
# This is to avoid confusion with the parent group name # This is to avoid confusion with the parent group name
# which is not the same as the scene name # which is not the same as the scene name
# and is not needed in this case # and is not needed in this case
out_console.print(f"Item '{item_name}' in scene '{scene_name}' has been shown.") out_console.print(
f'Item [green]{item_name}[/green] in scene [green]{scene_name}[/green] has been shown.'
)
@_validate_scene_name_and_item_name
@app.command('hide | h') @app.command('hide | h')
def hide( def hide(
ctx: typer.Context, ctx: typer.Context,
scene_name: Annotated[str, typer.Argument(..., help='Scene name the item is in')], scene_name: Annotated[
str, typer.Argument(..., show_default=False, help='Scene name the item is in')
],
item_name: Annotated[ item_name: Annotated[
str, typer.Argument(..., help='Item name to hide in the scene') str,
typer.Argument(..., show_default=False, help='Item name to hide in the scene'),
], ],
group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None, group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None,
): ):
"""Hide an item in a scene.""" """Hide an item in a scene."""
if not _validate_sources(ctx, scene_name, item_name, group):
raise typer.Exit(1)
old_scene_name = scene_name
scene_name, scene_item_id = _get_scene_name_and_item_id( scene_name, scene_item_id = _get_scene_name_and_item_id(
ctx, scene_name, item_name, group ctx, scene_name, item_name, group
) )
@ -219,7 +228,7 @@ def hide(
if group: if group:
out_console.print( out_console.print(
f"Item '{item_name}' in group '{group}' in scene '{scene_name}' has been hidden." f'Item [green]{item_name}[/green] in group [green]{group}[/green] in scene [green]{old_scene_name}[/green] has been hidden.'
) )
else: else:
# If not in a parent group, just show the scene name # If not in a parent group, just show the scene name
@ -227,36 +236,29 @@ def hide(
# which is not the same as the scene name # which is not the same as the scene name
# and is not needed in this case # and is not needed in this case
out_console.print( out_console.print(
f"Item '{item_name}' in scene '{scene_name}' has been hidden." f'Item [green]{item_name}[/green] in scene [green]{scene_name}[/green] has been hidden.'
) )
@_validate_scene_name_and_item_name
@app.command('toggle | tg') @app.command('toggle | tg')
def toggle( def toggle(
ctx: typer.Context, ctx: typer.Context,
scene_name: Annotated[str, typer.Argument(..., help='Scene name the item is in')], scene_name: Annotated[
str, typer.Argument(..., show_default=False, help='Scene name the item is in')
],
item_name: Annotated[ item_name: Annotated[
str, typer.Argument(..., help='Item name to toggle in the scene') str,
typer.Argument(
..., show_default=False, help='Item name to toggle in the scene'
),
], ],
group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None, group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None,
): ):
"""Toggle an item in a scene.""" """Toggle an item in a scene."""
if not validate.scene_in_scenes(ctx, scene_name): if not _validate_sources(ctx, scene_name, item_name, group):
err_console.print(f"Scene '{scene_name}' not found.")
raise typer.Exit(1)
if group:
if not validate.item_in_scene_item_list(ctx, scene_name, group):
err_console.print(
f"Parent group '{group}' not found in scene '{scene_name}'."
)
raise typer.Exit(1)
else:
if not validate.item_in_scene_item_list(ctx, scene_name, item_name):
err_console.print(f"Item '{item_name}' not found in scene '{scene_name}'.")
raise typer.Exit(1) raise typer.Exit(1)
old_scene_name = scene_name
scene_name, scene_item_id = _get_scene_name_and_item_id( scene_name, scene_item_id = _get_scene_name_and_item_id(
ctx, scene_name, item_name, group ctx, scene_name, item_name, group
) )
@ -276,11 +278,11 @@ def toggle(
if group: if group:
if new_state: if new_state:
out_console.print( out_console.print(
f"Item '{item_name}' in group '{group}' in scene '{scene_name}' has been shown." f'Item [green]{item_name}[/green] in group [green]{group}[/green] in scene [green]{old_scene_name}[/green] has been shown.'
) )
else: else:
out_console.print( out_console.print(
f"Item '{item_name}' in group '{group}' in scene '{scene_name}' has been hidden." f'Item [green]{item_name}[/green] in group [green]{group}[/green] in scene [green]{old_scene_name}[/green] has been hidden.'
) )
else: else:
# If not in a parent group, just show the scene name # If not in a parent group, just show the scene name
@ -289,34 +291,30 @@ def toggle(
# and is not needed in this case # and is not needed in this case
if new_state: if new_state:
out_console.print( out_console.print(
f"Item '{item_name}' in scene '{scene_name}' has been shown." f'Item [green]{item_name}[/green] in scene [green]{scene_name}[/green] has been shown.'
) )
else: else:
out_console.print( out_console.print(
f"Item '{item_name}' in scene '{scene_name}' has been hidden." f'Item [green]{item_name}[/green] in scene [green]{scene_name}[/green] has been hidden.'
) )
@_validate_scene_name_and_item_name
@app.command('visible | v') @app.command('visible | v')
def visible( def visible(
ctx: typer.Context, ctx: typer.Context,
scene_name: Annotated[str, typer.Argument(..., help='Scene name the item is in')], scene_name: Annotated[
str, typer.Argument(..., show_default=False, help='Scene name the item is in')
],
item_name: Annotated[ item_name: Annotated[
str, typer.Argument(..., help='Item name to check visibility in the scene') str,
typer.Argument(
..., show_default=False, help='Item name to check visibility in the scene'
),
], ],
group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None, group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None,
): ):
"""Check if an item in a scene is visible.""" """Check if an item in a scene is visible."""
if group: if not _validate_sources(ctx, scene_name, item_name, group):
if not validate.item_in_scene_item_list(ctx, scene_name, group):
err_console.print(
f"Parent group '{group}' not found in scene '{scene_name}'."
)
raise typer.Exit(1)
else:
if not validate.item_in_scene_item_list(ctx, scene_name, item_name):
err_console.print(f"Item '{item_name}' not found in scene '{scene_name}'.")
raise typer.Exit(1) raise typer.Exit(1)
old_scene_name = scene_name old_scene_name = scene_name
@ -331,7 +329,7 @@ def visible(
if group: if group:
out_console.print( out_console.print(
f"Item '{item_name}' in group '{group}' in scene '{old_scene_name}' is currently {'visible' if enabled.scene_item_enabled else 'hidden'}." f'Item [green]{item_name}[/green] in group [green]{group}[/green] in scene [green]{old_scene_name}[/green] is currently {"visible" if enabled.scene_item_enabled else "hidden"}.'
) )
else: else:
# If not in a parent group, just show the scene name # If not in a parent group, just show the scene name
@ -339,16 +337,22 @@ def visible(
# which is not the same as the scene name # which is not the same as the scene name
# and is not needed in this case # and is not needed in this case
out_console.print( out_console.print(
f"Item '{item_name}' in scene '{scene_name}' is currently {'visible' if enabled.scene_item_enabled else 'hidden'}." f'Item [green]{item_name}[/green] in scene [green]{scene_name}[/green] is currently {"visible" if enabled.scene_item_enabled else "hidden"}.'
) )
@_validate_scene_name_and_item_name
@app.command('transform | t') @app.command('transform | t')
def transform( def transform(
ctx: typer.Context, ctx: typer.Context,
scene_name: str, scene_name: Annotated[
item_name: str, str, typer.Argument(..., show_default=False, help='Scene name the item is in')
],
item_name: Annotated[
str,
typer.Argument(
..., show_default=False, help='Item name to transform in the scene'
),
],
group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None, group: Annotated[Optional[str], typer.Option(help='Parent group name')] = None,
alignment: Annotated[ alignment: Annotated[
Optional[int], typer.Option(help='Alignment of the item in the scene') Optional[int], typer.Option(help='Alignment of the item in the scene')
@ -397,15 +401,7 @@ def transform(
] = None, ] = None,
): ):
"""Set the transform of an item in a scene.""" """Set the transform of an item in a scene."""
if group: if not _validate_sources(ctx, scene_name, item_name, group):
if not validate.item_in_scene_item_list(ctx, scene_name, group):
err_console.print(
f"Parent group '{group}' not found in scene '{scene_name}'."
)
raise typer.Exit(1)
else:
if not validate.item_in_scene_item_list(ctx, scene_name, item_name):
err_console.print(f"Item '{item_name}' not found in scene '{scene_name}'.")
raise typer.Exit(1) raise typer.Exit(1)
old_scene_name = scene_name old_scene_name = scene_name
@ -457,7 +453,7 @@ def transform(
if group: if group:
out_console.print( out_console.print(
f"Item '{item_name}' in group '{group}' in scene '{old_scene_name}' has been transformed." f'Item [green]{item_name}[/green] in group [green]{group}[/green] in scene [green]{old_scene_name}[/green] has been transformed.'
) )
else: else:
# If not in a parent group, just show the scene name # If not in a parent group, just show the scene name
@ -465,5 +461,5 @@ def transform(
# which is not the same as the scene name # which is not the same as the scene name
# and is not needed in this case # and is not needed in this case
out_console.print( out_console.print(
f"Item '{item_name}' in scene '{scene_name}' has been transformed." f'Item [green]{item_name}[/green] in scene [green]{scene_name}[/green] has been transformed.'
) )