mirror of
https://github.com/onyx-and-iris/obsws-cli.git
synced 2025-08-07 20:21:48 +00:00
Compare commits
No commits in common. "ab71414d2750070079ff38ca468172cf663c56db" and "51a4a60aa619c1d5c869df60ff1581da593d863b" have entirely different histories.
ab71414d27
...
51a4a60aa6
12
CHANGELOG.md
12
CHANGELOG.md
@ -5,18 +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.16.1] - 2025-06-04
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- screenshot save command, see [Screenshot](https://github.com/onyx-and-iris/obsws-cli/tree/main?tab=readme-ov-file#screenshot)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- filter list:
|
|
||||||
- source_name arg is now optional, it defaults to the current scene.
|
|
||||||
- default values are printed if unmodified.
|
|
||||||
|
|
||||||
# [0.15.0] - 2025-06-02
|
# [0.15.0] - 2025-06-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
21
README.md
21
README.md
@ -519,10 +519,7 @@ obsws-cli hotkey trigger-sequence OBS_KEY_F1 --shift --ctrl
|
|||||||
#### Filter
|
#### Filter
|
||||||
|
|
||||||
- list: List filters for a source.
|
- list: List filters for a source.
|
||||||
|
|
||||||
*optional*
|
|
||||||
- args: <source_name>
|
- args: <source_name>
|
||||||
- defaults to current scene
|
|
||||||
|
|
||||||
```console
|
```console
|
||||||
obsws-cli filter list "Mic/Aux"
|
obsws-cli filter list "Mic/Aux"
|
||||||
@ -583,24 +580,6 @@ obsws-cli projector open --monitor-index=1 "test_scene"
|
|||||||
obsws-cli projector open --monitor-index=1 "test_group"
|
obsws-cli projector open --monitor-index=1 "test_group"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Screenshot
|
|
||||||
|
|
||||||
- save: Take a screenshot and save it to a file.
|
|
||||||
- flags:
|
|
||||||
|
|
||||||
*optional*
|
|
||||||
- --width:
|
|
||||||
- defaults to 1920
|
|
||||||
- --height:
|
|
||||||
- defaults to 1080
|
|
||||||
- --quality:
|
|
||||||
- defaults to -1
|
|
||||||
|
|
||||||
- args: <source_name> <output_path>
|
|
||||||
|
|
||||||
```console
|
|
||||||
obsws-cli screenshot save --width=2560 --height=1440 "Scene" "C:\Users\me\Videos\screenshot.png"
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -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.2"
|
__version__ = "0.15.2"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"""Command line interface for the OBS WebSocket API."""
|
"""Command line interface for the OBS WebSocket API."""
|
||||||
|
|
||||||
import importlib
|
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
import obsws_python as obsws
|
import obsws_python as obsws
|
||||||
@ -9,29 +8,43 @@ from rich.console import Console
|
|||||||
|
|
||||||
from obsws_cli.__about__ import __version__ as obsws_cli_version
|
from obsws_cli.__about__ import __version__ as obsws_cli_version
|
||||||
|
|
||||||
from . import settings
|
from . import (
|
||||||
|
filter,
|
||||||
|
group,
|
||||||
|
hotkey,
|
||||||
|
input,
|
||||||
|
profile,
|
||||||
|
projector,
|
||||||
|
record,
|
||||||
|
replaybuffer,
|
||||||
|
scene,
|
||||||
|
scenecollection,
|
||||||
|
sceneitem,
|
||||||
|
settings,
|
||||||
|
stream,
|
||||||
|
studiomode,
|
||||||
|
virtualcam,
|
||||||
|
)
|
||||||
from .alias import AliasGroup
|
from .alias import AliasGroup
|
||||||
|
|
||||||
app = typer.Typer(cls=AliasGroup)
|
app = typer.Typer(cls=AliasGroup)
|
||||||
for sub_typer in (
|
for module in (
|
||||||
'filter',
|
filter,
|
||||||
'group',
|
group,
|
||||||
'hotkey',
|
hotkey,
|
||||||
'input',
|
input,
|
||||||
'profile',
|
projector,
|
||||||
'projector',
|
profile,
|
||||||
'record',
|
record,
|
||||||
'replaybuffer',
|
replaybuffer,
|
||||||
'scene',
|
scene,
|
||||||
'scenecollection',
|
scenecollection,
|
||||||
'sceneitem',
|
sceneitem,
|
||||||
'screenshot',
|
stream,
|
||||||
'stream',
|
studiomode,
|
||||||
'studiomode',
|
virtualcam,
|
||||||
'virtualcam',
|
|
||||||
):
|
):
|
||||||
module = importlib.import_module(f'.{sub_typer}', package=__package__)
|
app.add_typer(module.app, name=module.__name__.split('.')[-1])
|
||||||
app.add_typer(module.app, name=sub_typer)
|
|
||||||
|
|
||||||
out_console = Console()
|
out_console = Console()
|
||||||
err_console = Console(stderr=True)
|
err_console = Console(stderr=True)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""module containing commands for manipulating filters in scenes."""
|
"""module containing commands for manipulating filters in scenes."""
|
||||||
|
|
||||||
from typing import Annotated, Optional
|
|
||||||
|
|
||||||
import obsws_python as obsws
|
import obsws_python as obsws
|
||||||
import typer
|
import typer
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
@ -21,20 +19,8 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(
|
def list(ctx: typer.Context, source_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
source_name: Annotated[
|
|
||||||
Optional[str],
|
|
||||||
typer.Argument(
|
|
||||||
show_default='The current scene',
|
|
||||||
help='The source to list filters for',
|
|
||||||
),
|
|
||||||
] = None,
|
|
||||||
):
|
|
||||||
"""List filters for a source."""
|
"""List filters for a source."""
|
||||||
if not source_name:
|
|
||||||
source_name = ctx.obj.get_current_program_scene().scene_name
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
resp = ctx.obj.get_source_filter_list(source_name)
|
resp = ctx.obj.get_source_filter_list(source_name)
|
||||||
except obsws.error.OBSSDKRequestError as e:
|
except obsws.error.OBSSDKRequestError as e:
|
||||||
@ -58,9 +44,6 @@ def list_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
for filter in resp.filters:
|
for filter in resp.filters:
|
||||||
resp = ctx.obj.get_source_filter_default_settings(filter['filterKind'])
|
|
||||||
settings = resp.default_filter_settings | filter['filterSettings']
|
|
||||||
|
|
||||||
table.add_row(
|
table.add_row(
|
||||||
filter['filterName'],
|
filter['filterName'],
|
||||||
util.snakecase_to_titlecase(filter['filterKind']),
|
util.snakecase_to_titlecase(filter['filterKind']),
|
||||||
@ -68,7 +51,7 @@ def list_(
|
|||||||
'\n'.join(
|
'\n'.join(
|
||||||
[
|
[
|
||||||
f'{util.snakecase_to_titlecase(k):<20} {v:>10}'
|
f'{util.snakecase_to_titlecase(k):<20} {v:>10}'
|
||||||
for k, v in settings.items()
|
for k, v in filter['filterSettings'].items()
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -85,18 +68,8 @@ def _get_filter_enabled(ctx: typer.Context, source_name: str, filter_name: str):
|
|||||||
@app.command('enable | on')
|
@app.command('enable | on')
|
||||||
def enable(
|
def enable(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
source_name: Annotated[
|
source_name: str = typer.Argument(..., help='The source to enable the filter for'),
|
||||||
str,
|
filter_name: str = typer.Argument(..., help='The name of the filter to enable'),
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The source to enable the filter for'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
filter_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The name of the filter to enable'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
):
|
):
|
||||||
"""Enable a filter for a source."""
|
"""Enable a filter for a source."""
|
||||||
if _get_filter_enabled(ctx, source_name, filter_name):
|
if _get_filter_enabled(ctx, source_name, filter_name):
|
||||||
@ -112,18 +85,8 @@ def enable(
|
|||||||
@app.command('disable | off')
|
@app.command('disable | off')
|
||||||
def disable(
|
def disable(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
source_name: Annotated[
|
source_name: str = typer.Argument(..., help='The source to disable the filter for'),
|
||||||
str,
|
filter_name: str = typer.Argument(..., help='The name of the filter to disable'),
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The source to disable the filter for'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
filter_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The name of the filter to disable'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
):
|
):
|
||||||
"""Disable a filter for a source."""
|
"""Disable a filter for a source."""
|
||||||
if not _get_filter_enabled(ctx, source_name, filter_name):
|
if not _get_filter_enabled(ctx, source_name, filter_name):
|
||||||
@ -139,18 +102,8 @@ def disable(
|
|||||||
@app.command('toggle | tg')
|
@app.command('toggle | tg')
|
||||||
def toggle(
|
def toggle(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
source_name: Annotated[
|
source_name: str = typer.Argument(..., help='The source to toggle the filter for'),
|
||||||
str,
|
filter_name: str = typer.Argument(..., help='The name of the filter to toggle'),
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The source to toggle the filter for'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
filter_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The name of the filter to toggle'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
):
|
):
|
||||||
"""Toggle a filter for a source."""
|
"""Toggle a filter for a source."""
|
||||||
is_enabled = _get_filter_enabled(ctx, source_name, filter_name)
|
is_enabled = _get_filter_enabled(ctx, source_name, filter_name)
|
||||||
@ -166,18 +119,12 @@ def toggle(
|
|||||||
@app.command('status | ss')
|
@app.command('status | ss')
|
||||||
def status(
|
def status(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
source_name: Annotated[
|
source_name: str = typer.Argument(
|
||||||
str,
|
..., help='The source to get the filter status for'
|
||||||
typer.Argument(
|
),
|
||||||
..., show_default=False, help='The source to get the filter status for'
|
filter_name: str = typer.Argument(
|
||||||
),
|
..., help='The name of the filter to get the status for'
|
||||||
],
|
),
|
||||||
filter_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='The name of the filter to get the status for'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
):
|
):
|
||||||
"""Get the status of a filter for a source."""
|
"""Get the status of a filter for a source."""
|
||||||
is_enabled = _get_filter_enabled(ctx, source_name, filter_name)
|
is_enabled = _get_filter_enabled(ctx, source_name, filter_name)
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""module containing commands for manipulating groups in scenes."""
|
"""module containing commands for manipulating groups in scenes."""
|
||||||
|
|
||||||
from typing import Annotated, Optional
|
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
@ -21,15 +19,11 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(
|
def list(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
scene_name: Annotated[
|
scene_name: str = typer.Argument(
|
||||||
Optional[str],
|
None, help='Scene name (optional, defaults to current scene)'
|
||||||
typer.Argument(
|
),
|
||||||
show_default='The current scene',
|
|
||||||
help='Scene name to list groups for',
|
|
||||||
),
|
|
||||||
] = None,
|
|
||||||
):
|
):
|
||||||
"""List groups in a scene."""
|
"""List groups in a scene."""
|
||||||
if not scene_name:
|
if not scene_name:
|
||||||
@ -81,16 +75,7 @@ def _get_group(group_name: str, resp: DataclassProtocol) -> dict | None:
|
|||||||
|
|
||||||
|
|
||||||
@app.command('show | sh')
|
@app.command('show | sh')
|
||||||
def show(
|
def show(ctx: typer.Context, scene_name: str, group_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
scene_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(..., show_default=False, help='Scene name the group is in'),
|
|
||||||
],
|
|
||||||
group_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Group name to show')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Show a group in a scene."""
|
"""Show a group in a scene."""
|
||||||
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 '{scene_name}' not found.")
|
||||||
@ -111,15 +96,7 @@ def show(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('hide | h')
|
@app.command('hide | h')
|
||||||
def hide(
|
def hide(ctx: typer.Context, scene_name: str, group_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
scene_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Scene name the group is in')
|
|
||||||
],
|
|
||||||
group_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Group name to hide')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Hide a group in a scene."""
|
"""Hide a group in a scene."""
|
||||||
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 '{scene_name}' not found.")
|
||||||
@ -140,15 +117,7 @@ def hide(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('toggle | tg')
|
@app.command('toggle | tg')
|
||||||
def toggle(
|
def toggle(ctx: typer.Context, scene_name: str, group_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
scene_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Scene name the group is in')
|
|
||||||
],
|
|
||||||
group_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Group name to toggle')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Toggle a group in a scene."""
|
"""Toggle a group in a scene."""
|
||||||
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 '{scene_name}' not found.")
|
||||||
@ -173,15 +142,7 @@ def toggle(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('status | ss')
|
@app.command('status | ss')
|
||||||
def status(
|
def status(ctx: typer.Context, scene_name: str, group_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
scene_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Scene name the group is in')
|
|
||||||
],
|
|
||||||
group_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Group name to check status')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Get the status of a group in a scene."""
|
"""Get the status of a group in a scene."""
|
||||||
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 '{scene_name}' not found.")
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""module containing commands for hotkey management."""
|
"""module containing commands for hotkey management."""
|
||||||
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
@ -19,7 +17,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(
|
def list(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
):
|
):
|
||||||
"""List all hotkeys."""
|
"""List all hotkeys."""
|
||||||
@ -37,9 +35,7 @@ def list_(
|
|||||||
@app.command('trigger | tr')
|
@app.command('trigger | tr')
|
||||||
def trigger(
|
def trigger(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
hotkey: Annotated[
|
hotkey: str = typer.Argument(..., help='The hotkey to trigger'),
|
||||||
str, typer.Argument(..., show_default=False, help='The hotkey to trigger')
|
|
||||||
],
|
|
||||||
):
|
):
|
||||||
"""Trigger a hotkey by name."""
|
"""Trigger a hotkey by name."""
|
||||||
ctx.obj.trigger_hotkey_by_name(hotkey)
|
ctx.obj.trigger_hotkey_by_name(hotkey)
|
||||||
@ -48,26 +44,14 @@ def trigger(
|
|||||||
@app.command('trigger-sequence | trs')
|
@app.command('trigger-sequence | trs')
|
||||||
def trigger_sequence(
|
def trigger_sequence(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
key_id: Annotated[
|
shift: bool = typer.Option(False, help='Press shift when triggering the hotkey'),
|
||||||
str,
|
ctrl: bool = typer.Option(False, help='Press control when triggering the hotkey'),
|
||||||
typer.Argument(
|
alt: bool = typer.Option(False, help='Press alt when triggering the hotkey'),
|
||||||
...,
|
cmd: bool = typer.Option(False, help='Press cmd when triggering the hotkey'),
|
||||||
show_default=False,
|
key_id: str = typer.Argument(
|
||||||
help='The OBS key ID to trigger, see https://github.com/onyx-and-iris/obsws-cli?tab=readme-ov-file#hotkey for more info',
|
...,
|
||||||
),
|
help='The OBS key ID to trigger, see https://github.com/onyx-and-iris/obsws-cli?tab=readme-ov-file#hotkey for more info',
|
||||||
],
|
),
|
||||||
shift: Annotated[
|
|
||||||
bool, typer.Option(..., help='Press shift when triggering the hotkey')
|
|
||||||
] = False,
|
|
||||||
ctrl: Annotated[
|
|
||||||
bool, typer.Option(..., help='Press control when triggering the hotkey')
|
|
||||||
] = False,
|
|
||||||
alt: Annotated[
|
|
||||||
bool, typer.Option(..., help='Press alt when triggering the hotkey')
|
|
||||||
] = False,
|
|
||||||
cmd: Annotated[
|
|
||||||
bool, typer.Option(..., help='Press cmd when triggering the hotkey')
|
|
||||||
] = False,
|
|
||||||
):
|
):
|
||||||
"""Trigger a hotkey by sequence."""
|
"""Trigger a hotkey by sequence."""
|
||||||
ctx.obj.trigger_hotkey_by_key_sequence(key_id, shift, ctrl, alt, cmd)
|
ctx.obj.trigger_hotkey_by_key_sequence(key_id, shift, ctrl, alt, cmd)
|
||||||
|
@ -20,7 +20,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(
|
def list(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
input: Annotated[bool, typer.Option(help='Filter by input type.')] = False,
|
input: Annotated[bool, typer.Option(help='Filter by input type.')] = False,
|
||||||
output: Annotated[bool, typer.Option(help='Filter by output type.')] = False,
|
output: Annotated[bool, typer.Option(help='Filter by output type.')] = False,
|
||||||
@ -67,12 +67,7 @@ def list_(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('mute | m')
|
@app.command('mute | m')
|
||||||
def mute(
|
def mute(ctx: typer.Context, input_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
input_name: Annotated[
|
|
||||||
str, typer.Argument(..., show_default=False, help='Name of the input to mute.')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Mute an input."""
|
"""Mute an input."""
|
||||||
if not validate.input_in_inputs(ctx, input_name):
|
if not validate.input_in_inputs(ctx, input_name):
|
||||||
err_console.print(f"Input '{input_name}' not found.")
|
err_console.print(f"Input '{input_name}' not found.")
|
||||||
@ -87,13 +82,7 @@ def mute(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('unmute | um')
|
@app.command('unmute | um')
|
||||||
def unmute(
|
def unmute(ctx: typer.Context, input_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
input_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(..., show_default=False, help='Name of the input to unmute.'),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Unmute an input."""
|
"""Unmute an input."""
|
||||||
if not validate.input_in_inputs(ctx, input_name):
|
if not validate.input_in_inputs(ctx, input_name):
|
||||||
err_console.print(f"Input '{input_name}' not found.")
|
err_console.print(f"Input '{input_name}' not found.")
|
||||||
@ -108,13 +97,7 @@ def unmute(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('toggle | tg')
|
@app.command('toggle | tg')
|
||||||
def toggle(
|
def toggle(ctx: typer.Context, input_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
input_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(..., show_default=False, help='Name of the input to toggle.'),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Toggle an input."""
|
"""Toggle an input."""
|
||||||
if not validate.input_in_inputs(ctx, input_name):
|
if not validate.input_in_inputs(ctx, input_name):
|
||||||
err_console.print(f"Input '{input_name}' not found.")
|
err_console.print(f"Input '{input_name}' not found.")
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""module containing commands for manipulating profiles in OBS."""
|
"""module containing commands for manipulating profiles in OBS."""
|
||||||
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
@ -20,7 +18,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(ctx: typer.Context):
|
def list(ctx: typer.Context):
|
||||||
"""List profiles."""
|
"""List profiles."""
|
||||||
resp = ctx.obj.get_profile_list()
|
resp = ctx.obj.get_profile_list()
|
||||||
|
|
||||||
@ -49,15 +47,7 @@ def current(ctx: typer.Context):
|
|||||||
|
|
||||||
|
|
||||||
@app.command('switch | set')
|
@app.command('switch | set')
|
||||||
def switch(
|
def switch(ctx: typer.Context, profile_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
profile_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(
|
|
||||||
..., show_default=False, help='Name of the profile to switch to'
|
|
||||||
),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Switch to a profile."""
|
"""Switch to a profile."""
|
||||||
if not validate.profile_exists(ctx, profile_name):
|
if not validate.profile_exists(ctx, profile_name):
|
||||||
err_console.print(f"Profile '{profile_name}' not found.")
|
err_console.print(f"Profile '{profile_name}' not found.")
|
||||||
@ -73,13 +63,7 @@ def switch(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('create | new')
|
@app.command('create | new')
|
||||||
def create(
|
def create(ctx: typer.Context, profile_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
profile_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(..., show_default=False, help='Name of the profile to create.'),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Create a new profile."""
|
"""Create a new profile."""
|
||||||
if validate.profile_exists(ctx, profile_name):
|
if validate.profile_exists(ctx, profile_name):
|
||||||
err_console.print(f"Profile '{profile_name}' already exists.")
|
err_console.print(f"Profile '{profile_name}' already exists.")
|
||||||
@ -90,13 +74,7 @@ def create(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('remove | rm')
|
@app.command('remove | rm')
|
||||||
def remove(
|
def remove(ctx: typer.Context, profile_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
profile_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(..., show_default=False, help='Name of the profile to remove.'),
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Remove a profile."""
|
"""Remove a profile."""
|
||||||
if not validate.profile_exists(ctx, profile_name):
|
if not validate.profile_exists(ctx, profile_name):
|
||||||
err_console.print(f"Profile '{profile_name}' not found.")
|
err_console.print(f"Profile '{profile_name}' not found.")
|
||||||
|
@ -52,8 +52,7 @@ def open(
|
|||||||
source_name: Annotated[
|
source_name: Annotated[
|
||||||
str,
|
str,
|
||||||
typer.Argument(
|
typer.Argument(
|
||||||
show_default='The current scene',
|
help='Name of the source to project. (optional, defaults to current scene)'
|
||||||
help='Name of the source to project.',
|
|
||||||
),
|
),
|
||||||
] = '',
|
] = '',
|
||||||
):
|
):
|
||||||
|
@ -20,7 +20,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(ctx: typer.Context):
|
def list(ctx: typer.Context):
|
||||||
"""List all scenes."""
|
"""List all scenes."""
|
||||||
resp = ctx.obj.get_scene_list()
|
resp = ctx.obj.get_scene_list()
|
||||||
scenes = (
|
scenes = (
|
||||||
@ -64,9 +64,7 @@ def current(
|
|||||||
@app.command('switch | set')
|
@app.command('switch | set')
|
||||||
def switch(
|
def switch(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
scene_name: Annotated[
|
scene_name: str,
|
||||||
str, typer.Argument(..., help='Name of the scene to switch to')
|
|
||||||
],
|
|
||||||
preview: Annotated[
|
preview: Annotated[
|
||||||
bool,
|
bool,
|
||||||
typer.Option(help='Switch to the preview scene instead of the program scene'),
|
typer.Option(help='Switch to the preview scene instead of the program scene'),
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""module containing commands for manipulating scene collections."""
|
"""module containing commands for manipulating scene collections."""
|
||||||
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
import typer
|
import typer
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
@ -20,7 +18,7 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(ctx: typer.Context):
|
def list(ctx: typer.Context):
|
||||||
"""List all scene collections."""
|
"""List all scene collections."""
|
||||||
resp = ctx.obj.get_scene_collection_list()
|
resp = ctx.obj.get_scene_collection_list()
|
||||||
|
|
||||||
@ -41,12 +39,7 @@ def current(ctx: typer.Context):
|
|||||||
|
|
||||||
|
|
||||||
@app.command('switch | set')
|
@app.command('switch | set')
|
||||||
def switch(
|
def switch(ctx: typer.Context, scene_collection_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
scene_collection_name: Annotated[
|
|
||||||
str, typer.Argument(..., help='Name of the scene collection to switch to')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Switch to a scene collection."""
|
"""Switch to a scene collection."""
|
||||||
if not validate.scene_collection_in_scene_collections(ctx, scene_collection_name):
|
if not validate.scene_collection_in_scene_collections(ctx, scene_collection_name):
|
||||||
err_console.print(f"Scene collection '{scene_collection_name}' not found.")
|
err_console.print(f"Scene collection '{scene_collection_name}' not found.")
|
||||||
@ -66,12 +59,7 @@ def switch(
|
|||||||
|
|
||||||
|
|
||||||
@app.command('create | new')
|
@app.command('create | new')
|
||||||
def create(
|
def create(ctx: typer.Context, scene_collection_name: str):
|
||||||
ctx: typer.Context,
|
|
||||||
scene_collection_name: Annotated[
|
|
||||||
str, typer.Argument(..., help='Name of the scene collection to create')
|
|
||||||
],
|
|
||||||
):
|
|
||||||
"""Create a new scene collection."""
|
"""Create a new scene collection."""
|
||||||
if validate.scene_collection_in_scene_collections(ctx, scene_collection_name):
|
if validate.scene_collection_in_scene_collections(ctx, scene_collection_name):
|
||||||
err_console.print(f"Scene collection '{scene_collection_name}' already exists.")
|
err_console.print(f"Scene collection '{scene_collection_name}' already exists.")
|
||||||
|
@ -22,15 +22,11 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
@app.command('list | ls')
|
@app.command('list | ls')
|
||||||
def list_(
|
def list(
|
||||||
ctx: typer.Context,
|
ctx: typer.Context,
|
||||||
scene_name: Annotated[
|
scene_name: str = typer.Argument(
|
||||||
Optional[str],
|
None, help='Scene name (optional, defaults to current scene)'
|
||||||
typer.Argument(
|
),
|
||||||
show_default='The current scene',
|
|
||||||
help='Scene name to list items for',
|
|
||||||
),
|
|
||||||
] = None,
|
|
||||||
):
|
):
|
||||||
"""List all items in a scene."""
|
"""List all items in a scene."""
|
||||||
if not scene_name:
|
if not scene_name:
|
||||||
@ -167,10 +163,8 @@ def _get_scene_name_and_item_id(
|
|||||||
@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: str,
|
||||||
item_name: Annotated[
|
item_name: str,
|
||||||
str, typer.Argument(..., 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."""
|
||||||
@ -200,10 +194,8 @@ def show(
|
|||||||
@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: str,
|
||||||
item_name: Annotated[
|
item_name: str,
|
||||||
str, typer.Argument(..., 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."""
|
||||||
@ -235,10 +227,8 @@ def hide(
|
|||||||
@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: str,
|
||||||
item_name: Annotated[
|
item_name: str,
|
||||||
str, typer.Argument(..., 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."""
|
||||||
@ -301,10 +291,8 @@ def toggle(
|
|||||||
@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: str,
|
||||||
item_name: Annotated[
|
item_name: str,
|
||||||
str, typer.Argument(..., 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."""
|
||||||
|
@ -1,92 +0,0 @@
|
|||||||
"""module for taking screenshots using OBS WebSocket API."""
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
import obsws_python as obsws
|
|
||||||
import typer
|
|
||||||
from rich.console import Console
|
|
||||||
|
|
||||||
from .alias import AliasGroup
|
|
||||||
|
|
||||||
app = typer.Typer(cls=AliasGroup)
|
|
||||||
out_console = Console()
|
|
||||||
err_console = Console(
|
|
||||||
stderr=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@app.callback()
|
|
||||||
def main():
|
|
||||||
"""Take screenshots using OBS."""
|
|
||||||
|
|
||||||
|
|
||||||
@app.command('save | sv')
|
|
||||||
def save(
|
|
||||||
ctx: typer.Context,
|
|
||||||
source_name: Annotated[
|
|
||||||
str,
|
|
||||||
typer.Argument(
|
|
||||||
...,
|
|
||||||
show_default=False,
|
|
||||||
help='Name of the source to take a screenshot of.',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
output_path: Annotated[
|
|
||||||
Path,
|
|
||||||
# Since the CLI and OBS may be running on different platforms,
|
|
||||||
# we won't validate the path here.
|
|
||||||
typer.Argument(
|
|
||||||
...,
|
|
||||||
show_default=False,
|
|
||||||
file_okay=True,
|
|
||||||
dir_okay=False,
|
|
||||||
help='Path to save the screenshot (must include file name and extension).',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
width: Annotated[
|
|
||||||
float,
|
|
||||||
typer.Option(
|
|
||||||
help='Width of the screenshot.',
|
|
||||||
),
|
|
||||||
] = 1920,
|
|
||||||
height: Annotated[
|
|
||||||
float,
|
|
||||||
typer.Option(
|
|
||||||
help='Height of the screenshot.',
|
|
||||||
),
|
|
||||||
] = 1080,
|
|
||||||
quality: Annotated[
|
|
||||||
float,
|
|
||||||
typer.Option(
|
|
||||||
min=-1,
|
|
||||||
max=100,
|
|
||||||
help='Quality of the screenshot.',
|
|
||||||
),
|
|
||||||
] = -1,
|
|
||||||
):
|
|
||||||
"""Take a screenshot and save it to a file."""
|
|
||||||
try:
|
|
||||||
ctx.obj.save_source_screenshot(
|
|
||||||
name=source_name,
|
|
||||||
img_format=output_path.suffix.lstrip('.').lower(),
|
|
||||||
file_path=str(output_path),
|
|
||||||
width=width,
|
|
||||||
height=height,
|
|
||||||
quality=quality,
|
|
||||||
)
|
|
||||||
except obsws.error.OBSSDKRequestError as e:
|
|
||||||
match e.code:
|
|
||||||
case 403:
|
|
||||||
err_console.print(
|
|
||||||
'The image format (file extension) must be included in the file name '
|
|
||||||
"for example: '/path/to/screenshot.png'.",
|
|
||||||
)
|
|
||||||
raise typer.Exit(1)
|
|
||||||
case 600:
|
|
||||||
err_console.print(f"No source was found by the name of '{source_name}'")
|
|
||||||
raise typer.Exit(1)
|
|
||||||
case _:
|
|
||||||
raise
|
|
||||||
|
|
||||||
out_console.print(f"Screenshot saved to [bold]'{output_path}'[/bold].")
|
|
Loading…
x
Reference in New Issue
Block a user