If the `Icy-MetaData=1` HTTP header is not answered with an HTTP response `icyMetaInt`, the server does not support ICY Metadata.
I checked the URL `http://streaming.radionomy.com/70-s-80-sMetal`, which now redirects to `https://streaming.brol.tech/rtfmlounge`, and I can confirm it does indeed not support support ICY metadata.
Evidence:
<!-- begin snippet: js hide: false console: true babel: false babelPresetReact: false babelPresetTS: false -->
<!-- language: lang-js -->
const STREAM_URL = 'https://streaming.brol.tech/rtfmlounge';
const trackDisplay = document.getElementById('track');
const audioElement = document.getElementById('player');
const mediaSource = new MediaSource();
audioElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
try {
// Dynamically import the ESM-only module
const { parseIcyResponse } = await import('https://cdn.jsdelivr.net/npm/@music-metadata/icy@0.1.0/+esm');
const response = await fetch(STREAM_URL, {
headers: { 'Icy-MetaData': '1' }
});
const audioStream = parseIcyResponse(response, metadata => {
for (const [key, value] of metadata.entries()) {
console.log(`Rx ICY Metadata: ${key}: ${value}`);
}
const title = metadata.get('StreamTitle');
if (title) {
trackDisplay.textContent = title;
}
});
const reader = audioStream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(value);
} else {
await new Promise(resolve => {
sourceBuffer.addEventListener('updateend', resolve, { once: true });
});
sourceBuffer.appendBuffer(value);
}
}
mediaSource.endOfStream();
} catch (err) {
console.error('Error streaming audio:', err.message);
trackDisplay.textContent = 'Failed to load stream';
}
});
<!-- language: lang-html -->
<html lang="en">
<head>
<title>ICY Stream Player</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 2em; }
audio { width: 100%; max-width: 500px; margin-top: 1em; }
#track { font-weight: bold; margin-top: 1em; color: red; font-style: italic}
</style>
</head>
<body>
<h2>ICY Stream Player</h2>
<div>Now playing: <span id="track">...</span></div>
<audio id="player" controls autoplay></audio>
</body>
</html>
<!-- end snippet -->
You can do client side, on the condition that you can [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) the Icecast stream.
To make client-side playback and ICY metadata extraction work via [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) in the browser, **[CORS (Cross-Origin Resource Sharing)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) requirements must be properly met by the radio stream server**.
I wrote the [@music-metadata/icy module](https://github.com/Borewit/music-metadata-icy) for this occasion. Credits to [Brad](https://stackoverflow.com/users/362536/brad) who encourage me to contribute to StackOverflow, while others gave me much reason to run away.
<!-- begin snippet: js hide: false console: true babel: false babelPresetReact: false babelPresetTS: false -->
<!-- language: lang-js -->
const STREAM_URL = 'https://audio-edge-kef8b.ams.s.radiomast.io/ref-128k-mp3-stereo';
const trackDisplay = document.getElementById('track');
const audioElement = document.getElementById('player');
const mediaSource = new MediaSource();
audioElement.src = URL.createObjectURL(mediaSource);
mediaSource.addEventListener('sourceopen', async () => {
const sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');
try {
// Dynamically import the ESM-only module
const { parseIcyResponse } = await import('https://cdn.jsdelivr.net/npm/@music-metadata/icy@0.1.0/+esm');
const response = await fetch(STREAM_URL, {
headers: { 'Icy-MetaData': '1' }
});
const audioStream = parseIcyResponse(response, metadata => {
for (const [key, value] of metadata.entries()) {
console.log(`Rx ICY Metadata: ${key}: ${value}`);
}
const title = metadata.get('StreamTitle');
if (title) {
trackDisplay.textContent = title;
}
});
const reader = audioStream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (value && !sourceBuffer.updating) {
sourceBuffer.appendBuffer(value);
} else {
await new Promise(resolve => {
sourceBuffer.addEventListener('updateend', resolve, { once: true });
});
sourceBuffer.appendBuffer(value);
}
}
mediaSource.endOfStream();
} catch (err) {
console.error('Error streaming audio:', err.message);
trackDisplay.textContent = 'Failed to load stream';
}
});
<!-- language: lang-html -->
<html lang="en">
<head>
<title>ICY Stream Player</title>
<style>
body { font-family: sans-serif; text-align: center; margin-top: 2em; }
audio { width: 100%; max-width: 500px; margin-top: 1em; }
#track { font-weight: bold; margin-top: 1em; color: red; font-style: italic}
</style>
</head>
<body>
<h2>ICY Stream Player</h2>
<div>Now playing: <span id="track">...</span></div>
<audio id="player" controls autoplay></audio>
</body>
</html>
<!-- end snippet -->