I'd like to stream MP3 audio files from a NodeJS / ExpressJS backend. I found some code in forums to begin with (e.g. here stream mp3 file express server with ability to fast forward/rewind), however it doesn't seem to work for me. I have the below code:
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
app.get('/api/play', (req, res) => {
var music = path.join('Z:', 'Music', 'Metallica', "Kill 'Em All", '01 - Hit the Lights.mp3');
var stat = fs.statSync(music);
var range = req.headers.range;
var readStream;
if (range != undefined) {
var parts = range.replace(/bytes=/, '').split('-');
var partialStart = parts[0];
var partialEnd = parts[1];
if (isNaN(partialStart) || partialStart == '') {
partialStart = '0';
}
if (isNaN(partialEnd) || partialEnd == '') {
partialEnd = stat.size - 1;
}
var start = parseInt(partialStart, 10);
var end = parseInt(partialEnd, 10);
var contentLength = end - start + 1;
console.log(`Streaming ${music} as ${contentLength} bytes from ${start} to ${end}`);
res.writeHead(206, {
'Content-Type': 'audio/mpeg',
'Content-Length': contentLength,
'Content-Range': 'bytes=' + start + '-' + end + '/' + stat.size,
'Accept-Ranges': 'bytes'
});
readStream = fs.createReadStream(music, { start: start, end: end });
} else {
console.log(`Streaming ${music} as ${stat.size} bytes`);
res.header({
'Content-Type': 'audio/mpeg',
'Content-Length': stat.size
});
readStream = fs.createReadStream(music);
}
readStream.pipe(res);
console.log('Done');
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
index.html is basically just an audio tag:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<audio controls="controls" preload="all">
<source src="http://localhost:5000/api/play" type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</body>
</html>
When I open the page from a browser, I get the audio tag but it is not playing. No error in the console, and in the network tab I see that the /api/play request is only 229 bytes, with following response headers:
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 4108683
Content-Range: bytes=0-4108682/4108683
Content-Type: audio/mpeg
Date: Mon, 30 Sep 2019 18:01:02 GMT
X-Powered-By: Express
However, when I try the same from the RestMan extension, I get the full 4MB file as a response with below headers:
date: Mon, 30 Sep 2019 17:41:01 GMT
connection: keep-alive
x-powered-by: Express
content-length: 15987040
content-type: audio/mpeg
I tried to execute manually from the RestMan client with different "Accept" headers, "0-" as the browser does, "0-2", "0-200", "100-200", etc, I always get the correct response with the correct amount of bytes (e.g. 101 bytes for "100-200"). So why is it that the browser fails to load the audio? Even when I open the /api/play URL directly, I only get an auto-generated "video" tag but it is not playing, same thing happens.
I also tried MP3 without spaces or special characters in the file name, tried loading it from a directory without spaces or special charactes, load from local drive (Z: is network attached storage), tried loading from same directory as the html, it's always the same.
Please help if you have some idea. I'm not sure if it is a problem or some missing thing on the NodeJS side, or something else entirely.