mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-18 06:22:15 +00:00
refactor(server): extract the wishlist routes into routers/wanted.py (R3) (#847)
Free after _clean_str moved to lib (#841): wanted's deps are JSONResponse, _clean_str (reqfields), app + meta_db (seam). 3 routes (list/add/remove), 0 setattr targets, 0 helpers to relocate. Bodies verbatim; @app -> @router, meta_db -> appstate.meta_db. include_router at the original site; 143-route table identical. No test retargeting. server.py: 8,003 -> 7,974 (this branch is independent of the chart PR). Verified: pyflakes clean; route table identical; pytest 2401 passed (52 in test_wanted_api); eslint 0. Boot smoke: add wishlist entry -> list -> delete, 400 on missing artist+title (_clean_str path). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7c87538d6b
commit
cbc65458e3
@@ -55,8 +55,8 @@ without a *signed* exemption" is unenforceable.
|
|||||||
## Planned, NOT exempt (owned by split plans — listed so nothing falls between states)
|
## Planned, NOT exempt (owned by split plans — listed so nothing falls between states)
|
||||||
|
|
||||||
core `static/app.js` (11,852) · `static/highway.js` (4,168, whole file) · `server.py`
|
core `static/app.js` (11,852) · `static/highway.js` (4,168, whole file) · `server.py`
|
||||||
(7,909 — was 14,037; ratcheted by the R3 `MetadataDB` + `AudioEffectsMappingDB`
|
(7,880 — was 14,037; ratcheted by the R3 `MetadataDB` + `AudioEffectsMappingDB`
|
||||||
extractions and six `routers/` modules) ·
|
extractions and seven `routers/` modules) ·
|
||||||
`lib/metadata_db.py` (4,373 — new in R3; the `MetadataDB` class alone is 4,018 lines
|
`lib/metadata_db.py` (4,373 — new in R3; the `MetadataDB` class alone is 4,018 lines
|
||||||
and is a monolith in its own right, to be split per-table once the router train
|
and is a monolith in its own right, to be split per-table once the router train
|
||||||
lands) · `static/v3/songs.js` (4,134) · `static/capabilities/audio-session.js`
|
lands) · `static/v3/songs.js` (4,134) · `static/capabilities/audio-session.js`
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""Wishlist / "wanted" API (feedBack#636) — songs the user wants but doesn't own.
|
||||||
|
|
||||||
|
Extracted verbatim from ``server.py`` (R3); edits: ``@app`` -> ``@router``,
|
||||||
|
``meta_db`` -> ``appstate.meta_db``, ``_clean_str`` from ``reqfields``.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
import appstate
|
||||||
|
from reqfields import _clean_str
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/api/wanted")
|
||||||
|
def api_list_wanted():
|
||||||
|
"""The wishlist — songs the user wants but doesn't own yet (newest first)."""
|
||||||
|
return {"wanted": appstate.meta_db.list_wanted()}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/api/wanted")
|
||||||
|
def api_add_wanted(data: dict):
|
||||||
|
"""Add a not-owned song to the wishlist. `artist`/`title` are required (at
|
||||||
|
least one non-empty); `source`/`source_ref`/`note` are optional. Idempotent
|
||||||
|
on identity so producers (find_more ownership-diff, manual add) can re-post."""
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
return JSONResponse({"error": "body must be an object"}, status_code=400)
|
||||||
|
artist = _clean_str(data.get("artist"))
|
||||||
|
title = _clean_str(data.get("title"))
|
||||||
|
if not artist and not title:
|
||||||
|
return JSONResponse({"error": "artist or title required"}, status_code=400)
|
||||||
|
row = appstate.meta_db.add_wanted(
|
||||||
|
artist=artist, title=title,
|
||||||
|
source=_clean_str(data.get("source")) or "manual",
|
||||||
|
source_ref=_clean_str(data.get("source_ref")),
|
||||||
|
note=_clean_str(data.get("note")),
|
||||||
|
)
|
||||||
|
return {"ok": True, "wanted": row}
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/api/wanted/{wanted_id}")
|
||||||
|
def api_remove_wanted(wanted_id: int):
|
||||||
|
"""Remove a wishlist entry by id."""
|
||||||
|
return {"ok": appstate.meta_db.remove_wanted(wanted_id)}
|
||||||
@@ -55,7 +55,7 @@ from dlc_paths import _get_dlc_dir, _resolve_dlc_path
|
|||||||
# Lives in lib/ because that is the one core dir every packaging path copies.
|
# Lives in lib/ because that is the one core dir every packaging path copies.
|
||||||
import appstate
|
import appstate
|
||||||
# Extracted route modules. They import `appstate`, never `server` — one-way graph.
|
# Extracted route modules. They import `appstate`, never `server` — one-way graph.
|
||||||
from routers import audio_effects, artist_aliases, loops, playlists, ws_highway, chart
|
from routers import audio_effects, artist_aliases, loops, playlists, ws_highway, chart, wanted
|
||||||
import sloppak as sloppak_mod
|
import sloppak as sloppak_mod
|
||||||
import loosefolder as loosefolder_mod
|
import loosefolder as loosefolder_mod
|
||||||
# Pure text-matching engine for MusicBrainz enrichment (P8): denoise/score/
|
# Pure text-matching engine for MusicBrainz enrichment (P8): denoise/score/
|
||||||
@@ -5375,37 +5375,8 @@ def api_session_continue():
|
|||||||
|
|
||||||
|
|
||||||
# ── Wishlist / "wanted" API (feedBack#636 item 4) ─────────────────────────────
|
# ── Wishlist / "wanted" API (feedBack#636 item 4) ─────────────────────────────
|
||||||
|
# Mounted here (registration order). Implementation in lib/routers/wanted.py.
|
||||||
@app.get("/api/wanted")
|
app.include_router(wanted.router)
|
||||||
def api_list_wanted():
|
|
||||||
"""The wishlist — songs the user wants but doesn't own yet (newest first)."""
|
|
||||||
return {"wanted": meta_db.list_wanted()}
|
|
||||||
|
|
||||||
|
|
||||||
@app.post("/api/wanted")
|
|
||||||
def api_add_wanted(data: dict):
|
|
||||||
"""Add a not-owned song to the wishlist. `artist`/`title` are required (at
|
|
||||||
least one non-empty); `source`/`source_ref`/`note` are optional. Idempotent
|
|
||||||
on identity so producers (find_more ownership-diff, manual add) can re-post."""
|
|
||||||
if not isinstance(data, dict):
|
|
||||||
return JSONResponse({"error": "body must be an object"}, status_code=400)
|
|
||||||
artist = _clean_str(data.get("artist"))
|
|
||||||
title = _clean_str(data.get("title"))
|
|
||||||
if not artist and not title:
|
|
||||||
return JSONResponse({"error": "artist or title required"}, status_code=400)
|
|
||||||
row = meta_db.add_wanted(
|
|
||||||
artist=artist, title=title,
|
|
||||||
source=_clean_str(data.get("source")) or "manual",
|
|
||||||
source_ref=_clean_str(data.get("source_ref")),
|
|
||||||
note=_clean_str(data.get("note")),
|
|
||||||
)
|
|
||||||
return {"ok": True, "wanted": row}
|
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/api/wanted/{wanted_id}")
|
|
||||||
def api_remove_wanted(wanted_id: int):
|
|
||||||
"""Remove a wishlist entry by id."""
|
|
||||||
return {"ok": meta_db.remove_wanted(wanted_id)}
|
|
||||||
|
|
||||||
|
|
||||||
# ── Loops API ────────────────────────────────────────────────────────────────
|
# ── Loops API ────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user