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
10 KiB
NtR SoundCloud Fetcher -- API Reference
Base URL: http://127.0.0.1:8000 (configurable via NTR_HOST / NTR_PORT)
Public Endpoints
GET /health
Service health check.
Response
{
"status": "ok",
"poller_alive": true,
"last_fetch": "2026-03-12T02:00:00+00:00",
"current_week_track_count": 9
}
| Field | Type | Description |
|---|---|---|
status |
string | Always "ok" |
poller_alive |
boolean | Whether the background poller is running |
last_fetch |
string | null | ISO 8601 timestamp of last successful poll, or null if never |
current_week_track_count |
integer | Number of tracks in the current week's playlist |
GET /playlist
Returns the current week's full playlist.
Response
{
"show_id": 10,
"episode_number": 530,
"week_start": "2026-03-05T02:00:00+00:00",
"week_end": "2026-03-12T02:00:00+00:00",
"tracks": [
{
"show_id": 10,
"track_id": 12345,
"position": 1,
"title": "Night Drive",
"artist": "SomeArtist",
"permalink_url": "https://soundcloud.com/someartist/night-drive",
"artwork_url": "https://i1.sndcdn.com/artworks-...-large.jpg",
"duration_ms": 245000,
"license": "cc-by",
"liked_at": "2026-03-06T14:23:00+00:00",
"raw_json": "{...}"
}
]
}
| Field | Type | Description |
|---|---|---|
show_id |
integer | Internal database ID for this show |
episode_number |
integer | null | Episode number (e.g. 530), or null if not assigned |
week_start |
string | ISO 8601 UTC timestamp -- start of the show's like window |
week_end |
string | ISO 8601 UTC timestamp -- end of the show's like window |
tracks |
array | Ordered list of tracks (see Track Object below) |
GET /playlist/{position}
Returns a single track by its position in the current week's playlist. Positions are 1-indexed (matching IRC commands !1, !2, etc.).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
position |
integer | 1-based position in the playlist |
Response -- a single Track Object (see below).
Errors
| Status | Detail |
|---|---|
| 404 | "No track at position {n}" |
GET /shows
Lists all shows, ordered by week start date (newest first).
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 20 | Max number of shows to return |
offset |
integer | 0 | Number of shows to skip |
Response
[
{
"id": 10,
"episode_number": 530,
"week_start": "2026-03-05T02:00:00+00:00",
"week_end": "2026-03-12T02:00:00+00:00",
"created_at": "2026-03-05T03:00:00+00:00"
}
]
GET /shows/by-episode/{episode_number}
Look up a show by its episode number. This is the recommended endpoint for IRC bot integrations (e.g. !playlist 530).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
episode_number |
integer | The episode number (e.g. 530) |
Response
{
"show_id": 10,
"episode_number": 530,
"week_start": "2026-03-05T02:00:00+00:00",
"week_end": "2026-03-12T02:00:00+00:00",
"tracks": [...]
}
Errors
| Status | Detail |
|---|---|
| 404 | "No show with episode number {n}" |
GET /shows/{show_id}
Returns a specific show by internal database ID.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
show_id |
integer | The show's internal database ID |
Response
{
"show_id": 10,
"episode_number": 530,
"week_start": "2026-03-05T02:00:00+00:00",
"week_end": "2026-03-12T02:00:00+00:00",
"tracks": [...]
}
Errors
| Status | Detail |
|---|---|
| 404 | "Show not found" |
Dashboard Endpoints
These routes are only available when the dashboard is enabled (all three NTR_WEB_* env vars set).
GET /login
Serves the login page.
POST /login
Authenticates with username and password. Sets a session cookie on success, redirects to /dashboard.
Form Data
| Field | Type | Description |
|---|---|---|
username |
string | Dashboard username |
password |
string | Dashboard password |
GET /logout
Clears the session cookie and redirects to /login.
GET /dashboard
Serves the playlist dashboard page. Requires a valid session cookie (redirects to /login if absent).
Admin Endpoints
All admin endpoints require a bearer token via the Authorization header:
Authorization: Bearer <NTR_ADMIN_TOKEN>
Returns 401 with "Missing or invalid token" if the header is absent or the token doesn't match.
POST /admin/refresh
Triggers an immediate SoundCloud fetch for the current week's show.
Request Body (optional)
{
"full": false
}
| Field | Type | Default | Description |
|---|---|---|---|
full |
boolean | false |
Reserved for future use (full vs incremental refresh) |
Response
{
"status": "refreshed",
"track_count": 9
}
POST /admin/tracks
Manually add a track to the current week's show.
Request Body
{
"track_id": 12345,
"position": 3
}
| Field | Type | Required | Description |
|---|---|---|---|
track_id |
integer | yes | SoundCloud track ID (must already exist in the tracks table) |
position |
integer | no | Insert at this position (shifts others down). Omit to append at end. |
Response
{
"status": "added"
}
DELETE /admin/tracks/{track_id}
Remove a track from the current week's show. Remaining positions are re-compacted.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
track_id |
integer | SoundCloud track ID to remove |
Response
{
"status": "removed"
}
Errors
| Status | Detail |
|---|---|
| 404 | "Track not in current show" |
PUT /admin/tracks/{track_id}/position
Move a track to a new position within the current week's show.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
track_id |
integer | SoundCloud track ID to move |
Request Body
{
"position": 1
}
| Field | Type | Required | Description |
|---|---|---|---|
position |
integer | yes | New 1-based position for the track |
Response
{
"status": "moved"
}
Errors
| Status | Detail |
|---|---|
| 404 | "Track not in current show" |
POST /admin/announce
Broadcasts a track announcement to all connected WebSocket subscribers (IRC bots). The announcement is formatted as: Now Playing: Song #N: Title by Artist - URL.
Accepts either a bearer token OR a valid session cookie.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
show_id |
integer | yes | Show database ID |
position |
integer | yes | Track position in the show |
Response
{
"status": "announced",
"message": "Now Playing: Song #1: Night Drive by SomeArtist - https://soundcloud.com/..."
}
Errors
| Status | Detail |
|---|---|
| 401 | "Unauthorized" |
| 404 | "No track at position {n}" |
Track Object
Returned inside playlist and show detail responses.
| Field | Type | Description |
|---|---|---|
show_id |
integer | The show this track belongs to |
track_id |
integer | SoundCloud track ID |
position |
integer | 1-based position in the playlist |
title |
string | Track title |
artist |
string | Uploader's SoundCloud username |
permalink_url |
string | Full URL to the track on SoundCloud |
artwork_url |
string | null | URL to artwork image, or null |
duration_ms |
integer | Track duration in milliseconds |
license |
string | License string (e.g. "cc-by", "cc-by-sa") |
liked_at |
string | ISO 8601 timestamp of when the host liked the track |
raw_json |
string | Full SoundCloud API response for this track (JSON string) |
Week Boundaries
Shows follow a weekly cadence aligned to Wednesday 22:00 Eastern Time (EST or EDT depending on DST). The like window for a show runs from the previous Wednesday 22:00 ET to the current Wednesday 22:00 ET.
All timestamps in API responses are UTC. The boundary shifts by 1 hour across DST transitions:
| Period | Eastern | UTC boundary |
|---|---|---|
| EST (Nov -- Mar) | Wed 22:00 | Thu 03:00 |
| EDT (Mar -- Nov) | Wed 22:00 | Thu 02:00 |
WebSocket
WS /ws/announce
WebSocket endpoint for receiving announce broadcasts. Used by IRC bot plugins.
Authentication
After connecting, send a subscribe message:
{
"type": "subscribe",
"token": "<NTR_ADMIN_TOKEN>",
"role": "bot",
"client_id": "limnoria"
}
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | yes | Must be "subscribe" |
token |
string | yes | NTR_ADMIN_TOKEN value |
role |
string | no | Client role, defaults to "bot" |
client_id |
string | no | Identifier for this client (e.g. "sopel", "limnoria") |
Invalid token closes the connection with code 4001.
Messages from server
Announce broadcast:
{"type": "announce", "message": "Now Playing: Song #1: Title by Artist - URL"}
Status update (sent on subscriber connect/disconnect):
{
"type": "status",
"subscribers": 2,
"clients": [
{
"client_id": "limnoria",
"remote_addr": "1.2.3.4",
"connected_at": "2026-03-12T02:00:00+00:00"
}
]
}
| Field | Type | Description |
|---|---|---|
subscribers |
integer | Number of connected bot clients |
clients |
array | List of connected bot clients with their client_id, remote_addr, and connected_at timestamp |
Dashboard Configuration
The web dashboard is optional. Enable it by setting all three environment variables:
| Variable | Default | Description |
|---|---|---|
NTR_WEB_USER |
(required) | Dashboard login username |
NTR_WEB_PASSWORD |
(required) | Dashboard login password |
NTR_SECRET_KEY |
(required) | Secret key for session cookie signing |
If any of these are absent, dashboard routes are not mounted and the API works exactly as before.