Native libshout bindings for Node.js.
Libshout allows applications to easily communicate and broadcast to an Icecast streaming media server. It handles the socket connections, metadata communication, and data streaming for the calling application, and lets developers focus on feature sets instead of implementation details.
More detail: http://icecast.org
Tracks libshout 2.4.x. Upstream API reference: https://gitlab.xiph.org/xiph/icecast-libshout/-/blob/master/doc/libshout.xml.
- Node.js >= 22 — supports active LTS (24) and maintenance LTS (22).
- libshout installed at the system level.
Install libshout:
# macOS
brew install libshout
# Debian/Ubuntu
sudo apt-get install libshout3Then add this package to your application:
yarn add @fusion2004/nodeshout-koffi
# or: npm i @fusion2004/nodeshout-koffi@fusion2004/nodeshout-koffi is a pure ESM package. Import named exports:
import {
shoutInit,
shoutShutdown,
shoutVersion,
createShout,
ShoutErrorTypes,
ShoutFormats,
ShoutUsages,
ShoutAudioInfoKeys,
} from "@fusion2004/nodeshout-koffi";
// Initialize
shoutInit();
// Create a shout instance
const shout = createShout();
// Configure it
shout.setHost("localhost");
shout.setPort(8000);
shout.setUser("source");
shout.setPassword("password");
shout.setMount("mount");
shout.setContentFormat(ShoutFormats.MP3, ShoutUsages.AUDIO, null);
shout.setAudioInfo(ShoutAudioInfoKeys.BITRATE, "192");
shout.setAudioInfo(ShoutAudioInfoKeys.SAMPLERATE, "44100");
shout.setAudioInfo(ShoutAudioInfoKeys.CHANNELS, "2");Open the connection and check the return code. Every method documented as "Callers should check this" returns ShoutErrorTypes.SUCCESS (0) on success or a negative ShoutErrorTypes value on failure. On failure, shout.getError() returns a human-readable message.
const status = shout.open();
if (status !== ShoutErrorTypes.SUCCESS) {
console.error("shout_open failed:", shout.getError());
process.exit(1);
}After successful connection, send audio file chunks via shout.send (which you should also check for success):
const sendStatus = shout.send(buffer, bytesRead);
if (sendStatus !== ShoutErrorTypes.SUCCESS) {
console.error("shout_send failed:", shout.getError());
}For synchronization, two methods are provided. shout.sync() blocks the current thread. shout.delay() returns how many milliseconds to wait before sending the next audio chunk.
If you're streaming multiple files, beware that Icecast requires stable bitrate and sample rate for the whole stream. All your music files should have identical bitrate and sample rate.
Check the /demos folder. Run them via:
yarn demo:blocking
yarn demo:nonblocking(Both expect a local Icecast on localhost:8000 with credentials source:hackme which can be run via icecast -c demos/icecast.xml.)
All libshout enums are exposed as named as const objects:
| Export | Purpose |
|---|---|
ShoutErrorTypes |
SHOUTERR_* codes returned by every checked method. |
ShoutFormats |
SHOUT_FORMAT_* — pass to setContentFormat(). |
ShoutUsages |
SHOUT_USAGE_* — bitwise OR for setContentFormat(). |
ShoutProtocols |
SHOUT_PROTOCOL_* — pass to setProtocol(). |
ShoutTlsModes |
SHOUT_TLS_* — pass to setTls(). |
ShoutBlocking |
SHOUT_BLOCKING_* — pass to setNonblocking(). |
ShoutAudioInfoKeys |
SHOUT_AI_* — keys for setAudioInfo() / getAudioInfo(). |
ShoutMetaKeys |
SHOUT_META_* — keys for setMeta() / getMeta(). |
These wrappers still work but are marked @deprecated because the underlying libshout functions are obsolete. New code should use the listed replacements:
| Deprecated | Replacement |
|---|---|
setName(s) / getName() |
setMeta(ShoutMetaKeys.NAME, s) / getMeta(ShoutMetaKeys.NAME) |
setUrl(s) / getUrl() |
setMeta(ShoutMetaKeys.URL, s) / getMeta(ShoutMetaKeys.URL) |
setGenre(s) / getGenre() |
setMeta(ShoutMetaKeys.GENRE, s) / getMeta(ShoutMetaKeys.GENRE) |
setDescription(s) / getDescription() |
setMeta(ShoutMetaKeys.DESCRIPTION, s) / getMeta(ShoutMetaKeys.DESCRIPTION) |
setFormat(fmt) / getFormat() |
setContentFormat(fmt, usage, codecs) / getContentFormat() |
setMetadata(m) |
setMetadataUtf8(m) |
setDumpfile(p) / getDumpfile() |
(no replacement — only useful with the deprecated ShoutProtocols.XAUDIOCAST) |
The ShoutFormats.WEBMAUDIO and ShoutProtocols.XAUDIOCAST constants are also deprecated.
v3 is a hard break. Every consumer needs changes:
| v2 | v3 |
|---|---|
const nodeshout = require('nodeshout') |
import * as nodeshout from '@fusion2004/nodeshout-koffi' |
nodeshout.init() |
nodeshout.shoutInit() |
nodeshout.shutdown() |
nodeshout.shoutShutdown() |
nodeshout.getVersion() |
nodeshout.shoutVersion() |
nodeshout.create() |
nodeshout.createShout() |
nodeshout.createMetadata() |
nodeshout.createShoutMetadata() |
nodeshout.ErrorTypes |
nodeshout.ShoutErrorTypes |
nodeshout.Formats |
nodeshout.ShoutFormats |
nodeshout.Protocols |
nodeshout.ShoutProtocols |
nodeshout.Usages |
nodeshout.ShoutUsages |
nodeshout.TlsModes |
nodeshout.ShoutTlsModes |
nodeshout.Blocking |
nodeshout.ShoutBlocking |
nodeshout.AudioInfoKeys |
nodeshout.ShoutAudioInfoKeys |
nodeshout.MetaKeys |
nodeshout.ShoutMetaKeys |
class ShoutT |
class Shout |
class MetadataT |
class ShoutMetadata |
ShoutT and MetadataT are kept as deprecated type aliases for one release; the runtime classes have new names.
Tooling & dependencies are managed via mise and Yarn Berry:
mise install— installs node & yarnyarn install— installs npm dependenciesmise run lint— runs linters through yarn in parallelyarn test— runs vitest unit tests (no server required).yarn build— emitsdist/*.jsanddist/*.d.ts.yarn demos— runs both demos against a local Icecast (starticecast -c demos/icecast.xmlfirst).
Publishing is driven by tag pushes: the maintainer runs mise run release, and the publish workflow takes it from there.
- Merge a PR that bumps the version in
package.json(and any other release-prep changes — changelog, etc.) into main. - Pull the latest main locally and run
mise run release. This reads the version frompackage.json, creates an annotated tag prefixed withv(e.g.v3.0.0), and pushes it toorigin. Run this from the exact commit you want to release — typically the merge commit of the version-bump PR. - Pushing the tag triggers the publish workflow, which publishes the package to npm with an OIDC-attested provenance statement via npm trusted publishing. If the version is already on the registry, npm rejects it and the workflow fails.