First step of the stream/player/radio revamp (see revamp_plan.md). The radio
algorithm lived inline inside database.music_database.get_radio_tracks as raw
SQL tangled with selection logic — untestable without a live DB (which also
throws in the dev sandbox). Lifted the pure DECISIONS into core/radio/selection.py:
- parse_tags / merge_tags — JSON-or-CSV tag fields → ordered deduped list
- same_artist_cap — tier-1 30%-floored-at-5 cap
- build_like_conditions — OR-of-LIKEs SQL fragment + params per tier
- RadioCollector — dedup + cap + exclude-set + NOT-IN placeholder/value tracking
The DB method keeps the cursor work and now delegates every decision to these
helpers. Faithful extraction, not a rewrite — behavior unchanged.
This is the kettui foundation move: radio is now unit-testable, so Phase 2
(smart ranking — play-count / recency / feature seeding) becomes 'evolve a
tested function' instead of 'rewrite SQL and pray'.
Tests (tests/radio/):
- test_selection.py (22): unit coverage of every extracted helper
- test_get_radio_tracks_db.py (7): drive the REAL get_radio_tracks against
in-memory sqlite — tier fallback, dedup, exclude, file_path filter.
Behavior-pinned: these 7 pass against BOTH old inline and new extracted
code (refactor-equivalence proof). 52 adjacent DB+radio tests green.
22 lines
612 B
Python
22 lines
612 B
Python
"""Radio / auto-play recommendation logic.
|
|
|
|
Pure, DB-agnostic helpers that decide *what* radio should play. The SQL
|
|
execution stays in ``database.music_database.get_radio_tracks``; this package
|
|
owns the decisions (tag parsing, tier caps, dedup/collection, LIKE-condition
|
|
building) so they're unit-testable without a live DB — the seam Phase 2's
|
|
smarter ranking will plug into.
|
|
"""
|
|
|
|
from core.radio.selection import (
|
|
RadioCollector,
|
|
build_like_conditions,
|
|
parse_tags,
|
|
same_artist_cap,
|
|
)
|
|
|
|
__all__ = [
|
|
"RadioCollector",
|
|
"build_like_conditions",
|
|
"parse_tags",
|
|
"same_artist_cap",
|
|
]
|