首页 > 解决方案 > 从 spotify 获取音频 - 加入音频块

问题描述

为了提高我的编码技能和学习提案,我正在编写一个 chrome 扩展程序,它将拦截来自 open.spotify.com 的流量。我的范围是访问正在播放的曲目并将其保存为 pc 上的音频文件。目前,我发现 xmlhttprequest 将在按下播放按钮时为曲目提供服务,我正在使用 url fetch API 来获取包含此代码块的 blob 文件

const createChunksFromStream = (details) => {
  const chunkUrl = details.url;
  fetch(chunkUrl)
  .then( (res) => res.arrayBuffer() )
  .then( (res) => {
    console.log(res);
    blobChunks.push(res);
  });
}

这将给我一个 ArrayBuffer(我也尝试过使用 blob),它将具有这种结构

ArrayBuffer(4749932)
[[Int8Array]]: Int8Array(4749932) [0, 0, 0, 24, 102, 116, 121, 112, 100, 97, 115, 104, 0, 0, 0, 0, 105, 115, 111, 54, 109, 112, 52, 49, 0, 0, 3, -62, 109, 111, 111, 118, 0, 0, 0, 108, 109, 118, 104, 100, 0, 0, 0, 0, -45, -116, 22, 32, -45, -116, 22, 32, 0, 0, -84, 68, 0, -67, -55, 52, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, …]
[[Int16Array]]: Int16Array(2374966) [0, 6144, 29798, 28793, 24932, 26739, 0, 0, 29545, 13935, 28781, 12596, 0, -15869, 28525, 30319, 0, 27648, 30317, 25704, 0, 0, -29485, 8214, -29485, 8214, 0, 17580, -17152, 13513, 256, 0, 1, 0, 0, 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 512, 0, -31232, 25965, 24948, 0, 0, 0, 8192, 25704, 29292, 0, 0, 0, 0, 17481, 12851, 0, 0, 0, 0, 0, 0, 0, 23040, 17481, 12851, 0, 0, -14571, 17481, …]
[[Int32Array]]: Int32Array(1187483) [402653184, 1887007846, 1752392036, 0, 913273705, 825520237, -1039990784, 1987014509, 1811939328, 1684567661, 0, 538348755, 538348755, 1152122880, 885636352, 256, 1, 0, 0, 256, 0, 0, 0, 256, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 33554432, -2046820352, 1635018093, 0, 536870912, 1919706216, 0, 0, 842220617, 0, 0, 0, 1509949440, 842220617, 0, 1145685781, 1075, 1107296256, 1447645776, 939524096, 1952972800, 980643956, 1768369967, 1651861620, 1836016430, 1869571887, 795176039, 1935762533, 1634741608, 1734437731, 889221733, 892416308, 1915565922, 1634036837, 25971, 1986869248, 30821, 1701646336, 25704, -1124073472, 13513, 1920212992, 30821, 0, 256, 256, 4, 0, 0, 1920220418, 27489, 1802787840, 25704, -1932328192, -1932320746, 8214, 256, -1124073472, 13513, 0, 0, 65536, 16777216, 0, 0, 0, …]
[[Uint8Array]]: Uint8Array(4749932) [0, 0, 0, 24, 102, 116, 121, 112, 100, 97, 115, 104, 0, 0, 0, 0, 105, 115, 111, 54, 109, 112, 52, 49, 0, 0, 3, 194, 109, 111, 111, 118, 0, 0, 0, 108, 109, 118, 104, 100, 0, 0, 0, 0, 211, 140, 22, 32, 211, 140, 22, 32, 0, 0, 172, 68, 0, 189, 201, 52, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, …]
byteLength: (...)

我将每个提取的轨道部分推入一个数组,然后我试图将所有部分加入一个音频文件。

const processChunks = async (blobChunks) => {
  console.log('chunk stream log', blobChunks);
  //let audioBuffer = [];
  //blobChunks.forEach( (chunk) => {
    //let wav = new lamejs.WavHeader.readHeader(new DataView(chunk));
    //console.log(wav);
    //let sample = new Int16Array(chunk, wav.dataOffset, wav.dataLen / 2);
    for(var i = 0; i < blobChunks.length; i ++){
      //let sampleChunk = blobChunks[i]["[[Int16Array]]"];
      let mp3buf = mp3encoder.encodeBuffer(blobChunks[i]);
      if(mp3buf.length > 0){
        mp3Data.push(mp3buf);
      }
    }
  //})

  // see if there's any data left
  let mp3buf = mp3encoder.flush();
  if(mp3buf.length > 0){
    mp3Data.push(mp3buf);
  }
  const output = new Blob([mp3Data], {type: 'audio/mp3'});
  const fileURL = URL.createObjectURL(output);
  chrome.downloads.download({
    saveAs: true,
    url: fileURL
  }, (downloadId) => {
    console.log(downloadId);
    URL.revokeObjectURL(fileURL);
  });
} 

我面临的主要问题是如何加入这些块,我试图打开一个链接,它会下载一个音频文件,但如果长度为 3MB,它也会下载几秒钟。我尝试过使用lamejs 库但没有成功。

在检查details提供的对象后,webRequest.onCompleted我可以看到每个块的获取请求都有一些像这样的标头信息

{frameId: 0, fromCache: false, initiator: "https://open.spotify.com", ip: "2.23.155.163", method: "GET", …}
frameId: 0
fromCache: false
initiator: "https://open.spotify.com"
ip: "2.23.155.163"
method: "GET"
parentFrameId: -1
requestId: "34278"
responseHeaders: Array(15)
0: {name: "Last-Modified", value: "Mon, 11 Nov 2019 15:09:43 GMT"}
1: {name: "ETag", value: ""9bea6e72675dd37c95f3912cbad4fa3b""}
2: {name: "Content-Type", value: "application/octet-stream"}
3: {name: "Accept-Ranges", value: "bytes"}
4: {name: "Cache-Control", value: "no-transform, max-age=10423291"}
5: {name: "Cache-Control", value: "max-age=315360000, no-transform"}
6: {name: "Date", value: "Sun, 03 Jan 2021 17:44:26 GMT"}
7: {name: "Connection", value: "keep-alive"}
8: {name: "Access-Control-Max-Age", value: "86400"}
9: {name: "Access-Control-Allow-Headers", value: "range, pragma, cache-control"}
10: {name: "Access-Control-Allow-Methods", value: "GET"}
11: {name: "Access-Control-Allow-Origin", value: "*"}
12: {name: "Expires", value: "Wed, 01 Jan 2031 17:44:26 GMT"}
13:
name: "Content-Range"
value: "bytes 495280-660808/4825768"
__proto__: Object
14:
name: "Content-Length"
value: "165529"
__proto__: Object
length: 15
__proto__: Array(0)
statusCode: 206
statusLine: "HTTP/1.1 206 Partial Content"
tabId: 127
timeStamp: 1609695866818.061
type: "xmlhttprequest"
...

我正在考虑仅获取具有状态代码 206 的请求并使用Content-Range标头值来检查是否已下载所有块但我不确定这一点,我也在考虑仅获取每个使用的单个请求的主体webRequest但我不知道如何以及是否可能。有什么解决方案可以用来加入文件并获得完整的音轨吗?

标签: javascriptarrays

解决方案


推荐阅读