TooTallNate / node-icy

Node.js module for parsing and/or injecting ICY metadata
MIT License
291 stars 48 forks source link

MP3 ICY streams no longer seem to work? #43

Open mo-g opened 1 year ago

mo-g commented 1 year ago

Sample: https://radio.dripfeed.net/listen/monstromental/radio.mp3

HTTP/2 200 
server: nginx
date: Sun, 26 Feb 2023 19:57:38 GMT
content-type: audio/mpeg
pragma: no-cache
expires: Thu, 19 Nov 1981 08:52:00 GMT
cache-control: no-store, no-cache, private
vary: Origin
icy-br: 128
ice-audio-info: channels=2;samplerate=44100;bitrate=128
icy-description: Pulp Horror Instro Surf
icy-genre: Horror
icy-name: MONSTROMENTAL
icy-pub: 1
icy-url: https://www.monstromental.com
x-xss-protection: 1
x-content-type-options: nosniff
referrer-policy: no-referrer-when-downgrade

Also: https://stream.camfm.co.uk/camfm

HTTP/1.1 200 OK
Content-Type: audio/mpeg
Date: Sun, 26 Feb 2023 19:59:40 GMT
Server: Icecast 2.4.0-kh13
Cache-Control: no-cache, no-store
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
Access-Control-Allow-Methods: GET, OPTIONS, HEAD
Connection: Close
Expires: Mon, 26 Jul 1997 05:00:00 GMT
        console.log(response.headers['content-type']);
                                    ^
TypeError: Cannot read properties of undefined (reading 'content-type')

But rawHeaders exist for these streams:

[
  'Content-Type',
  'audio/mpeg',
  'Date',
  'Sun, 26 Feb 2023 20:05:49 GMT',
  'Server',
  'Icecast 2.4.0-kh13',
  'Cache-Control',
  'no-cache, no-store',
  'Access-Control-Allow-Origin',
  '*',
  'Access-Control-Allow-Headers',
  'Origin, Accept, X-Requested-With, Content-Type',
  'Access-Control-Allow-Methods',
  'GET, OPTIONS, HEAD',
  'Connection',
  'Close',
  'Expires',
  'Mon, 26 Jul 1997 05:00:00 GMT',
  'icy-metaint',
  '16000'
]
[
  'Server',
  'nginx',
  'Date',
  'Sun, 26 Feb 2023 20:05:13 GMT',
  'Content-Type',
  'audio/mpeg',
  'Transfer-Encoding',
  'chunked',
  'Connection',
  'close',
  'Pragma',
  'no-cache',
  'Expires',
  'Thu, 19 Nov 1981 08:52:00 GMT',
  'Cache-Control',
  'no-store, no-cache, private',
  'Vary',
  'Origin',
  'icy-br',
  '128',
  'ice-audio-info',
  'channels=2;samplerate=44100;bitrate=128',
  'icy-description',
  'Pulp Horror Instro Surf',
  'icy-genre',
  'Horror',
  'icy-name',
  'MONSTROMENTAL',
  'icy-pub',
  '1',
  'icy-url',
  'https://www.monstromental.com',
  'icy-metaint',
  '16000',
  'X-XSS-Protection',
  '1',
  'X-Content-Type-Options',
  'nosniff',
  'Referrer-Policy',
  'no-referrer-when-downgrade'
]

The application/ogg stream I've been testing against works fine, it's specifically audio/mpeg streams that fail.

mo-g commented 1 year ago

Problem appears to be in client.js inside the "if metaint" block. This is debug with it:

  icy:client request "socket" event +0ms
  icy:client request "response" event +1s
  icy:client got metaint: 16000 +0ms
  icy:client proxying "socket" +4ms
  icy:client proxying "httpVersionMajor" +1ms
  icy:client proxying "httpVersionMinor" +0ms
  icy:client proxying "httpVersion" +0ms
  icy:client proxying "complete" +1ms
  icy:client proxying "rawHeaders" +0ms
  icy:client proxying "rawTrailers" +0ms
  icy:client proxying "aborted" +0ms
  icy:client proxying "upgrade" +1ms
  icy:client proxying "url" +0ms
  icy:client proxying "method" +0ms
  icy:client proxying "statusCode" +1ms
  icy:client proxying "statusMessage" +0ms
  icy:client proxying "client" +0ms
  icy:client proxying "req" +0ms
  icy:client proxying "responseUrl" +0ms
  icy:client proxying "redirects" +0ms

Note that headers are not being proxied, for some reason. If you comment it out, audio plays for the first 16000 samples, then gets crashed by the metadata arriving.

mo-g commented 1 year ago

So this is two separate problems.

First is the headers not being proxied on metaint streams. I've fixed that on my fork. The crashing is actually kind of unrelated - I was using some slightly more complex stream pipes than in the example, and it was the pipes collapsing when the speed of delivery changed. I'm going to look into fixing this in my application by buffering the response before piping it.