Skip to content
This repository was archived by the owner on Jun 18, 2025. It is now read-only.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ You can provide additional parameters to constructor:
- `userAgent` - by default `icecast-parser`.
- `keepListen` - by default `false`. If you set to `true`, then response from radio station will not be destroyed and you can pipe it to another streams. E.g. piping it to the `speaker` module.
- `autoUpdate` - by default `true`. If you set to `false`, then parser will not be listening for recent updates and immediately close the stream. So that, you will get a metadata only once.
- `notifyOnChangeOnly` - by default `false`. If you set both `autoUpdate` and `notifyOnChangeOnly` to `true`, it will keep listening the stream and notifying you about metadata, but it will not notify if metadata did not change from the previous time.
- `notifyOnChangeOnly` - by default `[]`. If you set both `autoUpdate` to `true` and `notifyOnChangeOnly` to include the a list of metadata keys to listen for (`['StreamTitle', 'StreamUrl']`), it will keep listening the stream and notifying you about metadata, but it will not notify if the selected metadata did not change from the previous time.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, let it be false as before for backward compatibility.

- `errorInterval` - by default 10 minutes. If an error occurred when requesting, the next try will be executed after this interval. Works only if `autoUpdate` is enabled.
- `emptyInterval` - by default 5 minutes. If the request was fullfiled but the metadata field was empty, the next try will be executed after this interval. Works only if `autoUpdate` is enabled.
- `metadataInterval` - by default 5 seconds. If the request was fullfiled and the metadata was present, the next update will be scheduled after this interval. Works only if `autoUpdate` is enabled.
Expand All @@ -61,7 +61,7 @@ const radioStation = new Parser({
errorInterval: 10 * 60,
keepListen: false,
metadataInterval: 5,
notifyOnChangeOnly: false,
notifyOnChangeOnly: [],
url: 'https://live.hunter.fm/80s_high',
userAgent: 'Custom User Agent',
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "icecast-parser",
"version": "4.0.2",
"version": "4.0.3",
"description": "Node.js module for getting and parsing metadata from SHOUTcast/Icecast radio streams",
"main": "dist/Parser.js",
"license": "MIT",
Expand Down
22 changes: 16 additions & 6 deletions src/Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface ParserOptions {
errorInterval: number
keepListen: boolean
metadataInterval: number
notifyOnChangeOnly: boolean
notifyOnChangeOnly: string[]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
notifyOnChangeOnly: string[]
notifyOnChangeOnly: boolean | string[]

url: string
userAgent: string
}
Expand All @@ -35,7 +35,7 @@ export class Parser extends EventEmitter {
errorInterval: 10 * 60,
keepListen: false,
metadataInterval: 5,
notifyOnChangeOnly: false,
notifyOnChangeOnly: [],
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
notifyOnChangeOnly: [],
notifyOnChangeOnly: false,

url: '',
userAgent: 'icecast-parser',
};
Expand Down Expand Up @@ -115,12 +115,22 @@ export class Parser extends EventEmitter {
}

protected isMetadataChanged (metadata: Map<string, string>): boolean {
for (const [key, value] of metadata.entries()) {
if (this.previousMetadata.get(key) !== value) {
return true;
if (this.options.notifyOnChangeOnly.length > 0) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now, here we can check the type of the option. If there is an array, we treat it by your logic but if there is a boolean, nothing changes and works as before.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (Array.isArray(this.options.notifyOnChangeOnly)) {
  // your new logic
} else if (this.options.notifyOnChangeOnly === true) {
  // old logic
}

for (const key of this.options.notifyOnChangeOnly) {
const data = metadata.get(key)
if (data) {
if (this.previousMetadata.get(key) !== data) {
return true;
}
}
}
} else {
for (const [key, value] of metadata.entries()) {
if (this.previousMetadata.get(key) !== value) {
return true;
}
}
}

return false;
}
}
31 changes: 30 additions & 1 deletion test/Parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,40 @@ describe('parser', () => {
it('should properly emit metadata event when metadata has been updated', async () => await new Promise((resolve) => {
expect.hasAssertions();

const radio = new Parser({ autoUpdate: false, notifyOnChangeOnly: true, url: 'https://live.hunter.fm/80s_high' });
const radio = new Parser({ autoUpdate: false, notifyOnChangeOnly: [], url: 'https://live.hunter.fm/80s_high' });
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is no longer needed then and this test will check that everything works as before

radio.on('metadata', (metadata) => {
// @ts-expect-error I want to check that metadata was stored in the private property to later comparsion
expect(radio.previousMetadata).toStrictEqual(metadata);
resolve();
});
}));

it('should properly emit metadata event when notifyOnChangeOnly is used and when metadata has been updated', async () => await new Promise((resolve) => {
expect.hasAssertions()

const radio = new Parser({ autoUpdate: false, notifyOnChangeOnly: ['StreamTitle'], url: 'https://live.hunter.fm/80s_high' });
radio.on('metadata', (metadata) => {
// @ts-expect-error I want to check that metadata was stored in the private property to later comparsion
expect(radio.previousMetadata).toStrictEqual(metadata);
resolve();
});
}))

it('should not emmit metadata event if notifyOnChangeOnly is incorrect', async () => await new Promise(async (resolve) => {
let triggered = false
const delay = (ms: number) => new Promise(res => setTimeout(res, ms));

const radio = new Parser({ autoUpdate: false, notifyOnChangeOnly: ['a'], url: 'https://live.hunter.fm/80s_high' });
radio.on('metadata', () => {
triggered = true
});

await delay(1000)

if(!triggered) {
resolve()
} else {
throw new Error('metadata event was triggered - Check that the notifyOnChangeOnly data does not exist in the metadata')
}
}))
});