mirror of
https://github.com/onyx-and-iris/obsws-cli.git
synced 2025-08-05 11:31:44 +00:00
Compare commits
16 Commits
0c72a10fb7
...
f852a733c3
Author | SHA1 | Date | |
---|---|---|---|
f852a733c3 | |||
44dadcee23 | |||
ed4531c305 | |||
ec42a4cdd9 | |||
6123c92d00 | |||
1ceb95ab16 | |||
f06e2d3982 | |||
39dff3cc28 | |||
967c4ab699 | |||
dc128720c7 | |||
2e3f4267cd | |||
000431ab82 | |||
ec3e31cc4f | |||
cda0bbedb9 | |||
d0c96b853d | |||
040a41daa7 |
39
.github/workflows/publish.yml
vendored
Normal file
39
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
environment: pypi
|
||||
permissions:
|
||||
# This permission is needed for private repositories.
|
||||
contents: read
|
||||
# IMPORTANT: this permission is mandatory for trusted publishing
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install hatch
|
||||
|
||||
- name: Build package
|
||||
run: hatch build
|
||||
|
||||
- name: Publish on PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
19
.github/workflows/ruff.yml
vendored
Normal file
19
.github/workflows/ruff.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Ruff
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
args: 'format --check --diff'
|
@ -5,6 +5,12 @@ 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/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# [0.20.0] - 2025-07-14
|
||||
|
||||
### Added
|
||||
|
||||
- text command group, see [Text](https://github.com/onyx-and-iris/obsws-cli?tab=readme-ov-file#text)
|
||||
|
||||
# [0.19.0] - 2025-06-23
|
||||
|
||||
### Added
|
||||
|
16
README.md
16
README.md
@ -335,6 +335,22 @@ obsws-cli input unmute "Mic/Aux"
|
||||
obsws-cli input toggle "Mic/Aux"
|
||||
```
|
||||
|
||||
#### Text
|
||||
|
||||
- current: Get the current text for a text input.
|
||||
- args: <input_name>
|
||||
|
||||
```console
|
||||
obsws-cli text current "My Text Input"
|
||||
```
|
||||
|
||||
- update: Update the text of a text input.
|
||||
- args: <input_name> <new_text>
|
||||
|
||||
```console
|
||||
obsws-cli text update "My Text Input" "hi OBS!"
|
||||
```
|
||||
|
||||
#### Record
|
||||
|
||||
- start: Start recording.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# SPDX-FileCopyrightText: 2025-present onyx-and-iris <code@onyxandiris.online>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
__version__ = "0.19.1"
|
||||
__version__ = '0.20.2'
|
||||
|
@ -4,4 +4,4 @@
|
||||
|
||||
from .app import app
|
||||
|
||||
__all__ = ["app"]
|
||||
__all__ = ['app']
|
||||
|
@ -44,6 +44,8 @@ class RootTyperAliasGroup(typer.core.TyperGroup):
|
||||
cmd_name = 'stream'
|
||||
case 'sm':
|
||||
cmd_name = 'studiomode'
|
||||
case 't':
|
||||
cmd_name = 'text'
|
||||
case 'vc':
|
||||
cmd_name = 'virtualcam'
|
||||
return super().get_command(ctx, cmd_name)
|
||||
|
@ -28,6 +28,7 @@ for sub_typer in (
|
||||
'screenshot',
|
||||
'stream',
|
||||
'studiomode',
|
||||
'text',
|
||||
'virtualcam',
|
||||
):
|
||||
module = importlib.import_module(f'.{sub_typer}', package=__package__)
|
||||
|
@ -16,6 +16,7 @@ class Settings(UserDict):
|
||||
The settings are expected to be in uppercase and should start with 'OBS_'.
|
||||
|
||||
Example:
|
||||
-------
|
||||
settings = Settings()
|
||||
host = settings['OBS_HOST']
|
||||
settings['OBS_PORT'] = 4455
|
||||
@ -64,12 +65,15 @@ def get(key: str) -> SettingsValue:
|
||||
"""Get a setting value by key.
|
||||
|
||||
Args:
|
||||
----
|
||||
key (str): The key of the setting to retrieve.
|
||||
|
||||
Returns:
|
||||
-------
|
||||
The value of the setting.
|
||||
|
||||
Raises:
|
||||
------
|
||||
KeyError: If the key does not exist in the settings.
|
||||
|
||||
"""
|
||||
|
78
obsws_cli/text.py
Normal file
78
obsws_cli/text.py
Normal file
@ -0,0 +1,78 @@
|
||||
"""module containing commands for manipulating text inputs."""
|
||||
|
||||
from typing import Annotated, Optional
|
||||
|
||||
import typer
|
||||
|
||||
from . import console, validate
|
||||
from .alias import SubTyperAliasGroup
|
||||
|
||||
app = typer.Typer(cls=SubTyperAliasGroup)
|
||||
|
||||
|
||||
@app.callback()
|
||||
def main():
|
||||
"""Control text inputs in OBS."""
|
||||
|
||||
|
||||
@app.command('current | get')
|
||||
def current(
|
||||
ctx: typer.Context,
|
||||
input_name: Annotated[str, typer.Argument(help='Name of the text input to get.')],
|
||||
):
|
||||
"""Get the current text for a text input."""
|
||||
if not validate.input_in_inputs(ctx, input_name):
|
||||
console.err.print(f'Input [yellow]{input_name}[/yellow] not found.')
|
||||
raise typer.Exit(1)
|
||||
|
||||
resp = ctx.obj['obsws'].get_input_settings(name=input_name)
|
||||
if not resp.input_kind.startswith('text_'):
|
||||
console.err.print(
|
||||
f'Input [yellow]{input_name}[/yellow] is not a text input.',
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
current_text = resp.input_settings.get('text', '')
|
||||
if not current_text:
|
||||
current_text = '(empty)'
|
||||
console.out.print(
|
||||
f'Current text for input {console.highlight(ctx, input_name)}: {current_text}',
|
||||
)
|
||||
|
||||
|
||||
@app.command('update | set')
|
||||
def update(
|
||||
ctx: typer.Context,
|
||||
input_name: Annotated[
|
||||
str, typer.Argument(help='Name of the text input to update.')
|
||||
],
|
||||
new_text: Annotated[
|
||||
Optional[str],
|
||||
typer.Argument(
|
||||
help='The new text to set for the input.',
|
||||
),
|
||||
] = None,
|
||||
):
|
||||
"""Update the text of a text input."""
|
||||
if not validate.input_in_inputs(ctx, input_name):
|
||||
console.err.print(f'Input [yellow]{input_name}[/yellow] not found.')
|
||||
raise typer.Exit(1)
|
||||
|
||||
resp = ctx.obj['obsws'].get_input_settings(name=input_name)
|
||||
if not resp.input_kind.startswith('text_'):
|
||||
console.err.print(
|
||||
f'Input [yellow]{input_name}[/yellow] is not a text input.',
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
ctx.obj['obsws'].set_input_settings(
|
||||
name=input_name,
|
||||
settings={'text': new_text},
|
||||
overlay=True,
|
||||
)
|
||||
|
||||
if not new_text:
|
||||
new_text = '(empty)'
|
||||
console.out.print(
|
||||
f'Text for input {console.highlight(ctx, input_name)} updated to: {new_text}',
|
||||
)
|
@ -71,6 +71,13 @@ def pytest_sessionstart(session):
|
||||
},
|
||||
sceneItemEnabled=True,
|
||||
)
|
||||
session.obsws.create_input(
|
||||
sceneName='pytest_scene',
|
||||
inputName='pytest_text_input',
|
||||
inputKind='text_gdiplus_v3',
|
||||
inputSettings={'text': 'Hello, OBS!'},
|
||||
sceneItemEnabled=True,
|
||||
)
|
||||
resp = session.obsws.get_scene_item_list('pytest_scene')
|
||||
for item in resp.scene_items:
|
||||
if item['sourceName'] == 'pytest_input_2':
|
||||
|
18
tests/test_text.py
Normal file
18
tests/test_text.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""Unit tests for the text command in the OBS WebSocket CLI."""
|
||||
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from obsws_cli.app import app
|
||||
|
||||
runner = CliRunner(mix_stderr=False)
|
||||
|
||||
|
||||
def test_text_update():
|
||||
"""Test the text update command."""
|
||||
result = runner.invoke(app, ['text', 'current', 'pytest_text_input'])
|
||||
assert result.exit_code == 0
|
||||
assert 'Current text for input pytest_text_input: Hello, OBS!' in result.stdout
|
||||
|
||||
result = runner.invoke(app, ['text', 'update', 'pytest_text_input', 'New Text'])
|
||||
assert result.exit_code == 0
|
||||
assert 'Text for input pytest_text_input updated to: New Text' in result.stdout
|
Loading…
x
Reference in New Issue
Block a user