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.
- Host: GitHub
- URL: https://github.com/borewit/music-metadata-icy
- Owner: Borewit
- License: mit
- Created: 2025-07-18T12:53:32.000Z (11 months ago)
- Default Branch: master
- Last Pushed: 2025-08-19T09:26:04.000Z (10 months ago)
- Last Synced: 2025-09-21T21:00:49.758Z (9 months ago)
- Language: TypeScript
- Size: 34.2 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
[](https://github.com/Borewit/music-metadata-icy/actions/workflows/nodejs-ci.yml)
[](https://npmjs.org/package/@music-metadata/icy)
[](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.