8 Commits

Author SHA1 Message Date
1a0fb979e0 minor bump 2026-03-02 00:29:02 +00:00
080e26f75f add more eq cell commands 2026-03-02 00:28:32 +00:00
f6d82c5064 remove duplicate entries 2026-03-01 22:08:41 +00:00
627ada3b09 md fix 2026-03-01 22:06:07 +00:00
f389eb53b8 patch bump 2026-03-01 21:59:49 +00:00
341c81fde1 minor bump 2026-03-01 21:16:11 +00:00
e062da51ed add Recorder Command to README 2026-03-01 21:15:51 +00:00
c82a021708 add recorder subcommand group
add validation module
2026-03-01 21:15:42 +00:00
8 changed files with 236 additions and 18 deletions

10
.gitignore vendored
View File

@@ -1,13 +1,3 @@
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info
# Virtual environments
.venv
# Generated by ignr: github.com/onyx-and-iris/ignr # Generated by ignr: github.com/onyx-and-iris/ignr
## Python ## ## Python ##

View File

@@ -85,6 +85,10 @@ examples:
```console ```console
vban-cli strip 0 eq cell 0 on false vban-cli strip 0 eq cell 0 on false
vban-cli strip 3 eq cell 2 freq 1500
vban-cli strip 4 eq cell 5 type 5
``` ```
see `vban-cli strip eq cell --help` for more info. see `vban-cli strip eq cell --help` for more info.
@@ -117,14 +121,30 @@ vban-cli command restart
see `vban-cli command --help` for more info. see `vban-cli command --help` for more info.
### Recorder Command
Usage: vban-cli recorder COMMAND
examples:
```console
vban-cli recorder play
vban-cli recorder rew
vban-cli recorder replay
```
see `vban-cli recorder --help` for more info.
### Sendtext Command ### Sendtext Command
Usage: vban-cli sendtext TEXT Usage: vban-cli sendtext TEXT
*To Voicemeeter*
examples: examples:
*To Voicemeeter*
```console ```console
vban-cli sendtext 'Strip[0].Mute=1;Bus[0].Mono=2' vban-cli sendtext 'Strip[0].Mute=1;Bus[0].Mono=2'
``` ```

View File

@@ -1,11 +1,11 @@
[project] [project]
name = "vban-cli" name = "vban-cli"
version = "0.6.0" version = "0.8.0"
description = "A command-line interface for Voicemeeter leveraging VBAN." description = "A command-line interface for Voicemeeter leveraging VBAN."
readme = "README.md" readme = "README.md"
license = { text = "LICENSE" } license = { text = "LICENSE" }
requires-python = ">=3.13" requires-python = ">=3.13"
dependencies = ["cyclopts>=4.6.0", "loguru>=0.7.3", "vban-cmd>=2.7.1"] dependencies = ["cyclopts>=4.6.0", "loguru>=0.7.3", "vban-cmd>=2.8.1"]
classifiers = [ classifiers = [
"Development Status :: 3 - Alpha", "Development Status :: 3 - Alpha",
"Programming Language :: Python", "Programming Language :: Python",

View File

@@ -5,7 +5,7 @@ import vban_cmd
from cyclopts import App, Argument, Parameter, config from cyclopts import App, Argument, Parameter, config
from . import __version__ as version from . import __version__ as version
from . import bus, command, console, strip from . import bus, command, console, recorder, strip
from .context import Context from .context import Context
app = App( app = App(
@@ -17,6 +17,7 @@ app = App(
app.command(strip.app.meta, name='strip') app.command(strip.app.meta, name='strip')
app.command(bus.app.meta, name='bus') app.command(bus.app.meta, name='bus')
app.command(command.app, name='command') app.command(command.app, name='command')
app.command(recorder.app, name='recorder')
app.register_install_completion_command() app.register_install_completion_command()

View File

@@ -64,7 +64,7 @@ def cell_launcher(
Only channel 0 is supported, see https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 3. Only channel 0 is supported, see https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 3.
""" """
additional_kwargs = {} additional_kwargs = {}
command, bound, _ = app.parse_args(tokens) command, bound, _ = cell_app.parse_args(tokens)
additional_kwargs['target'] = target.channel[0].cell[band] additional_kwargs['target'] = target.channel[0].cell[band]
return command(*bound.args, **bound.kwargs, **additional_kwargs) return command(*bound.args, **bound.kwargs, **additional_kwargs)
@@ -88,3 +88,83 @@ def cell_on(
# console.out.print(target.on) # console.out.print(target.on)
return return
target.on = new_state target.on = new_state
@cell_app.command(name='freq')
def cell_freq(
new_freq: Annotated[float, Argument()] = None,
*,
target: Annotated[object, Parameter(show=False)] = None,
):
"""Get or set the frequency of the specified EQ cell.
Parameters
----------
new_freq : float
If provided, sets the frequency to this value. If not provided, the current frequency is printed.
"""
if new_freq is None:
# See https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 2.
# console.out.print(target.f)
return
target.f = new_freq
@cell_app.command(name='gain')
def cell_gain(
new_gain: Annotated[float, Argument()] = None,
*,
target: Annotated[object, Parameter(show=False)] = None,
):
"""Get or set the gain of the specified EQ cell.
Parameters
----------
new_gain : float
If provided, sets the gain to this value. If not provided, the current gain is printed.
"""
if new_gain is None:
# See https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 2.
# console.out.print(target.gain)
return
target.gain = new_gain
@cell_app.command(name='quality')
def cell_q(
new_q: Annotated[float, Argument()] = None,
*,
target: Annotated[object, Parameter(show=False)] = None,
):
"""Get or set the Q of the specified EQ cell.
Parameters
----------
new_q : float
If provided, sets the Q to this value. If not provided, the current Q is printed.
"""
if new_q is None:
# See https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 2.
# console.out.print(target.q)
return
target.q = new_q
@cell_app.command(name='type')
def cell_type(
new_type: Annotated[int, Argument()] = None,
*,
target: Annotated[object, Parameter(show=False)] = None,
):
"""Get or set the type of the specified EQ cell.
Parameters
----------
new_type : int
If provided, sets the type to this value. If not provided, the current type is printed.
"""
if new_type is None:
# See https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 2.
# console.out.print(target.type)
return
target.type = new_type

118
src/vban_cli/recorder.py Normal file
View File

@@ -0,0 +1,118 @@
from pathlib import Path
from typing import Annotated
from cyclopts import App, Parameter, validators
from . import console, validation
from .context import Context
from .help import BaseHelpFormatter
app = App(name='recorder', help_formatter=BaseHelpFormatter())
@app.command(name='play')
def play(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Start the recorder playback."""
ctx.client.recorder.play()
console.out.print('Recorder playback started.')
@app.command(name='stop')
def stop(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Stop the recorder playback."""
ctx.client.recorder.stop()
console.out.print('Recorder playback stopped.')
@app.command(name='pause')
def pause(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Pause the recorder playback."""
ctx.client.recorder.pause()
console.out.print('Recorder playback paused.')
@app.command(name='replay')
def replay(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Replay the recorder playback."""
ctx.client.recorder.replay()
console.out.print('Recorder playback replay started.')
@app.command(name='record')
def record(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Start recording."""
ctx.client.recorder.record()
console.out.print('Recording started.')
@app.command(name='ff')
def ff(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Fast forward the recorder playback."""
ctx.client.recorder.ff()
console.out.print('Recorder playback fast forwarded.')
@app.command(name='rew')
def rew(
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Rewind the recorder playback."""
ctx.client.recorder.rew()
console.out.print('Recorder playback rewound.')
@app.command(name='load')
def load(
file_path: Annotated[
Path,
Parameter(
help='The path to the recording file to load.',
validator=validators.Path(exists=True),
),
],
/,
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Load a file into the recorder.
note: This command may only work if vban-cli is running on localhost and may not work if vban-cli is running on a remote server."""
ctx.client.recorder.load(file_path)
console.out.print(f'Loaded file: {file_path}')
@app.command(name='goto')
def goto(
time_string: Annotated[
str,
Parameter(
help='The timestamp to go to in the recorder playback (format: HH:MM:SS).',
validator=validation.is_valid_time_string,
),
],
/,
*,
ctx: Annotated[Context, Parameter(show=False)] = None,
):
"""Go to a specific timestamp in the recorder playback."""
ctx.client.recorder.goto(time_string)
console.out.print(f'Went to timestamp {time_string} in recorder playback.')

View File

@@ -0,0 +1,9 @@
import re
def is_valid_time_string(type_, value: str) -> str:
"""Validate if the given string is a valid time format (HH:MM:SS)."""
pattern = r'^(?:[01]\d|2[0123]):(?:[012345]\d):(?:[012345]\d)$'
if not re.match(pattern, value):
raise ValueError('Invalid time format. Expected HH:MM:SS.')
return value

4
uv.lock generated
View File

@@ -124,7 +124,7 @@ wheels = [
[[package]] [[package]]
name = "vban-cli" name = "vban-cli"
version = "0.6.0" version = "0.8.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "cyclopts" }, { name = "cyclopts" },
@@ -141,7 +141,7 @@ requires-dist = [
[[package]] [[package]]
name = "vban-cmd" name = "vban-cmd"
version = "2.7.1" version = "2.8.0"
source = { editable = "../vban-cmd-python" } source = { editable = "../vban-cmd-python" }
[package.metadata] [package.metadata]