Compare commits

..

5 Commits

Author SHA1 Message Date
f223c51a71 patch bump 2025-05-24 06:08:33 +01:00
5988f450b4 print sceneitem list as rich table
scene_name arg now optional

upd README
2025-05-24 06:08:20 +01:00
fd3c020c3f print scene collection list as rich table 2025-05-24 06:07:52 +01:00
b21ed78bfa print scene list as rich table 2025-05-24 06:07:35 +01:00
9cfbd67b25 write to rich consoles 2025-05-24 06:06:45 +01:00
11 changed files with 144 additions and 74 deletions

View File

@ -96,7 +96,10 @@ obsws-cli scene switch LIVE
#### Scene Item #### Scene Item
- list: List all items in a scene. - list: List all items in a scene.
*optional*
- args: <scene_name> - args: <scene_name>
- defaults to current scene
```console ```console
obsws-cli sceneitem list LIVE obsws-cli sceneitem list LIVE

View File

@ -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.12.4" __version__ = "0.12.5"

View File

@ -4,6 +4,7 @@ from typing import Annotated
import obsws_python as obsws import obsws_python as obsws
import typer import typer
from rich.console import Console
from . import ( from . import (
filter, filter,
@ -41,6 +42,9 @@ for module in (
): ):
app.add_typer(module.app, name=module.__name__.split('.')[-1]) app.add_typer(module.app, name=module.__name__.split('.')[-1])
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
def main( def main(
@ -71,6 +75,6 @@ def main(
def version(ctx: typer.Context): def version(ctx: typer.Context):
"""Get the OBS Client and WebSocket versions.""" """Get the OBS Client and WebSocket versions."""
resp = ctx.obj.get_version() resp = ctx.obj.get_version()
typer.echo( out_console.print(
f'OBS Client version: {resp.obs_version} with WebSocket version: {resp.obs_web_socket_version}' f'OBS Client version: {resp.obs_version} with WebSocket version: {resp.obs_web_socket_version}'
) )

View File

@ -1,10 +1,13 @@
"""module for controlling OBS recording functionality.""" """module for controlling OBS recording functionality."""
import typer import typer
from rich.console import Console
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -27,11 +30,11 @@ def start(ctx: typer.Context):
if paused: if paused:
err_msg += ' Try resuming it.' err_msg += ' Try resuming it.'
typer.echo(err_msg, err=True) err_console.print(err_msg)
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.start_record() ctx.obj.start_record()
typer.echo('Recording started successfully.') out_console.print('Recording started successfully.')
@app.command('stop | st') @app.command('stop | st')
@ -39,11 +42,11 @@ def stop(ctx: typer.Context):
"""Stop recording.""" """Stop recording."""
active, _ = _get_recording_status(ctx) active, _ = _get_recording_status(ctx)
if not active: if not active:
typer.echo('Recording is not in progress, cannot stop.', err=True) err_console.print('Recording is not in progress, cannot stop.')
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.stop_record() ctx.obj.stop_record()
typer.echo('Recording stopped successfully.') out_console.print('Recording stopped successfully.')
@app.command('toggle | tg') @app.command('toggle | tg')
@ -51,9 +54,9 @@ def toggle(ctx: typer.Context):
"""Toggle recording.""" """Toggle recording."""
resp = ctx.obj.toggle_record() resp = ctx.obj.toggle_record()
if resp.output_active: if resp.output_active:
typer.echo('Recording started successfully.') out_console.print('Recording started successfully.')
else: else:
typer.echo('Recording stopped successfully.') out_console.print('Recording stopped successfully.')
@app.command('status | ss') @app.command('status | ss')
@ -62,11 +65,11 @@ def status(ctx: typer.Context):
active, paused = _get_recording_status(ctx) active, paused = _get_recording_status(ctx)
if active: if active:
if paused: if paused:
typer.echo('Recording is in progress and paused.') out_console.print('Recording is in progress and paused.')
else: else:
typer.echo('Recording is in progress.') out_console.print('Recording is in progress.')
else: else:
typer.echo('Recording is not in progress.') out_console.print('Recording is not in progress.')
@app.command('resume | r') @app.command('resume | r')
@ -74,14 +77,14 @@ def resume(ctx: typer.Context):
"""Resume recording.""" """Resume recording."""
active, paused = _get_recording_status(ctx) active, paused = _get_recording_status(ctx)
if not active: if not active:
typer.echo('Recording is not in progress, cannot resume.', err=True) err_console.print('Recording is not in progress, cannot resume.')
raise typer.Exit(1) raise typer.Exit(1)
if not paused: if not paused:
typer.echo('Recording is in progress but not paused, cannot resume.', err=True) err_console.print('Recording is in progress but not paused, cannot resume.')
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.resume_record() ctx.obj.resume_record()
typer.echo('Recording resumed successfully.') out_console.print('Recording resumed successfully.')
@app.command('pause | p') @app.command('pause | p')
@ -89,13 +92,11 @@ def pause(ctx: typer.Context):
"""Pause recording.""" """Pause recording."""
active, paused = _get_recording_status(ctx) active, paused = _get_recording_status(ctx)
if not active: if not active:
typer.echo('Recording is not in progress, cannot pause.', err=True) err_console.print('Recording is not in progress, cannot pause.')
raise typer.Exit(1) raise typer.Exit(1)
if paused: if paused:
typer.echo( err_console.print('Recording is in progress but already paused, cannot pause.')
'Recording is in progress but already paused, cannot pause.', err=True
)
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.pause_record() ctx.obj.pause_record()
typer.echo('Recording paused successfully.') out_console.print('Recording paused successfully.')

View File

@ -1,10 +1,13 @@
"""module containing commands for manipulating the replay buffer in OBS.""" """module containing commands for manipulating the replay buffer in OBS."""
import typer import typer
from rich.console import Console
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -16,14 +19,14 @@ def main():
def start(ctx: typer.Context): def start(ctx: typer.Context):
"""Start the replay buffer.""" """Start the replay buffer."""
ctx.obj.start_replay_buffer() ctx.obj.start_replay_buffer()
typer.echo('Replay buffer started.') out_console.print('Replay buffer started.')
@app.command('stop | st') @app.command('stop | st')
def stop(ctx: typer.Context): def stop(ctx: typer.Context):
"""Stop the replay buffer.""" """Stop the replay buffer."""
ctx.obj.stop_replay_buffer() ctx.obj.stop_replay_buffer()
typer.echo('Replay buffer stopped.') out_console.print('Replay buffer stopped.')
@app.command('toggle | tg') @app.command('toggle | tg')
@ -31,9 +34,9 @@ def toggle(ctx: typer.Context):
"""Toggle the replay buffer.""" """Toggle the replay buffer."""
resp = ctx.obj.toggle_replay_buffer() resp = ctx.obj.toggle_replay_buffer()
if resp.output_active: if resp.output_active:
typer.echo('Replay buffer is active.') out_console.print('Replay buffer is active.')
else: else:
typer.echo('Replay buffer is not active.') out_console.print('Replay buffer is not active.')
@app.command('status | ss') @app.command('status | ss')
@ -41,13 +44,13 @@ def status(ctx: typer.Context):
"""Get the status of the replay buffer.""" """Get the status of the replay buffer."""
resp = ctx.obj.get_replay_buffer_status() resp = ctx.obj.get_replay_buffer_status()
if resp.output_active: if resp.output_active:
typer.echo('Replay buffer is active.') out_console.print('Replay buffer is active.')
else: else:
typer.echo('Replay buffer is not active.') out_console.print('Replay buffer is not active.')
@app.command('save | sv') @app.command('save | sv')
def save(ctx: typer.Context): def save(ctx: typer.Context):
"""Save the replay buffer.""" """Save the replay buffer."""
ctx.obj.save_replay_buffer() ctx.obj.save_replay_buffer()
typer.echo('Replay buffer saved.') out_console.print('Replay buffer saved.')

View File

@ -3,11 +3,15 @@
from typing import Annotated from typing import Annotated
import typer import typer
from rich.console import Console
from rich.table import Table
from . import validate from . import validate
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -19,8 +23,20 @@ def main():
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 = (scene.get('sceneName') for scene in reversed(resp.scenes)) scenes = (
typer.echo('\n'.join(scenes)) (scene.get('sceneIndex'), scene.get('sceneName'))
for scene in reversed(resp.scenes)
)
table = Table(title='Scenes')
table.add_column('Scene Name', justify='left', style='cyan')
for scene_index, scene_name in scenes:
table.add_row(
scene_name,
)
out_console.print(table)
@app.command('current | get') @app.command('current | get')
@ -32,15 +48,15 @@ def current(
): ):
"""Get the current program scene or preview scene.""" """Get the current program scene or preview scene."""
if preview and not validate.studio_mode_enabled(ctx): if preview and not validate.studio_mode_enabled(ctx):
typer.echo('Studio mode is not enabled, cannot get preview scene.', err=True) err_console.print('Studio mode is not enabled, cannot get preview scene.')
raise typer.Exit(1) raise typer.Exit(1)
if preview: if preview:
resp = ctx.obj.get_current_preview_scene() resp = ctx.obj.get_current_preview_scene()
typer.echo(resp.current_preview_scene_name) out_console.print(resp.current_preview_scene_name)
else: else:
resp = ctx.obj.get_current_program_scene() resp = ctx.obj.get_current_program_scene()
typer.echo(resp.current_program_scene_name) out_console.print(resp.current_program_scene_name)
@app.command('switch | set') @app.command('switch | set')
@ -54,18 +70,16 @@ def switch(
): ):
"""Switch to a scene.""" """Switch to a scene."""
if preview and not validate.studio_mode_enabled(ctx): if preview and not validate.studio_mode_enabled(ctx):
typer.echo( err_console.print('Studio mode is not enabled, cannot set the preview scene.')
'Studio mode is not enabled, cannot set the preview scene.', err=True
)
raise typer.Exit(1) raise typer.Exit(1)
if not validate.scene_in_scenes(ctx, scene_name): if not validate.scene_in_scenes(ctx, scene_name):
typer.echo(f"Scene '{scene_name}' not found.", err=True) err_console.print(f"Scene '{scene_name}' not found.")
raise typer.Exit(1) raise typer.Exit(1)
if preview: if preview:
ctx.obj.set_current_preview_scene(scene_name) ctx.obj.set_current_preview_scene(scene_name)
typer.echo(f'Switched to preview scene: {scene_name}') out_console.print(f'Switched to preview scene: {scene_name}')
else: else:
ctx.obj.set_current_program_scene(scene_name) ctx.obj.set_current_program_scene(scene_name)
typer.echo(f'Switched to program scene: {scene_name}') out_console.print(f'Switched to program scene: {scene_name}')

View File

@ -1,11 +1,15 @@
"""module containing commands for manipulating scene collections.""" """module containing commands for manipulating scene collections."""
import typer import typer
from rich.console import Console
from rich.table import Table
from . import validate from . import validate
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -17,44 +21,49 @@ def main():
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()
typer.echo('\n'.join(resp.scene_collections))
table = Table(title='Scene Collections')
table.add_column('Scene Collection Name', justify='left', style='cyan')
for scene_collection_name in resp.scene_collections:
table.add_row(scene_collection_name)
out_console.print(table)
@app.command('current | get') @app.command('current | get')
def current(ctx: typer.Context): def current(ctx: typer.Context):
"""Get the current scene collection.""" """Get the current scene collection."""
resp = ctx.obj.get_scene_collection_list() resp = ctx.obj.get_scene_collection_list()
typer.echo(resp.current_scene_collection_name) out_console.print(resp.current_scene_collection_name)
@app.command('switch | set') @app.command('switch | set')
def switch(ctx: typer.Context, scene_collection_name: str): def switch(ctx: typer.Context, scene_collection_name: str):
"""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):
typer.echo(f"Scene collection '{scene_collection_name}' not found.", err=True) err_console.print(f"Scene collection '{scene_collection_name}' not found.")
raise typer.Exit(1) raise typer.Exit(1)
current_scene_collection = ( current_scene_collection = (
ctx.obj.get_scene_collection_list().current_scene_collection_name ctx.obj.get_scene_collection_list().current_scene_collection_name
) )
if scene_collection_name == current_scene_collection: if scene_collection_name == current_scene_collection:
typer.echo( err_console.print(
f'Scene collection "{scene_collection_name}" is already active.', err=True f'Scene collection "{scene_collection_name}" is already active.'
) )
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.set_current_scene_collection(scene_collection_name) ctx.obj.set_current_scene_collection(scene_collection_name)
typer.echo(f"Switched to scene collection '{scene_collection_name}'") out_console.print(f"Switched to scene collection '{scene_collection_name}'")
@app.command('create | new') @app.command('create | new')
def create(ctx: typer.Context, scene_collection_name: str): def create(ctx: typer.Context, scene_collection_name: str):
"""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):
typer.echo( err_console.print(f"Scene collection '{scene_collection_name}' already exists.")
f"Scene collection '{scene_collection_name}' already exists.", err=True
)
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.create_scene_collection(scene_collection_name) ctx.obj.create_scene_collection(scene_collection_name)
typer.echo(f'Created scene collection {scene_collection_name}') out_console.print(f'Created scene collection {scene_collection_name}')

View File

@ -4,11 +4,15 @@ from collections.abc import Callable
from typing import Annotated, Optional from typing import Annotated, Optional
import typer import typer
from rich.console import Console
from rich.table import Table
from . import validate from . import validate
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -17,15 +21,34 @@ def main():
@app.command('list | ls') @app.command('list | ls')
def list(ctx: typer.Context, scene_name: str): def list(
ctx: typer.Context,
scene_name: str = typer.Argument(
None, help='Scene name (optional, defaults to current scene)'
),
):
"""List all items in a scene.""" """List all items in a scene."""
if not scene_name:
scene_name = ctx.obj.get_current_program_scene().scene_name
if not validate.scene_in_scenes(ctx, scene_name): if not validate.scene_in_scenes(ctx, scene_name):
typer.echo(f"Scene '{scene_name}' not found.", err=True) err_console.print(f"Scene '{scene_name}' not found.")
typer.Exit(1) raise typer.Exit(1)
resp = ctx.obj.get_scene_item_list(scene_name) resp = ctx.obj.get_scene_item_list(scene_name)
items = (item.get('sourceName') for item in resp.scene_items) items = [item.get('sourceName') for item in resp.scene_items]
typer.echo('\n'.join(items))
if not items:
err_console.print(f"No items found in scene '{scene_name}'.")
raise typer.Exit(1)
table = Table(title=f'Items in Scene: {scene_name}')
table.add_column('Item Name', justify='left', style='cyan')
for item in items:
table.add_row(item)
out_console.print(table)
def _validate_scene_name_and_item_name( def _validate_scene_name_and_item_name(

View File

@ -1,10 +1,13 @@
"""module for controlling OBS stream functionality.""" """module for controlling OBS stream functionality."""
import typer import typer
from rich.console import Console
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -23,11 +26,11 @@ def start(ctx: typer.Context):
"""Start streaming.""" """Start streaming."""
active, _ = _get_streaming_status(ctx) active, _ = _get_streaming_status(ctx)
if active: if active:
typer.echo('Streaming is already in progress, cannot start.', err=True) err_console.print('Streaming is already in progress, cannot start.')
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.start_stream() ctx.obj.start_stream()
typer.echo('Streaming started successfully.') out_console.print('Streaming started successfully.')
@app.command('stop | st') @app.command('stop | st')
@ -35,11 +38,11 @@ def stop(ctx: typer.Context):
"""Stop streaming.""" """Stop streaming."""
active, _ = _get_streaming_status(ctx) active, _ = _get_streaming_status(ctx)
if not active: if not active:
typer.echo('Streaming is not in progress, cannot stop.', err=True) err_console.print('Streaming is not in progress, cannot stop.')
raise typer.Exit(1) raise typer.Exit(1)
ctx.obj.stop_stream() ctx.obj.stop_stream()
typer.echo('Streaming stopped successfully.') out_console.print('Streaming stopped successfully.')
@app.command('toggle | tg') @app.command('toggle | tg')
@ -47,9 +50,9 @@ def toggle(ctx: typer.Context):
"""Toggle streaming.""" """Toggle streaming."""
resp = ctx.obj.toggle_stream() resp = ctx.obj.toggle_stream()
if resp.output_active: if resp.output_active:
typer.echo('Streaming started successfully.') out_console.print('Streaming started successfully.')
else: else:
typer.echo('Streaming stopped successfully.') out_console.print('Streaming stopped successfully.')
@app.command('status | ss') @app.command('status | ss')
@ -62,15 +65,19 @@ def status(ctx: typer.Context):
minutes = int(seconds // 60) minutes = int(seconds // 60)
seconds = int(seconds % 60) seconds = int(seconds % 60)
if minutes > 0: if minutes > 0:
typer.echo( out_console.print(
f'Streaming is in progress for {minutes} minutes and {seconds} seconds.' f'Streaming is in progress for {minutes} minutes and {seconds} seconds.'
) )
else: else:
if seconds > 0: if seconds > 0:
typer.echo(f'Streaming is in progress for {seconds} seconds.') out_console.print(
f'Streaming is in progress for {seconds} seconds.'
)
else: else:
typer.echo('Streaming is in progress for less than a second.') out_console.print(
'Streaming is in progress for less than a second.'
)
else: else:
typer.echo('Streaming is in progress.') out_console.print('Streaming is in progress.')
else: else:
typer.echo('Streaming is not in progress.') err_console.print('Streaming is not in progress.')

View File

@ -1,10 +1,13 @@
"""module containing commands for manipulating studio mode in OBS.""" """module containing commands for manipulating studio mode in OBS."""
import typer import typer
from rich.console import Console
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -16,14 +19,14 @@ def main():
def enable(ctx: typer.Context): def enable(ctx: typer.Context):
"""Enable studio mode.""" """Enable studio mode."""
ctx.obj.set_studio_mode_enabled(True) ctx.obj.set_studio_mode_enabled(True)
typer.echo('Studio mode has been enabled.') out_console.print('Studio mode has been enabled.')
@app.command('disable | off') @app.command('disable | off')
def disable(ctx: typer.Context): def disable(ctx: typer.Context):
"""Disable studio mode.""" """Disable studio mode."""
ctx.obj.set_studio_mode_enabled(False) ctx.obj.set_studio_mode_enabled(False)
typer.echo('Studio mode has been disabled.') out_console.print('Studio mode has been disabled.')
@app.command('toggle | tg') @app.command('toggle | tg')
@ -32,10 +35,10 @@ def toggle(ctx: typer.Context):
resp = ctx.obj.get_studio_mode_enabled() resp = ctx.obj.get_studio_mode_enabled()
if resp.studio_mode_enabled: if resp.studio_mode_enabled:
ctx.obj.set_studio_mode_enabled(False) ctx.obj.set_studio_mode_enabled(False)
typer.echo('Studio mode is now disabled.') out_console.print('Studio mode is now disabled.')
else: else:
ctx.obj.set_studio_mode_enabled(True) ctx.obj.set_studio_mode_enabled(True)
typer.echo('Studio mode is now enabled.') out_console.print('Studio mode is now enabled.')
@app.command('status | ss') @app.command('status | ss')
@ -43,6 +46,6 @@ def status(ctx: typer.Context):
"""Get the status of studio mode.""" """Get the status of studio mode."""
resp = ctx.obj.get_studio_mode_enabled() resp = ctx.obj.get_studio_mode_enabled()
if resp.studio_mode_enabled: if resp.studio_mode_enabled:
typer.echo('Studio mode is enabled.') out_console.print('Studio mode is enabled.')
else: else:
typer.echo('Studio mode is disabled.') out_console.print('Studio mode is disabled.')

View File

@ -1,10 +1,13 @@
"""module containing commands for manipulating virtual camera in OBS.""" """module containing commands for manipulating virtual camera in OBS."""
import typer import typer
from rich.console import Console
from .alias import AliasGroup from .alias import AliasGroup
app = typer.Typer(cls=AliasGroup) app = typer.Typer(cls=AliasGroup)
out_console = Console()
err_console = Console(stderr=True)
@app.callback() @app.callback()
@ -16,21 +19,21 @@ def main():
def start(ctx: typer.Context): def start(ctx: typer.Context):
"""Start the virtual camera.""" """Start the virtual camera."""
ctx.obj.start_virtual_cam() ctx.obj.start_virtual_cam()
typer.echo('Virtual camera started.') out_console.print('Virtual camera started.')
@app.command('stop | p') @app.command('stop | p')
def stop(ctx: typer.Context): def stop(ctx: typer.Context):
"""Stop the virtual camera.""" """Stop the virtual camera."""
ctx.obj.stop_virtual_cam() ctx.obj.stop_virtual_cam()
typer.echo('Virtual camera stopped.') out_console.print('Virtual camera stopped.')
@app.command('toggle | tg') @app.command('toggle | tg')
def toggle(ctx: typer.Context): def toggle(ctx: typer.Context):
"""Toggle the virtual camera.""" """Toggle the virtual camera."""
ctx.obj.toggle_virtual_cam() ctx.obj.toggle_virtual_cam()
typer.echo('Virtual camera toggled.') out_console.print('Virtual camera toggled.')
@app.command('status | ss') @app.command('status | ss')
@ -38,6 +41,6 @@ def status(ctx: typer.Context):
"""Get the status of the virtual camera.""" """Get the status of the virtual camera."""
resp = ctx.obj.get_virtual_cam_status() resp = ctx.obj.get_virtual_cam_status()
if resp.output_active: if resp.output_active:
typer.echo('Virtual camera is enabled.') out_console.print('Virtual camera is enabled.')
else: else:
typer.echo('Virtual camera is disabled.') out_console.print('Virtual camera is disabled.')