An open API service indexing awesome lists of open source software.

https://github.com/borewit/music-metadata-icy

Decode ICY metadata (used by Icecast and Shoutcast) from streaming audio responses.
https://github.com/borewit/music-metadata-icy

Last synced: 9 months ago
JSON representation

Decode ICY metadata (used by Icecast and Shoutcast) from streaming audio responses.

Awesome Lists containing this project

README

          

[![Node.js CI](https://github.com/Borewit/music-metadata-icy/actions/workflows/nodejs-ci.yml/badge.svg)](https://github.com/Borewit/music-metadata-icy/actions/workflows/nodejs-ci.yml)
[![NPM version](https://img.shields.io/npm/v/@music-metadata%2Ficy.svg)](https://npmjs.org/package/@music-metadata/icy)
[![npm downloads](http://img.shields.io/npm/dm/@music-metadata%2Ficy.svg)](https://npmcharts.com/compare/@music-metadata%2Ficy?start=365)

# @music-metadata/icy

Decode [ICY metadata](https://en.wikipedia.org/wiki/SHOUTcast#Metadata) (used by Icecast and Shoutcast) from audio streams, commonly used in internet radio.

This module extracts ICY metadata (e.g., `StreamTitle`) from HTTP responses while passing through clean audio chunks for playback or further processing.

> โœ… **Lightweight** โ€ข **Fast** โ€ข **Web & Node-compatible** โ€ข Built on [`strtok3`](https://github.com/Borewit/strtok3)

---

## ๐Ÿš€ Installation

```bash
npm install @music-metadata/icy
```

Or with Yarn:

```bash
yarn add @music-metadata/icy
```

---

## Demo

* [ICY Radio Stream Player](https://github.com/Borewit/icy-radio-stream-player)

---

## ๐Ÿ“ฆ Usage

```ts
import { parseIcyResponse } from '@music-metadata/icy';

const response = await fetch('https://example.com/radio-stream', {
headers: {
'Icy-MetaData': '1'
}
});

const audioStream = parseIcyResponse(response, ({ metadata }) => {
const title = metadata.StreamTitle;
if (title) {
console.log('Now Playing:', title);
}
});

// You can now pipe `audioStream` to a decoder or audio player.
```

---

## ๐Ÿง  API

### `parseIcyResponse(response, handler): ReadableStream`

Process a fetch-compatible HTTP response and extract ICY metadata on the fly.

#### Parameters

* `response: Response`
A standard Fetch API [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object with streaming body.
* `handler: (update: MetadataUpdate) => void`
A callback triggered when new ICY metadata is available.

#### Returns

* `ReadableStream`
A web-compatible readable stream containing **only the audio payload**, excluding metadata.

#### Example

```ts
{
metadata: {
StreamTitle: 'Cool Song',
StreamUrl: 'https://example.com',
...
},
stats: {
totalBytesRead: 20480,
audioBytesRead: 19200,
icyBytesRead: 1280
}
}
```

---

### `decodeIcyStreamChunks(stream, metaInt, handler): ReadableStream`

Lower-level function to extract ICY metadata from a `ReadableStream` where the metadata interval is already known.

#### Parameters

* `stream: ReadableStream` or Node's `ReadableStream`
* `metaInt: number` โ€“ The icy metadata interval in bytes.
* `handler: (update: MetadataUpdate) => void` โ€“ Metadata callback, same as above.

#### Returns

* `ReadableStream` โ€“ Cleaned stream without metadata blocks.

Use this method if you already know the `icy-metaint` (e.g., from headers or external configuration).

---

## ๐Ÿงบ ICY Metadata Parsing

ICY metadata is parsed from raw string format:

```ts
"StreamTitle='song';StreamUrl='url';"
```

Parsed result:

```ts
{
StreamTitle: 'song',
StreamUrl: 'url'
}
```

Internally handled by:

```ts
function parseRawIcyMetadata(raw: string): Map
```

---

## ๐Ÿ“œ Types

### `type IcyMetadata`

```ts
type IcyMetadata = {
StreamTitle?: string;
StreamUrl?: string;
icyName?: string;
icyGenre?: string;
icyUrl?: string;
bitrate?: string;
contentType?: string;
[key: string]: string | undefined;
}
```

### `type MetadataUpdate`

```ts
type MetadataUpdate = {
metadata: IcyMetadata;
stats: {
totalBytesRead: number;
audioBytesRead: number;
icyBytesRead: number;
};
};
```

---

## ๐Ÿงฑ Internals

If `Icy-Metaint` is not provided by the server, the module attempts to **auto-detect** the metadata interval by scanning the stream for known ICY patterns such as `"StreamTitle="`.

---

## ๐Ÿงญ How It Works

The following diagram shows how `@music-metadata/icy` fits into a web-based ICY audio streaming pipeline, parsing interleaved metadata while passing clean audio through to playback:

```mermaid
graph TD
%% Node Styles
style A fill:#bbf,stroke:#333,stroke-width:2px
style B fill:#ddf,stroke:#333,stroke-width:2px
style C fill:#afa,stroke:#333,stroke-width:2px,stroke-dasharray: 5 5
style D fill:#ffe4b3,stroke:#333,stroke-width:2px
style E fill:#fcc,stroke:#333,stroke-width:2px,stroke-dasharray: 3 3
style F fill:#fcf,stroke:#333,stroke-width:2px
style G fill:#cff,stroke:#333,stroke-width:2px,stroke-dasharray: 2 4

%% Nodes
A["๐ŸŽง ICY Web Stream
(Icecast via Fetch)"]
B["๐Ÿ”€ Fetch with
ICY-MetaData Header"]
C["๐Ÿงฉ @music-metadata/icy
(ICY Parser)"]
D["๐Ÿ” Decoded Audio Stream"]
E["๐ŸŽต HTML5 Audio
<audio> Element"]
F["๐Ÿ›ฐ๏ธ ICY Metadata Events"]
G["๐Ÿ–ฅ๏ธ Metadata Display
in React UI"]

%% Flow
A --> B
B -->|ICY Interleaved Audio| C
C -->|Audio Stream| D
D --> E
C -->|Metadata Events| F
F -->|Track Info etc.| G
```
---

## ๐Ÿ“„ License

MIT โ€” see [LICENSE.txt](LICENSE.txt) for full text.