mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-22 12:52:29 +00:00
work keeper-chart prefs (3) + favorites toggle + tags list + saved toggle + session/continue — 7 routes across 5 domains, all meta_db-only (0 setattr, 0 helpers). Bodies verbatim; @app -> @router, meta_db -> appstate.meta_db, _clean_str from reqfields. These were scattered singletons with no natural neighbor, so they're grouped as "small library/user-state endpoints" and mounted once. All paths are distinct and non-overlapping, so registering them together doesn't change routing: verified the route SET is identical to origin/main (143) AND that no moved path shadows or is shadowed by another (order-independence check). server.py: 7,880 -> 7,833. Verified: pyflakes clean; route set identical + order-independent; pytest 2401 passed; packaging guard 45; eslint 0. Boot smoke: tags/session GET 200, favorites/saved toggle (400 on missing filename), work/charts 200. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
"""Small meta_db-backed library / user-state endpoints — work keeper-chart
|
|
prefs, favorites, personal tags, saved-for-later, and continue-playing.
|
|
|
|
Extracted verbatim from ``server.py`` (R3); edits: ``@app`` -> ``@router``,
|
|
``meta_db`` -> ``appstate.meta_db``, ``_clean_str`` from ``reqfields``. All paths
|
|
are distinct and non-overlapping, so mounting them together (rather than at each
|
|
original scattered site) does not change routing.
|
|
"""
|
|
|
|
from fastapi import APIRouter
|
|
from fastapi.responses import JSONResponse
|
|
|
|
import appstate
|
|
from reqfields import _clean_str
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/api/work/{work_key:path}/charts")
|
|
def api_get_work_charts(work_key: str):
|
|
"""All charts in a work + which is the keeper (your pick vs auto-pick)."""
|
|
return appstate.meta_db.work_charts(work_key)
|
|
|
|
|
|
@router.put("/api/work/{work_key:path}/preferred")
|
|
def api_set_work_preferred(work_key: str, data: dict):
|
|
"""Set the keeper chart of a work: body {filename}. The filename must be a
|
|
current member of the work. Returns the refreshed chart list."""
|
|
fn = (data.get("filename") or "").strip()
|
|
if not fn:
|
|
return JSONResponse({"error": "filename is required"}, 400)
|
|
members = {c["filename"] for c in appstate.meta_db.work_charts(work_key)["charts"]}
|
|
if fn not in members:
|
|
return JSONResponse({"error": "filename is not a chart of this work"}, 400)
|
|
appstate.meta_db.set_chart_preferred(work_key, fn)
|
|
return appstate.meta_db.work_charts(work_key)
|
|
|
|
|
|
@router.delete("/api/work/{work_key:path}/preferred")
|
|
def api_reset_work_preferred(work_key: str):
|
|
"""Reset a work to auto-pick (drop the explicit preferred)."""
|
|
appstate.meta_db.clear_chart_preferred(work_key)
|
|
return appstate.meta_db.work_charts(work_key)
|
|
|
|
|
|
@router.post("/api/favorites/toggle")
|
|
def toggle_favorite(data: dict):
|
|
"""Toggle a song's favorite status."""
|
|
filename = data.get("filename", "")
|
|
if not filename:
|
|
return {"error": "No filename"}
|
|
new_state = appstate.meta_db.toggle_favorite(filename)
|
|
return {"favorite": new_state}
|
|
|
|
|
|
@router.get("/api/tags")
|
|
def list_tags():
|
|
"""All personal tags in use (over still-present songs), most-used first —
|
|
powers the tag filter UI."""
|
|
return {"tags": appstate.meta_db.all_tags()}
|
|
|
|
|
|
@router.post("/api/saved/toggle")
|
|
def api_toggle_saved(data: dict):
|
|
"""Add/remove a song on the reserved Saved-for-Later playlist."""
|
|
filename = _clean_str(data.get("filename"))
|
|
if not filename:
|
|
return JSONResponse({"error": "filename required"}, status_code=400)
|
|
return {"saved": appstate.meta_db.toggle_saved(filename)}
|
|
|
|
|
|
@router.get("/api/session/continue")
|
|
def api_session_continue():
|
|
"""The Continue-Playing card's song (most recent play) or null."""
|
|
return appstate.meta_db.continue_session()
|