Directory listing for: icecast
DIR Parent directory (..)
TEXT README.txt (5.3KB, 2026-05-12):
Icecast relay -- any HTTP/Icecast stream via gopher
=============================================================
This applet relays HTTP/Icecast audio streams over a long-lived
gopher TCP connection. Every station -- the default one or any
URL you paste in -- has a per-station menu page with metadata
(name, genre, bitrate, current StreamTitle when available), a
Listen link (audio passthrough), a Watch link (144p MPEG-TS
showcqt visualization, ~38 kbps total), and a copy-pasteable
curl command after each. A "Radio menu URI:" info-line at the
foot of every per-station menu is the page's stable permalink.
NOTE: the bare URL /applets/icecast/icecast.lhs returns a
gophermap landing menu, not audio. To play the default station,
pipe the bytes at /applets/icecast/icecast.lhs/default instead:
curl gopher://gopher.someodd.zip/9/applets/icecast/icecast.lhs/default | mpv -
------------------------------------------------------------
Endpoints
------------------------------------------------------------
/applets/icecast/icecast.lhs
landing menu (gophermap)
/applets/icecast/icecast.lhs/menu
"paste a URL" prompt; type-7 query lands on the per-
station menu for the submitted URL.
/applets/icecast/icecast.lhs<TAB><URL>
type-7 query against the landing: same as /menu + URL,
produces the per-station menu.
/applets/icecast/icecast.lhs/default
default station, audio passthrough.
/applets/icecast/icecast.lhs/default/audio
default station, audio passthrough (alias of /default).
/applets/icecast/icecast.lhs/default/with_visualization
default station, 144p MPEG-TS showcqt visualization.
/applets/icecast/icecast.lhs/default/menu
per-station menu for the default station --- metadata,
Listen + Watch links with curl recipes, permalink.
/applets/icecast/icecast.lhs/url/<b64>
/applets/icecast/icecast.lhs/url/<b64>/audio
/applets/icecast/icecast.lhs/url/<b64>/with_visualization
/applets/icecast/icecast.lhs/url/<b64>/menu
arbitrary-URL station: <b64> is a base64url-encoded
upstream URL with padding stripped. Hit any of these
directly to bookmark a station; the encoded URLs are
produced automatically when you submit a URL via the
type-7 box, and printed inside the per-station menu's
"Radio menu URI:" line.
------------------------------------------------------------
From the command line
------------------------------------------------------------
Default station (audio):
curl gopher://gopher.someodd.zip/9/applets/icecast/icecast.lhs/default | mpv -
Default station (visualization):
curl gopher://gopher.someodd.zip/9/applets/icecast/icecast.lhs/default/with_visualization | mpv -
Default station's menu page (gophermap with live metadata,
curl recipes, permalink):
curl gopher://gopher.someodd.zip/1/applets/icecast/icecast.lhs/default/menu
Open a per-station menu for an arbitrary URL (selector + TAB + URL):
printf '/applets/icecast/icecast.lhs/menu\thttp://ice1.somafm.com/groovesalad-128-mp3\r\n' \
| nc gopher.someodd.zip 70
The returned menu's Listen / Watch rows include base64url-encoded
selectors you can curl directly:
curl gopher://gopher.someodd.zip/9/applets/icecast/icecast.lhs/url/aHR0cDovL2ljZTEuc29tYWZtLmNvbS9ncm9vdmVzYWxhZC0xMjgtbXAz/audio | mpv -
(equivalent to the Listen row in that menu; bookmark the
"Radio menu URI" to come back to the menu later.)
------------------------------------------------------------
How this works
------------------------------------------------------------
Audio passthrough opens a TLS/HTTP connection to the upstream
and copies the response body straight to stdout. Constant
memory; nothing buffered through Haskell.
Visualization spawns ffmpeg with the upstream URL as the input
argument and showcqt as the filter; ffmpeg's stdout inherits
the gopher socket directly.
Menu pages do a brief one-shot fetch with `Icy-MetaData: 1` and
a 4-second timeout to populate the metadata block. If the
upstream is slow or offline the menu still renders --- just
without the optional fields.
Permalinks: per-station menus carry a "Radio menu URI:" line at
the foot. For the default station it points at
/applets/icecast/icecast.lhs/default/menu. For URL stations it
points at /applets/icecast/icecast.lhs/url/<b64>/menu, where
<b64> is base64url(upstream) with padding stripped --- so the
same URL always produces the same selector.
When you disconnect mid-stream (Ctrl-C the nc), stdout becomes
a broken pipe; the exception unwinds the script (or SIGPIPE
kills ffmpeg); the upstream connection closes; the daemon reaps
the process. No leaks.
------------------------------------------------------------
Limits
------------------------------------------------------------
The server caps concurrent connections at 256, so this is not
a CDN. Disconnect when you're done.
Invalid URLs in the type-7 boxes (anything that isn't http://
or https://) come back as a type-3 error row plus a "back to
landing" link. URLs are length-capped at 2 KiB; that's the cap
for the upstream URL before base64url encoding.
If the upstream is offline you'll get an immediate EOF or a
TLS handshake error on the streaming endpoints --- there's no
graceful "the radio is offline" message at the audio-byte
level. Menu endpoints emit a type-3 row when something throws,
and skip metadata fields that couldn't be fetched.
DIR icecast.lhs 29.1KB 2026-05-12
TEXT stations.txt 259B 2026-05-13