猫小白

猫小白

前两天更新了一下网站的音乐功能,在原来随机展示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);
}
Safari