3 Commits

Author SHA1 Message Date
61a37bcd0f patch bump 2026-02-28 15:48:28 +00:00
b62ee185c3 upd use section in README 2026-02-28 15:39:39 +00:00
c7365bfe4e add new help formatters for different kinds of commands 2026-02-28 15:39:26 +00:00
10 changed files with 139 additions and 46 deletions

View File

@@ -5,6 +5,8 @@
---
This CLI is still in an early stage of development with many more things that could be implemented. However, the commands that are implemented should be working without issues.
## Install
#### With uv
@@ -47,24 +49,35 @@ export VBAN_CLI_STREAMNAME=Command1
## Use
```console
Usage: vban-cli COMMAND
### Strip Command
╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮
│ bus Control the bus parameters. │
│ strip Control the strip parameters. │
│ --help (-h) Display this message and exit. │
│ --version Display application version. │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Parameters ─────────────────────────────────────────────────────────────────────────────────────╮
│ --kind Kind of Voicemeeter [env var: VBAN_CLI_KIND] [default: potato] │
│ --host VBAN host [env var: VBAN_CLI_HOST] [default: localhost] │
│ --port VBAN port [env var: VBAN_CLI_PORT] [default: 6980] │
│ --streamname VBAN stream name [env var: VBAN_CLI_STREAMNAME] [default: Command1] │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Usage: vban-cli strip <index> COMMAND [ARGS]
examples:
```console
vban-cli strip 0 mute true
vban-cli strip 1 A1 true
vban-cli strip 2 gain -18.7
```
For every command and subcommand there exists a `--help` flag for further usage information.
see `vban-cli strip --help` for more info.
### Bus Command
Usage: vban-cli bus <index> COMMAND [ARGS]
examples:
```console
vban-cli bus mode normal
vban-cli bus mute true
```
see `vban-cli bus --help` for more info.
---

View File

@@ -1,6 +1,6 @@
[project]
name = "vban-cli"
version = "0.4.1"
version = "0.4.2"
description = "A command-line interface for Voicemeeter leveraging VBAN."
readme = "README.md"
license = { text = "LICENSE" }

View File

@@ -4,9 +4,9 @@ from cyclopts import App, Argument, Parameter
from . import console
from .context import Context
from .help import CustomHelpFormatter
from .help import BusHelpFormatter
app = App(name='bus', help_formatter=CustomHelpFormatter())
app = App(name='bus', help_formatter=BusHelpFormatter())
# See https://github.com/onyx-and-iris/vban-cli?tab=readme-ov-file#implementation-notes - 1.
# app.command(eq.app.meta, name='eq')

View File

@@ -3,9 +3,9 @@ from typing import Annotated
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import CustomHelpFormatter
from .help import StripSubcommandHelpFormatter
app = App(name='comp', help_formatter=CustomHelpFormatter())
app = App(name='comp', help_formatter=StripSubcommandHelpFormatter())
@app.meta.default

View File

@@ -3,9 +3,9 @@ from typing import Annotated
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import CustomHelpFormatter
from .help import StripSubcommandHelpFormatter
app = App(name='denoiser', help_formatter=CustomHelpFormatter())
app = App(name='denoiser', help_formatter=StripSubcommandHelpFormatter())
@app.meta.default

View File

@@ -3,11 +3,11 @@ from typing import Annotated
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import CustomHelpFormatter
from .help import CellHelpFormatter, EqHelpFormatter
cell_app = App(name='cell', help_formatter=CustomHelpFormatter())
cell_app = App(name='cell', help_formatter=CellHelpFormatter())
app = App(name='eq', help_formatter=CustomHelpFormatter())
app = App(name='eq', help_formatter=EqHelpFormatter())
app.command(cell_app.meta, name='cell')

View File

@@ -3,9 +3,9 @@ from typing import Annotated
from cyclopts import App, Argument, Parameter
from .context import Context
from .help import CustomHelpFormatter
from .help import StripSubcommandHelpFormatter
app = App(name='gate', help_formatter=CustomHelpFormatter())
app = App(name='gate', help_formatter=StripSubcommandHelpFormatter())
@app.meta.default

View File

@@ -4,31 +4,24 @@ from cyclopts.help import DefaultFormatter, HelpPanel
from rich.console import Console, ConsoleOptions
class CustomHelpFormatter(DefaultFormatter):
"""Custom help formatter that injects an index argument into the usage line and filters it out from the parameters list.
This formatter modifies the usage line to include an <index> argument after the 'strip' command,
and filters out any parameters related to 'index' from the Parameters section of the help output.
"""
def render_usage(self, console: Console, options: ConsoleOptions, usage) -> None:
"""Render the usage line with index argument injected."""
if usage:
modified_usage = re.sub(
r'(\S+\s+[a-z]+)\s+(COMMAND)', r'\1 <index> \2', str(usage)
)
console.print(f'[bold]Usage:[/bold] {modified_usage}')
class BaseHelpFormatter(DefaultFormatter):
"""Base help formatter that provides common functionality."""
def __call__(
self, console: Console, options: ConsoleOptions, panel: HelpPanel
) -> None:
"""Render a help panel, filtering out the index parameter from Parameters sections."""
"""Render a help panel, filtering out hidden parameters from Parameters sections."""
if panel.title == 'Parameters':
filtered_entries = [
entry
for entry in panel.entries
if not (
entry.names and any('index' in name.lower() for name in entry.names)
entry.names
and any(
param in name.lower()
for name in entry.names
for param in self.get_filtered_params()
)
)
]
@@ -41,3 +34,90 @@ class CustomHelpFormatter(DefaultFormatter):
super().__call__(console, options, filtered_panel)
else:
super().__call__(console, options, panel)
def get_filtered_params(self):
"""Return list of parameter names to filter out of help output."""
return ['index', 'band', 'ctx', 'target', 'eq_kind']
class StripHelpFormatter(BaseHelpFormatter):
"""Help formatter for strip commands that injects <index> after 'strip'."""
def render_usage(self, console: Console, options: ConsoleOptions, usage) -> None:
"""Render the usage line with index argument injected after 'strip'.
Handles both command groups (COMMAND) and individual commands (commandname [ARGS/OPTIONS]).
"""
if usage:
modified_usage = re.sub(
r'(\S+\s+strip)\s+(\w+\s+\[[^\]]+\]|\w+\s+\[[^\]]+\]|\w+(?:\s+\[[^\]]+\])*|COMMAND)',
r'\1 <index> \2',
str(usage),
)
if modified_usage == str(usage):
modified_usage = re.sub(
r'(\S+\s+strip)\s+(\w+)', r'\1 <index> \2', str(usage)
)
console.print(f'[bold]Usage:[/bold] {modified_usage}')
class StripSubcommandHelpFormatter(BaseHelpFormatter):
"""Help formatter for strip subcommands that injects <index> after 'strip'."""
def render_usage(self, console: Console, options: ConsoleOptions, usage) -> None:
"""Render the usage line with index argument injected after 'strip'."""
if usage:
modified_usage = re.sub(
r'(\S+\s+strip)\s+(\w+)\s+(COMMAND)', r'\1 <index> \2 \3', str(usage)
)
console.print(f'[bold]Usage:[/bold] {modified_usage}')
class BusHelpFormatter(BaseHelpFormatter):
"""Help formatter for bus commands that injects <index> after 'bus'."""
def render_usage(self, console: Console, options: ConsoleOptions, usage) -> None:
"""Render the usage line with index argument injected after 'bus'.
Handles both command groups (COMMAND) and individual commands (commandname [ARGS/OPTIONS])."""
if usage:
modified_usage = re.sub(
r'(\S+\s+bus)\s+(\w+\s+\[[^\]]+\]|\w+\s+\[[^\]]+\]|\w+(?:\s+\[[^\]]+\])*|COMMAND)',
r'\1 <index> \2',
str(usage),
)
if modified_usage == str(usage):
modified_usage = re.sub(
r'(\S+\s+bus)\s+(\w+)', r'\1 <index> \2', str(usage)
)
console.print(f'[bold]Usage:[/bold] {modified_usage}')
class EqHelpFormatter(BaseHelpFormatter):
"""Help formatter for eq commands that works with both strip and bus commands.
Injects <index> after 'strip' or 'bus' and <band> after 'cell'."""
def render_usage(self, console: Console, options: ConsoleOptions, usage) -> None:
"""Render the usage line with proper <index> placement for both strip and bus commands."""
if usage:
modified_usage = re.sub(
r'(\S+\s+)(\w+)(\s+eq\s+)(COMMAND)', r'\1\2 <index>\3\4', str(usage)
)
console.print(f'[bold]Usage:[/bold] {modified_usage}')
class CellHelpFormatter(BaseHelpFormatter):
"""Help formatter for cell commands that works with both strip and bus commands.
Injects <index> after 'strip' or 'bus' and <band> after 'cell'."""
def render_usage(self, console: Console, options: ConsoleOptions, usage) -> None:
"""Render the usage line with proper <index> and <band> placement."""
if usage:
modified_usage = re.sub(
r'(\S+\s+)(\w+)(\s+eq\s+cell\s+)(COMMAND)',
r'\1\2 <index>\3<band> \4',
str(usage),
)
console.print(f'[bold]Usage:[/bold] {modified_usage}')

View File

@@ -4,9 +4,9 @@ from cyclopts import App, Argument, Parameter
from . import comp, console, denoiser, eq, gate
from .context import Context
from .help import CustomHelpFormatter
from .help import StripHelpFormatter
app = App(name='strip', help_formatter=CustomHelpFormatter())
app = App(name='strip', help_formatter=StripHelpFormatter())
app.command(eq.app.meta, name='eq')
app.command(comp.app.meta, name='comp')
app.command(gate.app.meta, name='gate')

2
uv.lock generated
View File

@@ -124,7 +124,7 @@ wheels = [
[[package]]
name = "vban-cli"
version = "0.4.1"
version = "0.4.2"
source = { editable = "." }
dependencies = [
{ name = "cyclopts" },