MP · Music Play

An AI DJ that takes requests and stories.

A request arrives with an optional story and enters a published five-song lineup; the DJ summarizes the story over the intro. Everyone connected hears the same song at the same point, and when nothing is queued a curated catalog fills the slot.

No audio is served. Nothing is streamed or proxied. Each client opens the official YouTube embed of the current song and seeks to the position the server reports. The server holds a playback time, a five-song lineup and a DJ script.

Design

Three design decisions

The service runs on a static catalog file and a hosting platform with no scheduler and no persistent process. The architecture follows from those two constraints.

Synchronized by timestampOne state row holds the current song, its start time and its length. A client computes its own position as start time plus elapsed time. Only timestamps are compared, and no audio leaves the server.
Advance on readThere is no cron. The state advances inside the poll that observes the song has ended, guarded by a conditional update so that concurrent polls advance it exactly once. Ninety seconds ahead, one poll takes a lock and prepares the next song's script and voice.
Catalog as the fallback scheduleWhen the request queue is empty the server draws from the catalog: songs never aired first, the most recently aired 60% excluded, the previous artist avoided, embed-blocked uploads excluded. Playback continues while the queue is empty.
Client

Join, synchronize, advance

The client is a single HTML file with no build step. It polls the server every ten seconds and acts on the response in four ways.

JoiningOne tap starts the session, because browsers refuse audio without a gesture. The same tap unlocks the voice player, so mobile browsers continue to play the DJ audio on later songs. The client then loads the current song at the position the server reports.
Staying in syncEach poll compares local position with the server's. Drift beyond three seconds is corrected with a seek; smaller drift is left alone, because ads and buffering cost a few seconds and repeated seeking interrupts playback.
Song boundariesThe next five songs are fixed in advance and shown in order. Slot one is locked; requests enter from slot two. A client that sees a new song loads it and, within the first 25 seconds, plays the DJ line over the intro with the music ducked.
Embed failuresAn upload with embedding disabled fails for every listener at once. Clients report it; after three distinct reports the song leaves the schedule. The catalog file itself is never modified by the server.
Scheduling

A weighted queue over first come, first served

The base score is minutes waited, which on its own is first come, first served. Conditions add to it, so a later request can be scheduled earlier; waiting time keeps accruing, so every request is eventually played. The weights are read from a table at scheduling time, and every decision records its winning score. An operator pin overrides the order for one song.

ConditionWeightRationale
Minutes waited+1 / minThe base term. Alone, it is first come, first served
Has a story+40Stories go first. Fixed by policy
Requester is listening now+30A story goes out while its sender can hear it
Same song requested by several+10 each, cap 30Played once; every requester is named
First request from this sender+15Newcomers are scheduled ahead of repeat requesters
Song not aired for a week+10Variety
Same sender aired in the last two songs−20Prevents one sender from dominating the lineup
Same song aired in the last twenty−50No near repeats

Current values. Because each decision logs the score that won, the weights are re-tuned from the log.

Requests

Three input forms, one resolution path

A request is a song plus an optional story and an optional name to be called by. Whatever form the song arrives in, it is resolved to an embeddable video before it can enter the lineup, and the requester never picks the video.

Catalog songChosen from the search box. The video is already known, so the request goes straight into the queue.
YouTube linkThe video is verified through oEmbed; an upload with embedding disabled is refused at submission. Title and length come from oEmbed and, where available, MusicBrainz.
Title and artist onlyCandidates come from the YouTube Data API, restricted to official artist channels, label and VEVO channels, and auto-generated Topic channels; fan uploads are excluded. MusicBrainz supplies the canonical length and candidates outside ±10 s are dropped. A model picks among the survivors; low confidence goes to the operator, not the lineup.
Two-stage story screeningA regular-expression pass rejects contact details and account numbers before anything else runs. What passes is classified by a model as safe, review or reject. Review holds the story until an operator approves it; the song plays regardless. Submissions are anonymous and rate-limited per address: twenty a day, five with a story, three waiting at once.
Requested songs stay separateSongs outside the catalog live in their own table with the channel, its type and the reason for the pick. They play only when requested and never enter the shuffle. Promotion into the catalog is a manual editorial step.
AI DJ

One script, two voices, two languages

The DJ speaks over the first seconds of a song while the music is ducked. The script is generated in Korean and English in one call, and every listener hears the language of their interface. The voice is synthesized on the server and cached, so every listener hears the same take. Listeners who prefer music alone switch the DJ off on their device.

What gets a model callA request with an approved story: the model retells it in two or three sentences, never verbatim, and names the song; the catalog note is not read. A request without a story: a fixed template naming the sender and the song. A catalog song: the note as written, with a model-written script every fifth song. The greeting with the time and the DJ's name appears once per three-hour block.
Bounded inputThe model sees only the catalog fields for that song and the story text. Story text is passed as data. Instruction-like content in a story raises a flag and is not executed. Output containing contact-detail or link patterns is discarded and the template goes out instead.
VoicesTwo voices, Sulafat and Orus, from Google Cloud Text-to-Speech (Chirp 3 HD). The operator fixes one or lets them alternate every three hours; listeners do not choose. Audio is cached by text and voice, so a catalog note is synthesized once. When synthesis is unavailable the browser's own speech engine reads the same script.
What happens to a storyA story is kept alongside the request and the line the DJ actually spoke, so an operator can see exactly what went out. Removal is an operator action; nothing is deleted on a timer.
Numbers

Read from the deployed catalog

The figures below are fetched from the live catalog file when this page loads. They are the pool the server schedules from when the queue is empty.

862
Playable songs across three lists
862
With a known length, so they can be scheduled
100
With a note and a source line for the DJ
±3s
Drift tolerated before a client seeks

Lists: My Play List 241, Apple Music 531, My K-Pop 90. Every entry carries performer, lyricist, composer, a note in Korean and English, and its source. Lengths come from the curation sheet, not from the player.

Fallback

Playback continues when a dependency fails

Scripts, voices and story screening all depend on external services. If any of them fails or does not answer, the song still plays on schedule, because every step has a fallback.

Playback stands alonePlaying a song needs only the schedule and the playback time. No language model, speech synthesis or screening sits on the playback path. If all of them stop, the music continues.
Scripts and voices fall back in stagesWhen script generation fails, the catalog note or a fixed template goes out instead. When server synthesis is unavailable, the browser's own speech engine reads the same script; failing that, the line stays on screen as a caption.
Screening never auto-approvesIf story classification fails or is unavailable, the story goes to operator review instead of being passed as safe. The song still plays; only the story waits.
Limits

What this does not claim

Shared listening over embedded video has hard edges. Below is where they are.

Not a music serviceNo rights are held and no audio is served. Each listener plays a YouTube upload through the official embed player; availability, ads and quality are YouTube's. A song whose upload disappears leaves the schedule with it.
Sync is approximateListeners are within a few seconds of each other, not sample-aligned. An ad inserts time for one listener and not another; the next poll pulls them back. The target is the same song within a few seconds.
The DJ knows only the catalogScripts are generated from the song's own fields and the story. They contain no facts beyond that input, and when generation fails a template or the note goes out instead. The DJ does not answer questions or add facts.
A curated catalog, anonymous listenersThe default schedule is an editorial selection, not a chart. Requests are anonymous; the name to be called by is chosen by the sender and is not an identity. The listener count is a hashed-address heartbeat, not a login.
Off-air mode is a personal playerWhen no session is on, the same catalog plays individually in the browser. The Android app reads the same catalog from local files without a network; it is sideloaded, not distributed, and not part of the session.
Open Music Play When a session is on, one tap joins at the same point as everyone else.