Skip to main content

HTTP API Endpoints

Active Express routes in server.js and its mounted modules. The service is served at api.mediahubnetwork.net (dev: port 5000).

Pages

MethodPathServes
GET/Home (index.html)
GET/chatChat page (public/chat.html)
GET/lokStudio page (public/lok.html)
GET/social.htmlStudio UI for the social platform

Streaming

MethodPathPurpose
GET/streamStream the main channel (proxy passthrough)
GET/stream/:channelIdStream any channel by id

Channels

MethodPathPurpose
GET/api/channelsList available channels
POST/api/channels/createCreate a channel
POST/api/channels/:id/queuePush a track onto a channel's queue

Content & library

MethodPathPurpose
GET/api/libraryInventory of the media library (from the registry/rundown)
GET/api/content/statusContent pipeline status (counts per category, last run)
POST/api/content/refreshTrigger a content refresh run
POST/api/content/restoreEmergency restore from the archive (returns restored counts + resyncs the scheduler)

Imaging — /api/imaging/*

MethodPathPurpose
POST/api/imaging/generateRun all four imaging generators (sweepers/IDs/voice drops/stingers)
POST/api/imaging/sweepersRegenerate sweepers ({songDrops,ids,voiceDrops,count,archiveOld})
POST/api/imaging/stingersRegenerate the stinger library ({count,archiveOld})

Each imaging endpoint resyncs the decks and pushes the rotation. See Imaging engine.

Bulletin — /api/bulletin/*

MethodPathPurpose
POST/api/bulletin/generateGenerate a spoken NEWS + time + weather bulletin, resync the rotation, push to remote
GET/api/bulletin/statusLatest bulletin file, current news.txt headlines, live Cairo time-check sentence

Hourly auto-gen: boot +30s then every BULLETIN_HOURS (default 1h, 0 disables). See Bulletin engine.

Reactions — /api/reactions

MethodPathPurpose
GET/api/reactionsStation-wide emoji counts {"🔥":2,"❤️":1,…}
POST/api/reactions{emoji, delta} — +1 per click, persisted to models/reactions.json

The chat-server proxy (:8094) forwards both so the public web page and the Flutter app can read/write reactions from the public origin.

POST /api/content/refresh accepts an options body, e.g. {"songs":20,"ads":16,"restore":false}. Defaults: restore is false (emergency-only — the old default of true resurrected purged OLD tracks every cycle), songs 20, ads 16. The automatic 6-hour refresh (CONTENT_REFRESH_HOURS, 0 to disable) is a chained setTimeout so a long run never overlaps the next tick, and liveProtectedPaths() + resyncSchedulerAfterRefresh() keep the on-air rotation safe across the swap.

Sync — /api/sync/* (HTTP-pull upload)

The production node pulls media over plain HTTP; these are the token-gated endpoints it curls (see Media Sync):

MethodPathGuardsServes
GET/api/sync/media?rel=<rel>&token=<t>SYNC_TOKEN (default loklok-sync-pull), rel path-guarded under MEDIA_ROOTAny media file
GET/api/sync/m3u?token=<t>SYNC_TOKENPLAYLIST_ROOT/main.m3u

Station control — /api/station/* (agent-facing)

Auth: if STATION_KEY is set, require x-station-key (or Authorization) header.

MethodPathPurpose
GET/api/station/stateFull snapshot: rotation, queue, decks, config, live
GET/api/station/configCurrent scheduler config
PUT/api/station/configDeep-merge a config patch
PUT/api/station/genresUpdate GENRE_SPLIT
PUT/api/station/weightsUpdate MASHUP_CHANCE / STOCHASTIC_SFX_CHANCE
PUT/api/station/rotationUpdate rotation buffer/fresh-window/cooldowns
PUT/api/station/daypartsSet daypart windows (weights / hard genre)
POST/api/station/dayparts/clearClear all dayparts
PUT/api/station/moodApply a mood preset (`chill
GET/api/station/singersSinger catalogs per category
POST/api/station/singersAdd/remove/replace singers for a category
PUT/api/station/queriesReplace the trend queries for a category
POST/api/station/content/fetchFetch content for a category ({category,target,kind})
POST/api/station/content/scrubArchive junk from a category deck
POST/api/station/content/refreshRun the full content refresh
POST/api/station/rotation/rebuildRebuild the on-air rotation
POST/api/station/skipSkip the rotation head (rebuild)
POST/api/station/playForce a track from a category to the head
POST/api/station/config/savePersist current config
POST/api/station/config/resetDelete station-config.json
GET/api/station/logTail content or server log (?file=&lines=)
GET/api/station/liveLive manager status
POST/api/station/live/registerRegister a live stream {url,title,creator,listeners,ingest}
POST/api/station/live/removeRemove a stream by id
POST/api/station/live/voteVote on a stream {id,delta}
POST/api/station/live/startGo live: {id} optional, else top-ranked
POST/api/station/live/stopBack to the normal mix

See Station control and Live manager.

Social platform — /api/social/*

MethodPathPurpose
GET/api/social/postsList posts (?status=&type=&sort=)
GET/api/social/posts/:idSingle post
POST/api/social/postsSubmit {type,title,artist,description,tags,sourceUrl|base64,createdBy}
POST/api/social/upload/:typeRaw audio body upload (?title=&artist=)
POST/api/social/posts/:id/vote{delta:±1}
POST/api/social/posts/:id/react{kind}
POST/api/social/posts/:id/approve · reject · processAdmin (gate via SOCIAL_ADMIN_KEY)
POST/api/social/syncAttach overlay + force rotation push
GET/api/social/statusPlatform status
GET/api/social/nextNext ranked post (?type=&category=)

Telemetry

MethodPathPurpose
GET/api/statusLive system status (now playing, listeners, bandwidth)
GET/api/scheduleThe full 120-track on-air rotation resolved through the registry, with onAir/aired flags
GET/api/upnextThe upcoming queue (with titles/artists)
POST/api/upnext/votesVote on the up-next queue
GET/api/upnext/votesCurrent up-next vote counts
GET/next-tracks.jsonThe upcoming queue as JSON
GET/network-test-payloadSynthetic payload for network testing
GET/pingLiveness probe (Date.now() as text)

The listener count shown in /api/status and stats_update comes from MediaMTX HLS (polled every 5s from the read-only proxy at 10.10.8.230:19353/v3/paths/list), with Icecast as fallback (source = mediamtx | icecast).

Push notifications

MethodPathPurpose
POST/api/subscribeRegister a web-push subscription

The subscription payload is stored and used to send push notifications (see Notifications).

Metadata (legacy / public)

MethodPathPurpose
POST/metadataPublish a now-playing metadata update

/metadata is also the authoritative "a track finished on-air" signal. Liquidsoap posts metadata after each track; the handler calls scheduler.markOnAirTrack() to advance the rotation cursor and cooldown the exact track that just played, so the next rotation never schedules it again. See Anti-repeat & M3U rotation.

Response conventions

  • JSON for all /api/* routes.
  • HTML text/html for page routes.
  • /ping returns a plain-text timestamp for load-balancer health checks.

Typical client flow

  1. GET /api/status for a snapshot.
  2. socket.emit('join_channel', 'main') for live status pushes.
  3. GET /api/library + /next-tracks.json for queue visualization on the monitor page.
  4. GET /api/station/state (or via MCP) to inspect/control the rotation.