mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
ship-ci / ci (push) Waiting to run
The first route module through the appstate seam (#833). Picked BY MEASUREMENT, not by the plan's guess: a transitive dep-closure scan over every route group ranked audio-effects at 0 monkeypatch.setattr targets and exactly one exclusive helper. (The same scan disproved the plan's assumption that artists/aliases was free -- api_artist_links reaches _mb_http_get and _enrich_network_enabled, both setattr targets.) Bodies are verbatim. The only edits are mechanical: @app.get(...) -> @router.get(...) audio_effect_mappings.x -> appstate.audio_effect_mappings.x The singleton read must stay a module attribute resolved at call time, so a re-imported server re-publishes a fresh DB into the seam and monkeypatch reaches this module. `routers/` never imports `server`: server -> routers -> appstate. `app.include_router(...)` sits exactly where the routes used to be defined -- FastAPI matches in registration order, so the mount site preserves it. Verified by diffing the FULL route table against origin/main: 143 routes, identical paths, methods AND order. server.py: 9,445 -> 9,386 lines. `fastapi.Query` went dead with the move and was removed (the other four unused imports are pre-existing on main). Packaging: COPY routers/ /app/routers/ plus `!routers/` + `!routers/**` in .dockerignore (that file opens with a blanket `*`). Verified against the real docker daemon: routers/ reaches the build context, __pycache__ does not. Verified: pyflakes clean on routers/; no new undefined name in server.py; pytest 2348 passed (75 in the audio-effects + demo-mode suites); eslint 0 errors; boot smoke drives all five routes end-to-end (create -> read back -> activate -> clear -> delete -> 404 on missing -> 400 on bad body), Query(...) still 422s on a missing required param, and demo mode still 403s all four moved write routes while allowing the read. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
"""Audio-effects mapping API — the core-owned song/tone -> provider routing index.
|
|
|
|
Extracted verbatim from ``server.py`` (R3); only the decorator receiver
|
|
(``@app`` -> ``@router``) and the singleton read (``audio_effect_mappings`` ->
|
|
``appstate.audio_effect_mappings``) changed. The read must stay a module
|
|
attribute so a re-imported ``server`` re-publishes a fresh DB into the seam and
|
|
`monkeypatch.setattr` reaches this module — see ``appstate.py``.
|
|
"""
|
|
|
|
from fastapi import APIRouter, Body, Query
|
|
from fastapi.responses import JSONResponse
|
|
|
|
import appstate
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
def _audio_effects_error(exc: Exception):
|
|
return JSONResponse({"error": str(exc)}, status_code=400)
|
|
|
|
|
|
@router.get("/api/audio-effects/mappings")
|
|
def list_audio_effect_mappings(
|
|
song_key: str = Query(""),
|
|
filename: str = Query(""),
|
|
tone_key: str = Query(""),
|
|
provider_id: str = Query(""),
|
|
):
|
|
try:
|
|
return {
|
|
"mappings": appstate.audio_effect_mappings.list(
|
|
song_key=song_key,
|
|
filename=filename,
|
|
tone_key=tone_key,
|
|
provider_id=provider_id,
|
|
)
|
|
}
|
|
except ValueError as exc:
|
|
return _audio_effects_error(exc)
|
|
|
|
|
|
@router.post("/api/audio-effects/mappings")
|
|
def upsert_audio_effect_mapping(data: dict = Body(...)):
|
|
try:
|
|
mapping = appstate.audio_effect_mappings.upsert(data)
|
|
except ValueError as exc:
|
|
return _audio_effects_error(exc)
|
|
return {"ok": True, "mapping": mapping}
|
|
|
|
|
|
@router.delete("/api/audio-effects/mappings/{mapping_id}")
|
|
def delete_audio_effect_mapping(mapping_id: int, provider_id: str = Query("")):
|
|
try:
|
|
deleted = appstate.audio_effect_mappings.delete(mapping_id, provider_id=provider_id)
|
|
except ValueError as exc:
|
|
return _audio_effects_error(exc)
|
|
if not deleted:
|
|
return JSONResponse({"error": "mapping not found"}, status_code=404)
|
|
return {"ok": True}
|
|
|
|
|
|
@router.post("/api/audio-effects/mappings/{mapping_id}/activate")
|
|
def activate_audio_effect_mapping(mapping_id: int, data: dict = Body(default_factory=dict)):
|
|
try:
|
|
provider_id = data.get("provider_id") if "provider_id" in data else data.get("providerId")
|
|
mapping = appstate.audio_effect_mappings.activate(mapping_id, provider_id="" if provider_id is None else provider_id)
|
|
except ValueError as exc:
|
|
return _audio_effects_error(exc)
|
|
if not mapping:
|
|
return JSONResponse({"error": "mapping not found"}, status_code=404)
|
|
return {"ok": True, "mapping": mapping}
|
|
|
|
|
|
@router.delete("/api/audio-effects/active-mapping")
|
|
def clear_audio_effect_active_mapping(song_key: str = Query(...), tone_key: str = Query("")):
|
|
try:
|
|
cleared = appstate.audio_effect_mappings.clear_active(song_key=song_key, tone_key=tone_key)
|
|
except ValueError as exc:
|
|
return _audio_effects_error(exc)
|
|
return {"ok": True, "cleared": cleared}
|