Commit Graph

52 Commits

Author SHA1 Message Date
cottongin
d014a14cbe docs: update README with public endpoints, auth changes, and new features
Reflects all changes since the initial README: Bearer auth on internal
endpoints, public index page and /public/* API, dashboard enhancements
(announced checkbox, ping button, tabbed UI, copy-to-clipboard), .env
file support, and new config variables.

Made-with: Cursor
2026-04-02 12:42:29 -04:00
cottongin
4dd3f43ae3 feat: lock playlist/shows endpoints behind Bearer token auth
The five internal endpoints (/playlist, /playlist/{position}, /shows,
/shows/by-episode/{ep}, /shows/{id}) now require admin authentication.
Dashboard JS, Sopel plugin, and Limnoria plugin updated to send the
token on GET requests. Six new 401 tests added.

Made-with: Cursor
2026-04-02 12:06:27 -04:00
cottongin
425a7047c3 feat: add public index page with censored playlist and live reveals
Public-facing page at / shows the current show's playlist with tracks
obscured until the admin marks them as announced. Tracks reveal in
real-time via a new unauthenticated /ws/public WebSocket. Server-side
censorship on /public/playlist strips track details from unannounced
items and sanitizes announced tracks to only expose frontend-needed
fields (no raw_json, track_id, etc). Past episodes are browsable with
fully revealed but sanitized tracklists.

Also fixes RuntimeError on backfill shutdown by closing the httpx
client on the same event loop that created it.

Made-with: Cursor
2026-04-01 23:50:41 -04:00
cottongin
11f13c86b5 feat: add announced checkbox to track rows
Add a persistent "announced" checkbox after each track's Announce button.
The state is stored in a new `announced` column on `show_tracks` and is
auto-set when the Announce button is pressed. The checkbox is also freely
togglable, and announced tracks have their Announce button disabled.

Also fixes .env leakage in test_config.py (pass _env_file=None) and adds
tests for the new DB method, API endpoint, and announce side-effect.

Made-with: Cursor
2026-04-01 23:00:09 -04:00
cottongin
a5f77187b3 feat: add dashboard ping button and .env file support
Add a "Send IRC Message" section to the admin dashboard that sends a
configurable privmsg to any IRC nick or channel via all connected bots.
New POST /admin/ping endpoint broadcasts a "privmsg" WebSocket message
type, handled by both Limnoria and Sopel plugins.

Also enable pydantic-settings .env file loading (python-dotenv) and
add .env.example documenting all NTR_* configuration variables.

Made-with: Cursor
2026-04-01 22:20:18 -04:00
cottongin
82049ab47f feat: delay show rotation during live recording window
Decouple the like-window boundary (Wed 10pm ET) from when the system
rotates to a new show. NTR_SHOW_ROTATION_DELAY_HOURS=2 keeps the
previous week's show visible during the ~2 hour recording, then creates
the new show at midnight.

Made-with: Cursor
2026-04-01 21:29:42 -04:00
cottongin
a328684af0 fix: handle SoundCloud API 5xx errors with client_id refresh, backoff, and cursor fallback
SoundCloud began rejecting the fabricated pagination cursor with 500
errors. Fixed cursor user_id padding (zfill 22→20) to match the
documented format, added 5xx retry with exponential backoff in _api_get,
and added a fallback in fetch_likes that drops the fabricated cursor
when it causes persistent 500s.

Made-with: Cursor
2026-03-25 08:20:20 -04:00
cottongin
b353f606e5 docs: fix AI disclaimer in README 2026-03-12 08:30:16 -04:00
cottongin
8d9d565c04 docs: add AI disclaimer to README 2026-03-12 08:28:54 -04:00
cottongin
0f99e7914b docs: add IRC commands/plugin setup to README, update WebSocket docs
README was missing IRC command reference, plugin installation/config
guidance, and referenced nonexistent plugin READMEs. The WebSocket
docs in api.md were stale — subscribe message now documents role and
client_id fields, status message now includes the clients array.

Made-with: Cursor
2026-03-12 08:26:16 -04:00
cottongin
911dd3d5dd feat: tabbed show interface and copy-to-clipboard button
Replace the link column with a Copy button that copies
"Title by Artist - URL" to clipboard. Replace the current/previous
show layout with a horizontal scrollable tab bar showing all shows
from the database, most recent first. Tabs lazy-load and cache
show data on click.

Made-with: Cursor
2026-03-12 08:15:22 -04:00
cottongin
f244749293 fix: auto-normalize http(s) URLs to ws(s) in bot plugins
Users behind reverse proxies naturally configure https:// URLs.
The websocket-client library requires ws:// or wss:// schemes.
Both plugins now auto-convert before connecting.

Made-with: Cursor
2026-03-12 08:01:11 -04:00
cottongin
f6840a777c chore: add verbose WS logging to both bot plugins
Log each phase: thread start, TCP connect, subscribe sent, message
received (type), status updates with client list, connection closed
reasons, connection refused/timeout as distinct warnings.

Made-with: Cursor
2026-03-12 07:55:04 -04:00
cottongin
d6d5ac10e6 fix: separate bot vs viewer WebSocket connections, add client identification
The dashboard's own WS connection was being counted as a bot subscriber,
causing "1 bot connected" with no bots actually present. Now WS clients
send a role ("bot" or "viewer") in the subscribe message. Only bots count
toward the subscriber total. Bot plugins also send a configurable client_id
so the dashboard shows which specific bots are connected.

Made-with: Cursor
2026-03-12 07:51:55 -04:00
cottongin
658c0d4a15 docs: add dashboard and announce API documentation
Made-with: Cursor
2026-03-12 07:24:42 -04:00
cottongin
e31a9503db Add WebSocket announce listener to both IRC bot plugins
- Limnoria: add wsUrl, announceChannel config; __init__, die, _ws_listener
- Sopel: add ws_url, announce_channel config; setup/shutdown, _ws_listener
- Feature parity: subscribe to WS, receive announce msgs, send to IRC channel
- Deferred websocket-client import to avoid load failure if not installed

Made-with: Cursor
2026-03-12 07:22:49 -04:00
cottongin
a7849e6cd9 feat: styled login and dashboard pages with Pico CSS dark theme
Made-with: Cursor
2026-03-12 07:21:39 -04:00
cottongin
92136f0508 Wire dashboard into app
- Add web_user, web_password, secret_key as optional params to create_app
- Conditionally mount dashboard router when all three are set
- Pass settings from main.py to create_app
- Add tests for no dashboard/login routes when config absent

Made-with: Cursor
2026-03-12 07:19:01 -04:00
cottongin
e90f44439b Add dashboard router with session auth, announce endpoint, and WebSocket handler
- Login/logout/dashboard with HMAC-signed session cookies
- POST /admin/announce with session or bearer auth
- WS /ws/announce for subscribe/broadcast
- Static stubs: login.html, dashboard.html

Made-with: Cursor
2026-03-12 07:17:20 -04:00
cottongin
788225b3b6 feat: add WebSocket announce manager
Made-with: Cursor
2026-03-12 07:14:04 -04:00
cottongin
7ed7ace578 feat: add optional dashboard config fields
Made-with: Cursor
2026-03-12 07:12:10 -04:00
cottongin
e5c06a2f67 docs: add live announce dashboard implementation plan
13-task TDD plan covering WebSocket manager, dashboard auth, announce
endpoint, styled UI, and bot plugin WS clients for both Sopel and Limnoria.

Made-with: Cursor
2026-03-12 07:08:02 -04:00
cottongin
47a78b09a7 docs: add live announce dashboard design document
Approved design for a web dashboard that lets the host announce tracks
to IRC in real-time during live shows via WebSocket push.

Made-with: Cursor
2026-03-12 07:04:53 -04:00
cottongin
9664b8225d feat: add human-readable datetime formatting and !lastshow command
IRC plugins now format datetimes as "Wed Mar 11, 10:00 PM EDT" instead
of raw ISO 8601. Configurable timezone defaults to America/New_York.

Adds !lastshow N command to both Limnoria and Sopel plugins, returning
track N from the previous week's show via existing API endpoints.

Made-with: Cursor
2026-03-12 05:31:50 -04:00
cottongin
ae66242935 chore: add MIT license
Made-with: Cursor
2026-03-12 03:51:01 -04:00
cottongin
359a11dd4a chore: exclude chat-summaries from version control
Add chat-summaries/ to .gitignore and remove tracked files from index.

Made-with: Cursor
2026-03-12 03:48:21 -04:00
cottongin
03ce201a47 docs: add chat summary for IRC bot plugins implementation
Made-with: Cursor
2026-03-12 03:42:27 -04:00
cottongin
05bcf184ac fix: playlist truncation overflow and align logging across plugins
- Fix format_playlist truncation: +4 → +5 to account for ", ..." suffix
- Add API error logging to all Sopel command handlers (matching Limnoria)
- Add long-track truncation test case

Made-with: Cursor
2026-03-12 03:24:29 -04:00
cottongin
b63c851d14 docs: add IRC bot plugins design and implementation plan
Made-with: Cursor
2026-03-12 03:20:45 -04:00
cottongin
5c227766f1 test: add tests for IRC plugin formatting and API helpers
Made-with: Cursor
2026-03-12 03:20:05 -04:00
cottongin
6dd7aee2f2 feat(limnoria): add NtrPlaylist IRC plugin
Made-with: Cursor
2026-03-12 03:18:04 -04:00
cottongin
2a00cc263f feat(sopel): add NtR playlist IRC plugin
Made-with: Cursor
2026-03-12 03:11:04 -04:00
cottongin
1d08580a45 feat: add GET /shows/by-episode/{episode_number} endpoint
Allows looking up shows by episode number instead of internal DB ID,
enabling IRC bot commands like !playlist 530 to resolve directly.

Made-with: Cursor
2026-03-12 02:33:42 -04:00
cottongin
b529edecc3 chore: update .gitignore for SQLite WAL, venv, and tool caches
Made-with: Cursor
2026-03-12 02:10:55 -04:00
cottongin
cb3ae403cf feat: add historical backfill with --init CLI and episode numbering
Adds a --init mode that seeds the database with past shows from a given
anchor episode/date forward, batch-fetching likes from SoundCloud and
partitioning them into weekly buckets. Episode numbers are tracked in
the shows table and auto-incremented by the poller for new shows.

Includes full API documentation (docs/api.md) and updated README.

Made-with: Cursor
2026-03-12 02:09:15 -04:00
cottongin
c88826ac4d fix: wire show_day/show_hour config into API, simplify AddTrackRequest
- create_app now accepts show_day/show_hour params instead of
  hardcoding Wednesday 22:00
- main.py passes config values through to create_app
- Removed soundcloud_url from AddTrackRequest (not implemented);
  track_id is now required

Made-with: Cursor
2026-03-12 01:46:23 -04:00
cottongin
485f0c1e35 fix: address linting issues
Made-with: Cursor
2026-03-12 01:44:00 -04:00
cottongin
c2b032ea0c docs: add README with quick start and API reference
Made-with: Cursor
2026-03-12 01:43:11 -04:00
cottongin
219e2d4545 feat: add main entry point wiring API server and poller
Made-with: Cursor
2026-03-12 01:41:16 -04:00
cottongin
2e22a2b3ff feat: add FastAPI routes for playlist, shows, admin, and health
Made-with: Cursor
2026-03-12 01:40:04 -04:00
cottongin
ab81ad4fd4 feat: add poller with supervised restart loop
Made-with: Cursor
2026-03-12 01:40:00 -04:00
cottongin
f7dde6781a feat: add user resolution and likes fetching with 401 retry
Made-with: Cursor
2026-03-12 01:36:28 -04:00
cottongin
49846f9d7e feat: add SoundCloud client with client_id extraction
Made-with: Cursor
2026-03-12 01:36:03 -04:00
cottongin
d2ab4e94c0 feat: add database query methods for tracks, shows, and show_tracks
Made-with: Cursor
2026-03-12 01:33:17 -04:00
cottongin
2b38fda25a feat: add database module with schema initialization
Made-with: Cursor
2026-03-12 01:21:33 -04:00
cottongin
537d9ecd8b feat: add data models for Track, Show, ShowTrack
Made-with: Cursor
2026-03-12 01:18:20 -04:00
cottongin
38dda5dda0 feat: add week boundary computation with DST handling
Made-with: Cursor
2026-03-12 01:18:12 -04:00
cottongin
7e41f55e26 feat: add configuration module with pydantic-settings
Made-with: Cursor
2026-03-12 01:16:51 -04:00
cottongin
c40559822e scaffold: project structure with pyproject.toml and test config
Made-with: Cursor
2026-03-12 01:16:16 -04:00
cottongin
22f6b5cbca design: sync unliked tracks — remove from show and re-compact positions
Nick is the host; if he unlikes a track, the service should respect
that and remove it from the show playlist. Positions re-compact after
removal. The tracks table retains the record for historical reference.

Made-with: Cursor
2026-03-12 01:13:19 -04:00