前两天更新了一下网站的音乐功能,在原来随机展示10条网易云我喜欢的音乐的基础上,把5sing我收藏的音乐也融合到一起了,由于 5sing API 没有返回 mp3 的时长,网易云的有返回,所以改为通过浏览器接口返回时长。
上线后发现在 Safari 浏览器中,播放进度条显示异常,经调试发现 getDuration() 方法返回 Infinite 无限大。在网上搜了一下原因是 Safari 浏览器和 Chrome 浏览器不同,Safari 在请求的时候发送了两个请求,第一个请求只是尝试获取一些字节,Header("Range", "bytes=0-1")
,如果服务端没有根据这个请求返回相应的字节内容,Safari 的下一个请求就会加载全量音频数据,音频时长无限大。
原始代码如下:
@Get('/kinds/:kind/songs/:songId')
async getSongStream(
@Param('kind') kind: string,
@Param('songId') songId: string,
@Res() res,
) {
const url = await this.getSongUrl(songId, kind);
const stream = request.get(url);
stream.pipe(res);
}
修改后的代码:
@Get('/kinds/:kind/songs/:songId/stream')
async getSongStream(
@Param('kind') kind: string,
@Param('songId') songId: string,
@Headers('range') range: string,
@Req() req: Request,
@Res() res: Response,
) {
const { url, size } = await this.getSongUrl(songId, kind);
res.setHeader('Content-Type', 'audio/mpeg');
res.setHeader('Content-Length', size);
if (range === 'bytes=0-1') {
res.setHeader('Content-Range', `bytes 0-1/${size}`);
res.send('1');
return;
}
res.setHeader('Accept-Ranges', 'bytes');
const stream = request.get(decodeURIComponent(url));
stream.pipe(res);
}
修改后 Safari 正常可以获取到时长了,不过最后发现脑子秀逗了,直接重定向到音频的真实URL就可以,没必要通过 Stream 转发。
最终代码如下:
@Get('/kinds/:kind/songs/:songId')
async getSongUrl(
@Param('kind') kind: string,
@Param('songId') songId: string,
@Res() res: Response,
) {
const { url } = await this.getSongUrl(songId, kind);
res.redirect(url);
}