From 2c2501e01771fbbdf2ac15ef0bdba716e7f147e1 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Fri, 9 Jan 2026 13:45:25 +0000 Subject: [PATCH] implement settings command group --- obsws_cli/app.py | 1 + obsws_cli/settings.py | 337 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 338 insertions(+) create mode 100644 obsws_cli/settings.py diff --git a/obsws_cli/app.py b/obsws_cli/app.py index 531ea7b..4ce9b86 100644 --- a/obsws_cli/app.py +++ b/obsws_cli/app.py @@ -30,6 +30,7 @@ for sub_typer in ( 'studiomode', 'text', 'virtualcam', + 'settings', ): module = importlib.import_module(f'.{sub_typer}', package=__package__) app.add_typer(module.app, name=sub_typer) diff --git a/obsws_cli/settings.py b/obsws_cli/settings.py new file mode 100644 index 0000000..8ac483d --- /dev/null +++ b/obsws_cli/settings.py @@ -0,0 +1,337 @@ +"""module for settings management.""" + +from typing import Annotated, Optional + +import typer +from rich.table import Table +from rich.text import Text + +from . import console, util +from .alias import SubTyperAliasGroup + +app = typer.Typer(cls=SubTyperAliasGroup) + + +@app.callback() +def main(): + """Manage OBS settings.""" + + +@app.command('show | sh') +def show( + ctx: typer.Context, + video: Annotated[ + bool, typer.Option('--video', '-v', help='Show video settings.') + ] = False, + record: Annotated[ + bool, typer.Option('--record', '-r', help='Show recording settings.') + ] = False, + profile: Annotated[ + bool, typer.Option('--profile', '-p', help='Show profile settings.') + ] = False, +): + """Show current OBS settings.""" + if not any([video, record, profile]): + video = True + record = True + profile = True + + resp = ctx.obj['obsws'].get_video_settings() + video_table = Table( + title='Video Settings', + padding=(0, 2), + border_style=ctx.obj['style'].border, + ) + video_columns = ( + ('Setting', 'left', ctx.obj['style'].column), + ('Value', 'left', ctx.obj['style'].column), + ) + + for header_text, justify, style in video_columns: + video_table.add_column( + Text(header_text, justify='center'), + justify=justify, + style=style, + ) + + for setting in resp.attrs(): + video_table.add_row( + util.snakecase_to_titlecase(setting), + str(getattr(resp, setting)), + style='' if video_table.row_count % 2 == 0 else 'dim', + ) + + if video: + console.out.print(video_table) + + resp = ctx.obj['obsws'].get_record_directory() + record_table = Table( + title='Recording Settings', + padding=(0, 2), + border_style=ctx.obj['style'].border, + ) + record_columns = ( + ('Setting', 'left', ctx.obj['style'].column), + ('Value', 'left', ctx.obj['style'].column), + ) + for header_text, justify, style in record_columns: + record_table.add_column( + Text(header_text, justify='center'), + justify=justify, + style=style, + ) + + record_table.add_row( + 'Directory', + resp.record_directory, + style='' if record_table.row_count % 2 == 0 else 'dim', + ) + + if record: + console.out.print(record_table) + + profile_table = Table( + title='Profile Settings', + padding=(0, 2), + border_style=ctx.obj['style'].border, + ) + profile_columns = ( + ('Setting', 'left', ctx.obj['style'].column), + ('Value', 'left', ctx.obj['style'].column), + ) + for header_text, justify, style in profile_columns: + profile_table.add_column( + Text(header_text, justify='center'), + justify=justify, + style=style, + ) + + params = [ + ('Output', 'Mode', 'Output Mode'), + ('SimpleOutput', 'StreamEncoder', 'Simple Streaming Encoder'), + ('SimpleOutput', 'RecEncoder', 'Simple Recording Encoder'), + ('SimpleOutput', 'RecFormat2', 'Simple Recording Video Format'), + ('SimpleOutput', 'RecAudioEncoder', 'Simple Recording Audio Format'), + ('SimpleOutput', 'RecQuality', 'Simple Recording Quality'), + ('AdvOut', 'Encoder', 'Advanced Streaming Encoder'), + ('AdvOut', 'RecEncoder', 'Advanced Recording Encoder'), + ('AdvOut', 'RecType', 'Advanced Recording Type'), + ('AdvOut', 'RecFormat2', 'Advanced Recording Video Format'), + ('AdvOut', 'RecAudioEncoder', 'Advanced Recording Audio Format'), + ] + + for category, name, display_name in params: + resp = ctx.obj['obsws'].get_profile_parameter( + category=category, + name=name, + ) + if resp.parameter_value is not None: + profile_table.add_row( + display_name, + str(resp.parameter_value), + style='' if profile_table.row_count % 2 == 0 else 'dim', + ) + + if profile: + console.out.print(profile_table) + + +@app.command('profile | pr') +def profile( + ctx: typer.Context, + category: Annotated[ + str, + typer.Argument( + ..., + help='Profile parameter category (e.g., SimpleOutput, AdvOut).', + ), + ], + name: Annotated[ + str, + typer.Argument( + ..., + help='Profile parameter name (e.g., StreamEncoder, RecFormat2).', + ), + ], + value: Annotated[ + Optional[str], + typer.Argument( + ..., + help='Value to set for the profile parameter. If omitted, the current value is retrieved.', + ), + ] = None, +): + """Get/set OBS profile settings.""" + if value is None: + resp = ctx.obj['obsws'].get_profile_parameter( + category=category, + name=name, + ) + console.out.print( + f'Parameter Value for [bold]{name}[/bold]: ' + f'[green]{resp.parameter_value}[/green]' + ) + else: + ctx.obj['obsws'].set_profile_parameter( + category=category, + name=name, + value=value, + ) + console.out.print( + f'Set Parameter [bold]{name}[/bold] to [green]{value}[/green]' + ) + + +@app.command('stream-service | ss') +def stream_service( + ctx: typer.Context, + type_: Annotated[ + Optional[str], + typer.Argument( + ..., + help='Stream service type (e.g., Twitch, YouTube). If omitted, current settings are retrieved.', + ), + ] = None, + key: Annotated[ + Optional[str], + typer.Option('--key', '-k', help='Stream key to set. Optional.'), + ] = None, + server: Annotated[ + Optional[str], + typer.Option('--server', '-s', help='Stream server to set. Optional.'), + ] = None, +): + """Get/set OBS stream service settings.""" + if type_ is None: + resp = ctx.obj['obsws'].get_stream_service_settings() + table = Table( + title='Stream Service Settings', + padding=(0, 2), + border_style=ctx.obj['style'].border, + ) + columns = ( + ('Setting', 'left', ctx.obj['style'].column), + ('Value', 'left', ctx.obj['style'].column), + ) + for header_text, justify, style in columns: + table.add_column( + Text(header_text, justify='center'), + justify=justify, + style=style, + ) + table.add_row( + 'Type', + resp.stream_service_type, + style='' if table.row_count % 2 == 0 else 'dim', + ) + table.add_row( + 'Server', + resp.stream_service_settings.get('server', ''), + style='' if table.row_count % 2 == 0 else 'dim', + ) + table.add_row( + 'Key', + resp.stream_service_settings.get('key', ''), + style='' if table.row_count % 2 == 0 else 'dim', + ) + console.out.print(table) + else: + current_settings = ctx.obj['obsws'].get_stream_service_settings() + if key is None: + key = current_settings.stream_service_settings.get('key', '') + if server is None: + server = current_settings.stream_service_settings.get('server', '') + + ctx.obj['obsws'].set_stream_service_settings( + ss_type=type_, + ss_settings={'key': key, 'server': server}, + ) + console.out.print('Stream service settings updated.') + + +@app.command('video | vi') +def video( + ctx: typer.Context, + base_width: Annotated[ + Optional[int], + typer.Option('--base-width', '-bw', help='Set base (canvas) width.'), + ] = None, + base_height: Annotated[ + Optional[int], + typer.Option('--base-height', '-bh', help='Set base (canvas) height.'), + ] = None, + output_width: Annotated[ + Optional[int], + typer.Option('--output-width', '-ow', help='Set output (scaled) width.'), + ] = None, + output_height: Annotated[ + Optional[int], + typer.Option('--output-height', '-oh', help='Set output (scaled) height.'), + ] = None, + fps_num: Annotated[ + Optional[int], + typer.Option('--fps-num', '-fn', help='Set FPS numerator.'), + ] = None, + fps_den: Annotated[ + Optional[int], + typer.Option('--fps-den', '-fd', help='Set FPS denominator.'), + ] = None, +): + """Get/set OBS video settings.""" + if not any( + [ + base_width, + base_height, + output_width, + output_height, + fps_num, + fps_den, + ] + ): + resp = ctx.obj['obsws'].get_video_settings() + table = Table( + title='Video Settings', + padding=(0, 2), + border_style=ctx.obj['style'].border, + ) + columns = ( + ('Setting', 'left', ctx.obj['style'].column), + ('Value', 'left', ctx.obj['style'].column), + ) + for header_text, justify, style in columns: + table.add_column( + Text(header_text, justify='center'), + justify=justify, + style=style, + ) + for setting in resp.attrs(): + table.add_row( + util.snakecase_to_titlecase(setting), + str(getattr(resp, setting)), + style='' if table.row_count % 2 == 0 else 'dim', + ) + console.out.print(table) + else: + current_settings = ctx.obj['obsws'].get_video_settings() + if base_width is None: + base_width = current_settings.base_width + if base_height is None: + base_height = current_settings.base_height + if output_width is None: + output_width = current_settings.output_width + if output_height is None: + output_height = current_settings.output_height + if fps_num is None: + fps_num = current_settings.fps_num + if fps_den is None: + fps_den = current_settings.fps_den + + ctx.obj['obsws'].set_video_settings( + base_width=base_width, + base_height=base_height, + out_width=output_width, + out_height=output_height, + numerator=fps_num, + denominator=fps_den, + ) + console.out.print('Video settings updated.')