feedBack/main.py
Job 1a5b49f83b feat: extract instruments from hardcoded logic into plugins
Replace ~15 scattered hardcoded guitar/bass checks across Python and JS
with a unified InstrumentRegistry. Guitar, Bass, Drums, and Keys now
ship as bundled instrument plugins under plugins/instrument_<name>/,
each defining its own tunings, roles, arrangement mappings, string/key
counts, detection strategy, and icon.

Backend:
- lib/instruments.py — registry with schema validation and offset-to-MIDI
- lib/routers/instruments.py — GET /api/instruments endpoint
- Plugin loader recognizes type:instrument and registers definitions
- lib/tunings.py is registry-aware (profiles, tuning validation, presets)
- Arrangement routing uses role flags/names from registry
- Progression instrument_for_arrangement accepts registry, returns None for unknown
- Settings API validates against registered instrument IDs
- /api/tunings populates both tuningMidis AND tunings (fixes tuner sync)
- Non-stringed instruments skip string_count/tuning validation

Frontend:
- badges.js: dynamic instrument pills, key count selector (keyboard),
  per-instrument icons from plugin assets, tuner grays out for non-pitched
- songs.js: auto-filters library by current instrument's arrangement roles,
  filter options derived from registry
- instruments-settings.js: editable instrument cards with custom tunings,
  arrangement names, string counts. Persisted via instrument_overrides
- index.html: new Instruments tab, default_arrangement moved to selector
- working-tuning.js: registry-aware instrument normalization
2026-07-16 20:43:11 +02:00

58 lines
1.8 KiB
Python

"""Programmatic entry point for the FeedBack server.
Using ``uvicorn.run()`` with ``log_config=None`` prevents uvicorn from calling
``logging.config.dictConfig(LOGGING_CONFIG)`` during its startup sequence.
This ensures that the structlog pipeline installed by ``configure_logging()``
is active for **all** uvicorn messages, including the earliest lifecycle lines
logged before the ASGI startup hook fires, such as:
"Started server process [PID]"
"Waiting for application startup"
Usage::
python main.py # default host 0.0.0.0, port 8000
HOST=127.0.0.1 PORT=8001 python main.py
"""
import os
import sys
# Ensure lib/ is on the path for local development (Docker sets PYTHONPATH).
_lib = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib")
if _lib not in sys.path:
sys.path.insert(0, _lib)
def run() -> None:
"""Configure logging and start the uvicorn server.
``configure_logging()`` is called first so that when uvicorn starts with
``log_config=None`` it finds the structlog handlers already installed.
Uvicorn will not call its own ``dictConfig()``, so those handlers are never
overwritten and every log record passes through the same structured
pipeline.
"""
from logging_setup import configure_logging
configure_logging()
import uvicorn
host = os.environ.get("HOST", "0.0.0.0")
port = int(os.environ.get("PORT", "8000"))
uvicorn.run(
"server:app",
host=host,
port=port,
# Skips uvicorn's logging.config.dictConfig(LOGGING_CONFIG) call so
# our structlog handlers are never overwritten. Every uvicorn log
# record — including early startup messages — passes through the same
# structured pipeline.
log_config=None,
)
if __name__ == "__main__":
run()