fetch audio from spotify - join audio chunks

Viewed 214

To improve my coding skills and for learning propouse, I'm writing a chrome extension that will intercept the traffic from open.spotify.com My scope is to access to the track that is playing and save it as an audio file on the pc. At the moment I've found the xmlhttprequest that will serve the tracks when the play button is pressed, I'm using on the url fetch API to grab the blob file that contain the chunk with this code

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

This will give me an ArrayBuffer (I've also tried with blob) that will have this structure

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: (...)

I'm pushing each fetched part of the track inside an array and then I'm trying to join all into an audio file.

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);
  });
} 

The main problem I'm facing is how I can join the chunks, I've tried to open a single link and it will download an audio file but of few seconds also if the length is for example 3MB. I've tried with lamejs library but without success.

After inspecting the details object provided from webRequest.onCompleted I'm able to see that each fetch request for chunks have some headers info like this

{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"
...

I'm thinking to fetch only request with status code 206 and to use the Content-Range header value to check if all the chunks are downloaded but I'm not sure about this, I'm thinking also to get only the body of each single request using webRequest but I don't know how and if it's possible. Is there any solution I can use to join the files and obtain a full audio track?

0 Answers
Related