feedBack/lib
byrongamatos 13e68c2f83 feat(career): hours-per-genre odometer — honest wall-clock play time
Career v2, WS2. Nothing measured play time before (the achievements
plugin's final-position shortcut double-counts loops and mis-reads
seeks). Now:

- stats-recorder.js accrues WALL-CLOCK seconds across song:play/resume ↔
  pause/stop/ended spans (single spans clamp at 2h against suspend
  inflation) and piggybacks them as `seconds` on the POSTs it already
  sends; failed POSTs restore the accumulator; a session reset flushes
  first so time can't re-attribute to the next song/arrangement.
- POST /api/stats accepts optional `seconds` (finite, 0 < s ≤ 6h) on the
  scored and position branches, plus a new seconds-only branch for
  unscored plays that ran to the natural end — banks time WITHOUT
  touching the resume position (song:ended must not overwrite Continue)
  and still counts as playing today for the streak.
- song_stats gains additive idempotent `seconds_total`; record_session/
  touch_position accrue, new add_play_seconds() for the seconds-only
  path; the legacy-encoding stats merge sums seconds across duplicates.
- Passports surface it: "14.2 h in Blues" under the badge stamp and on
  the shelf cover sub-line — a true fact that only grows, never a
  target or a meter (Stage 5 post-cap, per the career design).

Tests: seconds accrual/validation/seconds-only branch (stats API),
per-instrument-and-genre summing (career), fmtHours formatting (vm).
Full suites: pytest 2480, JS 1165.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:08:54 +02:00
..
routers feat(career): hours-per-genre odometer — honest wall-clock play time 2026-07-13 15:08:54 +02:00
acoustid_match.py feat(enrichment): AcoustID audio-fingerprint identification (opt-in) (#759) 2026-07-05 00:17:43 +02:00
appconfig.py refactor(server): extract /api/tunings into routers/tunings.py + lib/appconfig.py (R3) (#858) 2026-07-11 01:43:19 +02:00
appstate.py refactor(server): carve the library scanner into lib/scan.py (R3b) (#901) 2026-07-12 01:26:02 +02:00
audio_effects_db.py refactor(server): extract AudioEffectsMappingDB into lib/audio_effects_db.py (R3) (#831) 2026-07-10 14:30:05 +02:00
audio.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
builtin_content.py refactor(server): carve builtin-content seeding into lib/builtin_content.py (R3b) (#900) 2026-07-12 00:50:50 +02:00
demo_mode.py fix(demo): the janitor re-entry guard actually works now (#902) (#909) 2026-07-12 11:30:53 +02:00
diagnostics_bundle.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
diagnostics_hardware.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
diagnostics_redact.py fix(tests): isolate plugin routes modules + redact .feedpak filenames (#736) 2026-07-03 13:01:07 +02:00
dlc_paths.py refactor(server): move DLC path resolution to lib/dlc_paths.py (R3 substrate) (#843) 2026-07-10 20:41:42 +02:00
drums.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
enrichment.py refactor(server): extract the song routes into routers/song.py (R3) (#864) 2026-07-11 13:14:52 +02:00
env_compat.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
gp2midi.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
gp2notation.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
gp2rs_gpx.py feat(gp_autosync): piecewise time-warp helpers + refine_sync onset pass (#787) 2026-07-05 20:08:19 +02:00
gp2rs.py fix(gp2rs): write beat times at 6-decimal precision so imported tempo matches Guitar Pro (#819) 2026-07-10 13:12:10 +02:00
gp8_audio_sync.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
gp_autosync.py fix(gp_autosync): slope-constrained DTW steps — stop path collapse on riff-based songs (#791) 2026-07-05 23:12:31 +02:00
jsonc.py feat(core): read .jsonc data files (strip C-style comments) (feedpak-spec §8) 2026-06-20 14:10:04 -07:00
library_registry.py refactor(server): extract library + collections routes + the provider registry (R3) (#866) 2026-07-11 14:56:12 +02:00
logging_setup.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
loosefolder.py Improve wording in terminology cleanup 2026-06-16 19:43:45 +01:00
lyrics_transcribe.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
mb_match.py feat(enrichment): alias-aware scoring — auto-confirm non-Latin-primary artists (#772) 2026-07-05 01:11:38 +02:00
metadata_db.py feat(career): hours-per-genre odometer — honest wall-clock play time 2026-07-13 15:08:54 +02:00
midi_import.py feat(drums): capture velocities alongside times in unmapped-percussion reporting (#808) 2026-07-07 23:58:49 +02:00
notation_lift.py fix: correctly import and notate multi-staff (piano/keys) tracks from GP8 (#692) 2026-07-02 08:40:20 +02:00
notation.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
progression.py Improve wording in terminology cleanup 2026-06-16 19:43:45 +01:00
reqfields.py refactor(server): extract the playlists routes into routers/playlists.py (R3) (#841) 2026-07-10 19:58:09 +02:00
safepath.py fix(library): serve art/load for songs mounted through a library junction (#766) 2026-07-05 00:15:50 +02:00
scan_worker.py refactor(server): extract MetadataDB into lib/metadata_db.py (R3) (#830) 2026-07-10 14:18:59 +02:00
scan.py refactor(server): carve the library scanner into lib/scan.py (R3b) (#901) 2026-07-12 01:26:02 +02:00
sloppak.py Drop dead whisperx entry from allowed lyrics_source set 2026-07-06 17:30:26 -04:00
song_score.py Clean release snapshot 2026-06-16 18:47:13 +02:00
song.py fix(gp-import): correct bass string count, lead/rhythm roles, preview note count (#601) 2026-06-26 18:58:35 +02:00
songmeta.py library: opt-in gap-fill — write confirmed missing metadata into the pack (R4a) (#724) 2026-07-02 20:52:11 +02:00
tailwind_rebuild.py fix(tailwind): stop the dev server rewriting a tracked file (#911) (#918) 2026-07-12 13:25:16 +02:00
tones.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
tunings.py Harden freqs_to_midis against NaN/Inf; badges read exact tuningMidis 2026-07-12 15:24:39 -05:00
vocal_pitch.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
wem_decode.py rename: slopsmith → feedBack, byron → got-feedBack (#537) 2026-06-23 11:03:01 +02:00
xp.py Clean release snapshot 2026-06-16 18:47:13 +02:00