From 87eb7752bd4595148e97373a3e2f404dcd79b651 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sun, 27 Apr 2025 13:50:40 +0100 Subject: [PATCH] add bounds/crop/rotation flags to sceneitem transform upd readme upd changelog upd tests minor bump --- CHANGELOG.md | 6 +++++ README.md | 19 ++++++++++++-- obsws_cli/__about__.py | 2 +- obsws_cli/sceneitem.py | 55 +++++++++++++++++++++++++++++++++++++++++ tests/conftest.py | 11 +++++++++ tests/test_sceneitem.py | 22 +++++++++++++++-- tests/test_stream.py | 4 +-- 7 files changed, 112 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f86ab..8d73a99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.10.0] - 2025-04-27 + +### Added + +- sceneitem transform, see *transform* under [Scene Item](https://github.com/onyx-and-iris/obsws-cli?tab=readme-ov-file#scene-item) + # [0.9.2] - 2025-04-26 ### Added diff --git a/README.md b/README.md index cf3525b..9701627 100644 --- a/README.md +++ b/README.md @@ -148,15 +148,30 @@ obsws-cli sceneitem visible --parent=test_group START "Colour Source 4" - transform: Set the transform of an item in a scene. - flags: - *optional* + - --parent: Parent group name. + + - --alignment: Alignment of the item in the scene + - --bounds-alignment: Bounds alignment of the item in the scene + - --bounds-height: Height of the item in the scene + - --bounds-type: Type of bounds for the item in the scene + - --bounds-width: Width of the item in the scene + - --crop-to-bounds: Crop the item to the bounds + - --crop-bottom: Bottom crop of the item in the scene + - --crop-left: Left crop of the item in the scene + - --crop-right: Right crop of the item in the scene + - --crop-top: Top crop of the item in the scene - --position-x: X position of the item in the scene - --position-y: Y position of the item in the scene - --scale-x: X scale of the item in the scene - --scale-y: Y scale of the item in the scene + - args: ```console -obsws-cli sceneitem transform --position-x=556.0 --scale-x=0.7946954965591431 --scale-y=0.7948529124259949 LIVE "Window Capture" +obsws-cli sceneitem transform \ + --rotation=5 \ + --position-x=250.8 \ + Scene "Colour Source 3" ``` #### Scene Collections diff --git a/obsws_cli/__about__.py b/obsws_cli/__about__.py index 1a9867e..d5f6aa1 100644 --- a/obsws_cli/__about__.py +++ b/obsws_cli/__about__.py @@ -1,4 +1,4 @@ # SPDX-FileCopyrightText: 2025-present onyx-and-iris # # SPDX-License-Identifier: MIT -__version__ = "0.9.2" +__version__ = "0.10.0" diff --git a/obsws_cli/sceneitem.py b/obsws_cli/sceneitem.py index 0fe633a..0329683 100644 --- a/obsws_cli/sceneitem.py +++ b/obsws_cli/sceneitem.py @@ -212,12 +212,45 @@ def transform( scene_name: str, item_name: str, parent: Annotated[str, typer.Option(help='Parent group name')] = None, + alignment: Annotated[ + int, typer.Option(help='Alignment of the item in the scene') + ] = None, + bounds_alignment: Annotated[ + int, typer.Option(help='Bounds alignment of the item in the scene') + ] = None, + bounds_height: Annotated[ + float, typer.Option(help='Height of the item in the scene') + ] = None, + bounds_type: Annotated[ + str, typer.Option(help='Type of bounds for the item in the scene') + ] = None, + bounds_width: Annotated[ + float, typer.Option(help='Width of the item in the scene') + ] = None, + crop_to_bounds: Annotated[ + bool, typer.Option(help='Crop the item to the bounds') + ] = None, + crop_bottom: Annotated[ + float, typer.Option(help='Bottom crop of the item in the scene') + ] = None, + crop_left: Annotated[ + float, typer.Option(help='Left crop of the item in the scene') + ] = None, + crop_right: Annotated[ + float, typer.Option(help='Right crop of the item in the scene') + ] = None, + crop_top: Annotated[ + float, typer.Option(help='Top crop of the item in the scene') + ] = None, position_x: Annotated[ float, typer.Option(help='X position of the item in the scene') ] = None, position_y: Annotated[ float, typer.Option(help='Y position of the item in the scene') ] = None, + rotation: Annotated[ + float, typer.Option(help='Rotation of the item in the scene') + ] = None, scale_x: Annotated[ float, typer.Option(help='X scale of the item in the scene') ] = None, @@ -241,10 +274,32 @@ def transform( ) transform = {} + if alignment is not None: + transform['alignment'] = alignment + if bounds_alignment is not None: + transform['boundsAlignment'] = bounds_alignment + if bounds_height is not None: + transform['boundsHeight'] = bounds_height + if bounds_type is not None: + transform['boundsType'] = bounds_type + if bounds_width is not None: + transform['boundsWidth'] = bounds_width + if crop_to_bounds is not None: + transform['cropToBounds'] = crop_to_bounds + if crop_bottom is not None: + transform['cropBottom'] = crop_bottom + if crop_left is not None: + transform['cropLeft'] = crop_left + if crop_right is not None: + transform['cropRight'] = crop_right + if crop_top is not None: + transform['cropTop'] = crop_top if position_x is not None: transform['positionX'] = position_x if position_y is not None: transform['positionY'] = position_y + if rotation is not None: + transform['rotation'] = rotation if scale_x is not None: transform['scaleX'] = scale_x if scale_y is not None: diff --git a/tests/conftest.py b/tests/conftest.py index 29a480a..959f7ec 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -71,6 +71,17 @@ def pytest_sessionstart(session): }, sceneItemEnabled=True, ) + resp = session.obsws.get_scene_item_list('pytest') + for item in resp.scene_items: + if item['sourceName'] == 'pytest_input_2': + session.obsws.set_scene_item_transform( + 'pytest', + item['sceneItemId'], + { + 'rotation': 0, + }, + ) + break def pytest_sessionfinish(session, exitstatus): diff --git a/tests/test_sceneitem.py b/tests/test_sceneitem.py index 503c184..158497b 100644 --- a/tests/test_sceneitem.py +++ b/tests/test_sceneitem.py @@ -7,9 +7,27 @@ from obsws_cli.app import app runner = CliRunner() -def test_item_list(): - """Test the item list command.""" +def test_sceneitem_list(): + """Test the sceneitem list command.""" result = runner.invoke(app, ['sceneitem', 'list', 'pytest']) assert result.exit_code == 0 assert 'pytest_input' in result.stdout assert 'pytest_input_2' in result.stdout + + +def test_sceneitem_transform(): + """Test the sceneitem transform command.""" + result = runner.invoke( + app, + [ + 'sceneitem', + 'transform', + '--rotation=60', + 'pytest', + 'pytest_input_2', + ], + ) + assert result.exit_code == 0 + assert ( + "Item 'pytest_input_2' in scene 'pytest' has been transformed" in result.stdout + ) diff --git a/tests/test_stream.py b/tests/test_stream.py index 6bf2c83..bcdf9da 100644 --- a/tests/test_stream.py +++ b/tests/test_stream.py @@ -18,7 +18,7 @@ def test_stream_start(): result = runner.invoke(app, ['stream', 'start']) assert result.exit_code == 0 - time.sleep(1) # Wait for the stream to start + time.sleep(2) # Wait for the stream to start if active: assert 'Streaming is already in progress, cannot start.' in result.stdout @@ -35,7 +35,7 @@ def test_stream_stop(): result = runner.invoke(app, ['stream', 'stop']) assert result.exit_code == 0 - time.sleep(1) # Wait for the stream to stop + time.sleep(2) # Wait for the stream to stop if active: assert 'Streaming stopped successfully.' in result.stdout