yt-audio-player

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

youtube.js (2779B)


      1 import axios from 'axios';
      2  import vm from 'vm';
      3  
      4  let videoId = 'aqz-KE-bpKQ';
      5  
      6  /**
      7   * From the Youtube API, retrieve metadata about the video (title, video format and audio format)
      8   */
      9  async function retrieveMetadata(videoId) {
     10      const response = await axios.post('https://www.youtube.com/youtubei/v1/player', {
     11          "videoId": videoId,
     12          "context": {
     13              "client": { "clientName": "WEB", "clientVersion": "2.20230810.05.00" }
     14          }
     15      });
     16  
     17      const formats = response.data.streamingData.adaptiveFormats;
     18  
     19      return [
     20          response.data.videoDetails.title,
     21          formats.filter(w => w.mimeType.startsWith("video/webm"))[0], 
     22          formats.filter(w => w.mimeType.startsWith("audio/webm"))[0],
     23      ];
     24  }
     25  
     26  /**
     27   * From the Youtube Web Page, retrieve the challenge algorithm for the n query parameter
     28   */
     29  async function retrieveChallenge(video_id){
     30  
     31      /**
     32       * Find the URL of the javascript file for the current player version
     33       */
     34      async function retrieve_player_url(video_id) {
     35          let response = await axios.get('https://www.youtube.com/embed/' + video_id);
     36          let player_hash = /\/s\/player\/(\w+)\/player_ias.vflset\/\w+\/base.js/.exec(response.data)[1]
     37          return `https://www.youtube.com/s/player/${player_hash}/player_ias.vflset/en_US/base.js`
     38      }
     39  
     40      const player_url = await retrieve_player_url(video_id);
     41  
     42      const response = await axios.get(player_url);
     43      let challenge_name = /\.get\("n"\)\)&&\(b=([a-zA-Z0-9$]+)(?:\[(\d+)\])?\([a-zA-Z0-9]\)/.exec(response.data)[1];
     44      challenge_name = new RegExp(`var ${challenge_name}\\s*=\\s*\\[(.+?)\\]\\s*[,;]`).exec(response.data)[1];
     45  
     46      const challenge = new RegExp(`${challenge_name}\\s*=\\s*function\\s*\\(([\\w$]+)\\)\\s*{(.+?}\\s*return\\ [\\w$]+.join\\(""\\))};`, "s").exec(response.data)[2];
     47  
     48      return challenge;
     49  }
     50  
     51  /**
     52   * Solve the challenge and replace the n query parameter from the url
     53   */
     54  function solveChallenge(challenge, formatUrl) {
     55      const url = new URL(formatUrl);
     56  
     57      const n = url.searchParams.get("n");
     58      const n_transformed = vm.runInNewContext(`((a) => {${challenge}})('${n}')`);
     59  
     60      url.searchParams.set("n", n_transformed);
     61      return url.toString();
     62  }
     63  
     64  
     65  console.log()
     66  console.log("Retrieving metadata")
     67  const [title, video, audio] = await retrieveMetadata(videoId);
     68  const challenge = await retrieveChallenge(videoId);
     69  
     70  console.log()
     71  console.log("Solving challenge")
     72  video.url = solveChallenge(challenge, video.url);
     73  audio.url = solveChallenge(challenge, audio.url);
     74  
     75  export async function get_url(video_id){
     76      const [title, video, audio] = await retrieveMetadata(video_id);
     77      audio.url = solveChallenge(challenge, audio.url);
     78      return(audio.url);
     79  }