From 8aa1fb2c091af31fc536a9f294c2286c7e242807 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sat, 10 Jan 2026 13:59:55 +0000 Subject: [PATCH] add validate.timecode_format add None checks for callbacks with optional values --- obsws_cli/group.py | 2 +- obsws_cli/media.py | 6 ++++-- obsws_cli/validate.py | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/obsws_cli/group.py b/obsws_cli/group.py index 8ac2073..f83ef3a 100644 --- a/obsws_cli/group.py +++ b/obsws_cli/group.py @@ -31,7 +31,7 @@ def list_( ] = None, ): """List groups in a scene.""" - if not scene_name: + if scene_name is None: scene_name = ctx.obj['obsws'].get_current_program_scene().scene_name resp = ctx.obj['obsws'].get_scene_item_list(scene_name) diff --git a/obsws_cli/media.py b/obsws_cli/media.py index 656f38c..d53c11a 100644 --- a/obsws_cli/media.py +++ b/obsws_cli/media.py @@ -4,7 +4,7 @@ from typing import Annotated, Optional import typer -from . import console, util +from . import console, util, validate from .alias import SubTyperAliasGroup app = typer.Typer(cls=SubTyperAliasGroup) @@ -24,7 +24,9 @@ def cursor( timecode: Annotated[ Optional[str], typer.Argument( - ..., help='The timecode to set the cursor to (format: HH:MM:SS).' + ..., + help='The timecode to set the cursor to (format: HH:MM:SS).', + callback=validate.timecode_format, ), ] = None, ): diff --git a/obsws_cli/validate.py b/obsws_cli/validate.py index b4cdc67..2ed18fc 100644 --- a/obsws_cli/validate.py +++ b/obsws_cli/validate.py @@ -1,5 +1,7 @@ """module containing validation functions.""" +from typing import Optional + import typer from . import console @@ -26,8 +28,11 @@ def input_not_in_inputs(ctx: typer.Context, input_name: str) -> bool: return input_name -def scene_in_scenes(ctx: typer.Context, scene_name: str) -> str: +def scene_in_scenes(ctx: typer.Context, scene_name: Optional[str]) -> str | None: """Check if a scene exists in the list of scenes.""" + if scene_name is None: + return scene_name + resp = ctx.obj['obsws'].get_scene_list() if not any(scene.get('sceneName') == scene_name for scene in resp.scenes): console.err.print(f'Scene [yellow]{scene_name}[/yellow] not found.') @@ -104,3 +109,29 @@ def kind_in_input_kinds(ctx: typer.Context, input_kind: str) -> str: console.err.print(f'Input kind [yellow]{input_kind}[/yellow] not found.') raise typer.Exit(1) return input_kind + + +def timecode_format(ctx: typer.Context, timecode: Optional[str]) -> str | None: + """Validate that a timecode is in HH:MM:SS or MM:SS format.""" + if timecode is None: + return timecode + + match timecode.split(':'): + case [mm, ss]: + if not (mm.isdigit() and ss.isdigit()): + console.err.print( + f'Timecode [yellow]{timecode}[/yellow] is not valid. Use MM:SS or HH:MM:SS format.' + ) + raise typer.Exit(1) + case [hh, mm, ss]: + if not (hh.isdigit() and mm.isdigit() and ss.isdigit()): + console.err.print( + f'Timecode [yellow]{timecode}[/yellow] is not valid. Use MM:SS or HH:MM:SS format.' + ) + raise typer.Exit(1) + case _: + console.err.print( + f'Timecode [yellow]{timecode}[/yellow] is not valid. Use MM:SS or HH:MM:SS format.' + ) + raise typer.Exit(1) + return timecode