From 2ebf926b22f92b451e2b6a519b1da7b84f77e9f0 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sun, 5 Apr 2026 01:19:22 +0100 Subject: [PATCH] update routes now support PATCH and PUT operations remove the individual parameter PUT endpoints minor bump --- pyproject.toml | 2 +- src/vmr_http/app.py | 2 +- src/vmr_http/web/bus.py | 38 ++------ src/vmr_http/web/busmode.py | 26 +++--- src/vmr_http/web/strip.py | 146 ++---------------------------- src/vmr_http/web/stripcomp.py | 26 ++---- src/vmr_http/web/stripdenoiser.py | 30 ++---- src/vmr_http/web/stripgate.py | 26 ++---- uv.lock | 2 +- 9 files changed, 56 insertions(+), 242 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7c5e106..8caaf58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "vmr-http" -version = "0.2.0" +version = "0.3.0" description = "HTTP API for controlling Voicemeeter" readme = "README.md" authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }] diff --git a/src/vmr_http/app.py b/src/vmr_http/app.py index db7352c..c96a9d6 100644 --- a/src/vmr_http/app.py +++ b/src/vmr_http/app.py @@ -35,7 +35,7 @@ app.include_router(bus.router, prefix='/bus') def health_check(voicemeeter=Depends(get_voicemeeter_client)): """Health check endpoint to verify the service is running.""" try: - version = voicemeeter.version # Check if we can communicate with Voicemeeter + version = voicemeeter.version type_ = voicemeeter.type except CAPIError as e: raise HTTPException(status_code=503, detail=f'Voicemeeter API error: {str(e)}') diff --git a/src/vmr_http/web/bus.py b/src/vmr_http/web/bus.py index 8838ee3..ba17375 100644 --- a/src/vmr_http/web/bus.py +++ b/src/vmr_http/web/bus.py @@ -1,6 +1,6 @@ """module for bus-related endpoints.""" -from fastapi import APIRouter, Body, Depends +from fastapi import APIRouter, Depends from vmr_http.dependencies import get_voicemeeter_client from vmr_http.models.bus import BusParams @@ -8,17 +8,19 @@ from vmr_http.models.bus import BusParams from . import busmode router = APIRouter() -router.include_router(busmode.router, prefix='/mode', tags=['bus mode']) +router.include_router(busmode.router, tags=['bus mode']) +@router.patch('/{index}', tags=['bus']) @router.put('/{index}', tags=['bus']) -async def set_bus_params(index: int, request: BusParams, voicemeeter=Depends(get_voicemeeter_client)): - """Set multiple parameters of a bus at once.""" +async def update_bus_params(index: int, params: BusParams, voicemeeter=Depends(get_voicemeeter_client)): + """Update one or more parameters for the specified bus index.""" bus = voicemeeter.bus[index] - for key, value in request.model_dump(exclude_unset=True).items(): + updated = {} + for key, value in params.model_dump(exclude_unset=True).items(): setattr(bus, key, value) - - return {key: getattr(bus, key) for key in request.model_dump(exclude_unset=True)} + updated[key] = getattr(bus, key) + return updated @router.get('/{index}/gain', tags=['bus']) @@ -27,29 +29,7 @@ async def get_gain(index: int, voicemeeter=Depends(get_voicemeeter_client)): return {'gain': voicemeeter.bus[index].gain} -@router.put('/{index}/gain', tags=['bus']) -async def set_gain( - index: int, - gain: float = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the gain value for the specified bus index.""" - voicemeeter.bus[index].gain = gain - return {'gain': voicemeeter.bus[index].gain} - - @router.get('/{index}/mute', tags=['bus']) async def get_mute(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current mute status for the specified bus index.""" return {'mute': voicemeeter.bus[index].mute} - - -@router.put('/{index}/mute', tags=['bus']) -async def set_mute( - index: int, - mute: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the mute status for the specified bus index.""" - voicemeeter.bus[index].mute = mute - return {'mute': voicemeeter.bus[index].mute} diff --git a/src/vmr_http/web/busmode.py b/src/vmr_http/web/busmode.py index 066108d..96d5b5c 100644 --- a/src/vmr_http/web/busmode.py +++ b/src/vmr_http/web/busmode.py @@ -1,6 +1,6 @@ """module for bus mode related endpoints.""" -from fastapi import APIRouter, Body, Depends, HTTPException +from fastapi import APIRouter, Depends, HTTPException from vmr_http.dependencies import get_voicemeeter_client @@ -23,23 +23,19 @@ _readable_busmodes = { _reversed_busmodes = {v: k for k, v in _readable_busmodes.items()} -@router.get('/{index}') -async def get_bus_mode(index: int, voicemeeter=Depends(get_voicemeeter_client)): - """Get the current bus mode for the specified bus index.""" - return {'mode': _readable_busmodes.get(voicemeeter.bus[index].mode.get(), 'Unknown')} - - -@router.put('/{index}') -async def set_bus_mode( - index: int, - mode: str = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the bus mode for the specified bus index.""" +@router.patch('/{index}/mode') +@router.put('/{index}/mode') +async def update_bus_mode(index: int, mode: str, voicemeeter=Depends(get_voicemeeter_client)): + """Update the bus mode for the specified bus index.""" if mode not in _reversed_busmodes: raise HTTPException( status_code=400, detail=f'Invalid mode. Valid modes are: {", ".join(_reversed_busmodes.keys())}' ) - setattr(voicemeeter.bus[index].mode, _reversed_busmodes[mode], True) return {'mode': _readable_busmodes[_reversed_busmodes[mode]]} + + +@router.get('/{index}/mode') +async def get_bus_mode(index: int, voicemeeter=Depends(get_voicemeeter_client)): + """Get the current bus mode for the specified bus index.""" + return {'mode': _readable_busmodes.get(voicemeeter.bus[index].mode.get(), 'Unknown')} diff --git a/src/vmr_http/web/strip.py b/src/vmr_http/web/strip.py index 0161994..73cc539 100644 --- a/src/vmr_http/web/strip.py +++ b/src/vmr_http/web/strip.py @@ -1,6 +1,6 @@ """module for strip-related endpoints.""" -from fastapi import APIRouter, Body, Depends +from fastapi import APIRouter, Depends from vmr_http.dependencies import get_voicemeeter_client from vmr_http.models.strip import StripParams @@ -13,14 +13,16 @@ router.include_router(stripgate.router, prefix='/gate', tags=['strip gate']) router.include_router(stripdenoiser.router, prefix='/denoiser', tags=['strip denoiser']) +@router.patch('/{index}', tags=['strip']) @router.put('/{index}', tags=['strip']) -async def set_strip_params(index: int, request: StripParams, voicemeeter=Depends(get_voicemeeter_client)): - """Set the parameters for the specified strip index.""" +async def update_strip_params(index: int, params: StripParams, voicemeeter=Depends(get_voicemeeter_client)): + """Update one or more parameters for the specified strip index.""" strip = voicemeeter.strip[index] - for key, value in request.model_dump(exclude_unset=True).items(): + updated = {} + for key, value in params.model_dump(exclude_unset=True).items(): setattr(strip, key, value) - - return {key: getattr(strip, key) for key in request.model_dump(exclude_unset=True)} + updated[key] = getattr(strip, key) + return updated @router.get('/{index}/gain', tags=['strip']) @@ -29,199 +31,67 @@ async def get_gain(index: int, voicemeeter=Depends(get_voicemeeter_client)): return {'gain': voicemeeter.strip[index].gain} -@router.put('/{index}/gain', tags=['strip']) -async def set_gain( - index: int, - gain: float = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the gain value for the specified strip index.""" - voicemeeter.strip[index].gain = gain - return {'gain': voicemeeter.strip[index].gain} - - @router.get('/{index}/mute', tags=['strip']) async def get_mute(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current mute status for the specified strip index.""" return {'mute': voicemeeter.strip[index].mute} -@router.put('/{index}/mute', tags=['strip']) -async def set_mute( - index: int, - mute: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the mute status for the specified strip index.""" - voicemeeter.strip[index].mute = mute - return {'mute': voicemeeter.strip[index].mute} - - @router.get('/{index}/mono', tags=['strip']) async def get_mono(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current mono status for the specified strip index.""" return {'mono': voicemeeter.strip[index].mono} -@router.put('/{index}/mono', tags=['strip']) -async def set_mono( - index: int, - mono: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the mono status for the specified strip index.""" - voicemeeter.strip[index].mono = mono - return {'mono': voicemeeter.strip[index].mono} - - @router.get('/{index}/solo', tags=['strip']) async def get_solo(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current solo status for the specified strip index.""" return {'solo': voicemeeter.strip[index].solo} -@router.put('/{index}/solo', tags=['strip']) -async def set_solo( - index: int, - solo: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the solo status for the specified strip index.""" - voicemeeter.strip[index].solo = solo - return {'solo': voicemeeter.strip[index].solo} - - @router.get('/{index}/A1', tags=['strip']) async def get_A1(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current A1 output status for the specified strip index.""" return {'A1': voicemeeter.strip[index].A1} -@router.put('/{index}/A1', tags=['strip']) -async def set_A1( - index: int, - A1: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the A1 output status for the specified strip index.""" - voicemeeter.strip[index].A1 = A1 - return {'A1': voicemeeter.strip[index].A1} - - @router.get('/{index}/A2', tags=['strip']) async def get_A2(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current A2 output status for the specified strip index.""" return {'A2': voicemeeter.strip[index].A2} -@router.put('/{index}/A2', tags=['strip']) -async def set_A2( - index: int, - A2: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the A2 output status for the specified strip index.""" - voicemeeter.strip[index].A2 = A2 - return {'A2': voicemeeter.strip[index].A2} - - @router.get('/{index}/A3', tags=['strip']) async def get_A3(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current A3 output status for the specified strip index.""" return {'A3': voicemeeter.strip[index].A3} -@router.put('/{index}/A3', tags=['strip']) -async def set_A3( - index: int, - A3: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the A3 output status for the specified strip index.""" - voicemeeter.strip[index].A3 = A3 - return {'A3': voicemeeter.strip[index].A3} - - @router.get('/{index}/A4', tags=['strip']) async def get_A4(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current A4 output status for the specified strip index.""" return {'A4': voicemeeter.strip[index].A4} -@router.put('/{index}/A4', tags=['strip']) -async def set_A4( - index: int, - A4: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the A4 output status for the specified strip index.""" - voicemeeter.strip[index].A4 = A4 - return {'A4': voicemeeter.strip[index].A4} - - @router.get('/{index}/A5', tags=['strip']) async def get_A5(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current A5 output status for the specified strip index.""" return {'A5': voicemeeter.strip[index].A5} -@router.put('/{index}/A5', tags=['strip']) -async def set_A5( - index: int, - A5: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the A5 output status for the specified strip index.""" - voicemeeter.strip[index].A5 = A5 - return {'A5': voicemeeter.strip[index].A5} - - @router.get('/{index}/B1', tags=['strip']) async def get_B1(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current B1 output status for the specified strip index.""" return {'B1': voicemeeter.strip[index].B1} -@router.put('/{index}/B1', tags=['strip']) -async def set_B1( - index: int, - B1: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the B1 output status for the specified strip index.""" - voicemeeter.strip[index].B1 = B1 - return {'B1': voicemeeter.strip[index].B1} - - @router.get('/{index}/B2', tags=['strip']) async def get_B2(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current B2 output status for the specified strip index.""" return {'B2': voicemeeter.strip[index].B2} -@router.put('/{index}/B2', tags=['strip']) -async def set_B2( - index: int, - B2: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the B2 output status for the specified strip index.""" - voicemeeter.strip[index].B2 = B2 - return {'B2': voicemeeter.strip[index].B2} - - @router.get('/{index}/B3', tags=['strip']) async def get_B3(index: int, voicemeeter=Depends(get_voicemeeter_client)): """Get the current B3 output status for the specified strip index.""" return {'B3': voicemeeter.strip[index].B3} - - -@router.put('/{index}/B3', tags=['strip']) -async def set_B3( - index: int, - B3: bool = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the B3 output status for the specified strip index.""" - voicemeeter.strip[index].B3 = B3 - return {'B3': voicemeeter.strip[index].B3} diff --git a/src/vmr_http/web/stripcomp.py b/src/vmr_http/web/stripcomp.py index 21e3814..50573a1 100644 --- a/src/vmr_http/web/stripcomp.py +++ b/src/vmr_http/web/stripcomp.py @@ -1,6 +1,6 @@ """module for strip compressor related endpoints.""" -from fastapi import APIRouter, Body, Depends +from fastapi import APIRouter, Depends from vmr_http.dependencies import get_voicemeeter_client from vmr_http.models.strip import StripCompParams @@ -8,14 +8,16 @@ from vmr_http.models.strip import StripCompParams router = APIRouter() +@router.patch('/{index}/comp') @router.put('/{index}/comp') -async def set_strip_comp_params(index: int, request: StripCompParams, voicemeeter=Depends(get_voicemeeter_client)): - """Set the compressor parameters for the specified strip index.""" +async def update_strip_comp_params(index: int, params: StripCompParams, voicemeeter=Depends(get_voicemeeter_client)): + """Update one or more compressor parameters for the specified strip index.""" strip_comp = voicemeeter.strip[index].comp - for key, value in request.model_dump(exclude_unset=True).items(): + updated = {} + for key, value in params.model_dump(exclude_unset=True).items(): setattr(strip_comp, key, value) - - return {key: getattr(strip_comp, key) for key in request.model_dump(exclude_unset=True)} + updated[key] = getattr(strip_comp, key) + return updated @router.get('/{index}/comp/knob') @@ -23,15 +25,3 @@ async def get_strip_comp_knob(index: int, voicemeeter=Depends(get_voicemeeter_cl """Get the current compressor knob value for the specified strip index.""" strip_comp = voicemeeter.strip[index].comp return {'knob': strip_comp.knob} - - -@router.put('/{index}/comp/knob') -async def set_strip_comp_knob( - index: int, - knob: float = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the compressor knob value for the specified strip index.""" - strip_comp = voicemeeter.strip[index].comp - strip_comp.knob = knob - return {'knob': strip_comp.knob} diff --git a/src/vmr_http/web/stripdenoiser.py b/src/vmr_http/web/stripdenoiser.py index 614f25d..bb5b545 100644 --- a/src/vmr_http/web/stripdenoiser.py +++ b/src/vmr_http/web/stripdenoiser.py @@ -1,6 +1,6 @@ """module for strip denoiser related endpoints.""" -from fastapi import APIRouter, Body, Depends +from fastapi import APIRouter, Depends from vmr_http.dependencies import get_voicemeeter_client from vmr_http.models.strip import StripDenoiserParams @@ -8,18 +8,18 @@ from vmr_http.models.strip import StripDenoiserParams router = APIRouter() +@router.patch('/{index}/denoiser') @router.put('/{index}/denoiser') -async def set_strip_denoiser_params( - index: int, - request: StripDenoiserParams, - voicemeeter=Depends(get_voicemeeter_client), +async def update_strip_denoiser_params( + index: int, params: StripDenoiserParams, voicemeeter=Depends(get_voicemeeter_client) ): - """Set the denoiser parameters for the specified strip index.""" + """Update one or more denoiser parameters for the specified strip index.""" strip_denoiser = voicemeeter.strip[index].denoiser - for key, value in request.model_dump(exclude_unset=True).items(): + updated = {} + for key, value in params.model_dump(exclude_unset=True).items(): setattr(strip_denoiser, key, value) - - return {key: getattr(strip_denoiser, key) for key in request.model_dump(exclude_unset=True)} + updated[key] = getattr(strip_denoiser, key) + return updated @router.get('/{index}/denoiser/knob') @@ -30,15 +30,3 @@ async def get_strip_denoiser_knob( """Get the denoiser knob value for the specified strip index.""" strip_denoiser = voicemeeter.strip[index].denoiser return {'knob': strip_denoiser.knob} - - -@router.put('/{index}/denoiser/knob') -async def set_strip_denoiser_knob( - index: int, - knob: float = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the denoiser knob value for the specified strip index.""" - strip_denoiser = voicemeeter.strip[index].denoiser - strip_denoiser.knob = knob - return {'knob': strip_denoiser.knob} diff --git a/src/vmr_http/web/stripgate.py b/src/vmr_http/web/stripgate.py index 0727b5b..d08478e 100644 --- a/src/vmr_http/web/stripgate.py +++ b/src/vmr_http/web/stripgate.py @@ -1,6 +1,6 @@ """module for strip gate related endpoints.""" -from fastapi import APIRouter, Body, Depends +from fastapi import APIRouter, Depends from vmr_http.dependencies import get_voicemeeter_client from vmr_http.models.strip import StripGateParams @@ -8,14 +8,16 @@ from vmr_http.models.strip import StripGateParams router = APIRouter() +@router.patch('/{index}/gate') @router.put('/{index}/gate') -async def set_strip_gate_params(index: int, request: StripGateParams, voicemeeter=Depends(get_voicemeeter_client)): - """Set the gate parameters for the specified strip index.""" +async def update_strip_gate_params(index: int, params: StripGateParams, voicemeeter=Depends(get_voicemeeter_client)): + """Update one or more gate parameters for the specified strip index.""" strip_gate = voicemeeter.strip[index].gate - for key, value in request.model_dump(exclude_unset=True).items(): + updated = {} + for key, value in params.model_dump(exclude_unset=True).items(): setattr(strip_gate, key, value) - - return {key: getattr(strip_gate, key) for key in request.model_dump(exclude_unset=True)} + updated[key] = getattr(strip_gate, key) + return updated @router.get('/{index}/gate/knob') @@ -23,15 +25,3 @@ async def get_strip_gate_knob(index: int, voicemeeter=Depends(get_voicemeeter_cl """Get the current gate knob value for the specified strip index.""" strip_gate = voicemeeter.strip[index].gate return {'knob': strip_gate.knob} - - -@router.put('/{index}/gate/knob') -async def set_strip_gate_knob( - index: int, - knob: float = Body(..., embed=True), - voicemeeter=Depends(get_voicemeeter_client), -): - """Set the gate knob value for the specified strip index.""" - strip_gate = voicemeeter.strip[index].gate - strip_gate.knob = knob - return {'knob': strip_gate.knob} diff --git a/uv.lock b/uv.lock index bb22709..c255a0b 100644 --- a/uv.lock +++ b/uv.lock @@ -1151,7 +1151,7 @@ wheels = [ [[package]] name = "vmr-http" -version = "0.2.0" +version = "0.3.0" source = { editable = "." } dependencies = [ { name = "fastapi" },