3

I have built a javascript code to be able to read any Youtube video transcript (gapi.client.youtube.captions.download). The auth 2.0 works fine, I run my app in a local web server everything is fine, the problem is that when I run the request I have the error 403: cb=gapi.loaded_0:164 GET https://content.googleapis.com/youtube/v3/captions/My_API_Key 403 I have not found any solution here in StackOverflow.. any idea ?

Here is my js file:

const CLIENT_ID = 'My_Client_ID';
const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest"];
const SCOPES = 'https://www.googleapis.com/auth/youtube.readonly';

const authorizeButton = document.getElementById('enter-button');
const signoutButton = document.getElementById('exit-button');
const content = document.getElementById('content');

// default youtube channel
const defaultChannel = 'googledevelopers';

// Load auth2 library
function handleClientLoad(){
    gapi.load('client:auth2', initClient);
}

// Init API client library and set up sing in listeners
function initClient(){
    gapi.client.init({
        discoveryDocs: DISCOVERY_DOCS,
        clientId: CLIENT_ID,
        scope: SCOPES
    }).then(() => {
        // Listen for sing state changes
        gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
        // Handle initial sign in state
        updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
        authorizeButton.onclick = handleAuthClick;
        signoutButton.onclick = handleSignouClick;
    });
}

// update UI sign in state changes
function updateSigninStatus(isSignedIn){
    if(isSignedIn){
        authorizeButton.style.display = 'none';
        signoutButton.style.display = 'block';
        content.style.display = 'block';
        getChannel(defaultChannel);
    }else{
        authorizeButton.style.display = 'block';
        signoutButton.style.display = 'none';
        content.style.display = 'none';
    }
}

// Handle Login
function handleAuthClick(){
    gapi.auth2.getAuthInstance().signIn();
}

// Handle Logout
function handleSignouClick(){
    gapi.auth2.getAuthInstance().signOut();
}

// Display channel Data
function showChannelData(data){
    const channelData = document.getElementById('channel-data');
    channelData.innerHTML = data;
}

// Get channel from API
function getChannel(channel){
    gapi.client.youtube.captions.download({
        id: 'guMGyC1tUYAdL3hgBlcGnW4Rt_bBUbtp'
    })
    .then(response => {
        console.log(response);
        const channel = response.result.items[0];
    })
    .catch(err => alert('No Channel By THat Name'));
}

And here is my index.ejs file:

<!DOCTYPE html>
<html lang="en">
    <head>      
        <title>Your awesome Youtube search engine</title>
        <meta charset="UTF-8" />                    
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="Awesome videos!" />
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
    </head>
    <body>
        <header>
            <h1 class="w100 text-center"><a href="index.html">YouTube Viral Search</a></h1>
        </header>

<div class="container">
        <p>Login with Google</p>
        <button class="btn green" id="enter-button">Log In</button>
        <button class="btn green" id="exit-button">Log Out</button>
        <br />
        <div id="content">
            <div class="row">
                <div id="channel-data" class="col s12"></div>
            </div>
        </div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0-rc.2/js/materialize.min.js"></script>
<script src="/javascripts/appYT.js"></script>
    <script async defer src="https://apis.google.com/js/api.js"
      onload="this.onload=function(){};handleClientLoad()"
      onreadystatechange="if (this.readyState === 'complete') this.onload()">
    </script>
    </body>
</html>
enter code here
Lydia halls
  • 652
  • 8
  • 17
  • For more clarifications, here is the error message I see : Insufficient Permission: Request had insufficient authentication scope. I don't understand !! reading any youtube caption track is public right ? and my auth ahthentification works fine – Lydia halls Jul 06 '19 at 18:18
  • Lydia, do you have this error with a single video or with any video? – Mauricio Arias Olave Jul 06 '19 at 18:57
  • I have it with any video – Lydia halls Jul 06 '19 at 19:01
  • Try this [code](https://jsfiddle.net/MauricioSOes/yu19knrL/) and test. Can't say for sure why you have such error. Hope it helps, if so, please indicate and I'll post it as an answer. – Mauricio Arias Olave Jul 06 '19 at 19:02
  • Ok, I will try it. So you'r not using gapi for that ? – Lydia halls Jul 06 '19 at 19:12
  • I'm using YouTube Data API. I'm not familiar with gapi. – Mauricio Arias Olave Jul 06 '19 at 19:18
  • Mauricio you'r the best :) it works number 1 !! Just last question, how to get the time for each script ? – Lydia halls Jul 06 '19 at 19:22
  • And is this working for manual and automatic script as well ? – Lydia halls Jul 06 '19 at 19:30
  • An finally, yes you can post your answer, it works fine and I will be the first to vote for it :) – Lydia halls Jul 06 '19 at 19:39
  • Lydia, I can't tell for sure whether will work with automatic script as well, but, you can try and if it does not work, [edit] your question accordingly. I did edit my answer too if you need more information. Good luck and thanks – Mauricio Arias Olave Jul 06 '19 at 19:43
  • Lydia, if my answer solved your question, [please consider accept it](https://stackoverflow.com/help/accepted-answer). Thank you. – Mauricio Arias Olave Jul 06 '19 at 20:26
  • Mauricio, I just tried this link: https://video.google.com/timedtext?type=track&v=ziGZj_jZ72E&id=0&lang=en it did not work, any idea ? Is it because the video is too long ? Thanks – Lydia halls Jul 06 '19 at 20:42
  • Mauricio, I just tried this link: https://video.google.com/timedtext?type=track&v=ziGZj_jZ72E&id=0&lang=en it did not work, any idea ? it looks like all talk at Google Videos are not responding..!! humm even the shortest ones. I definitely not a matter of file size – Lydia halls Jul 06 '19 at 20:48
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/196087/discussion-between-mauricio-arias-olave-and-lydia-halls). – Mauricio Arias Olave Jul 06 '19 at 21:14

1 Answers1

10

You can use the following code for get the transcript in a given video.

This is the working jsfiddle


N.B here I have the videoId zenMEj0cAC4, but you can change it as you desire.

$.ajax({
  type: "GET",
  url: "https://video.google.com/timedtext?type=track&v=zenMEj0cAC4&id=0&lang=en",
  crossDomain: true,
}).done(function(data) {
  console.log(data);
  getCaption(data);
});


var parser, xmlDoc;
var HTML_captions = "";

// Parse the AJAX response and get the captions.
function getCaption(ajax_response) {
  try {

    parser = new DOMParser();
    xmlDoc = parser.parseFromString(ajax_response, "text/xml");
    //console.log(ajax_response);
    //console.log(xmlDoc.getElementsByTagName("transcript").length);

    if (xmlDoc.getElementsByTagName("transcript").length > 0) {
      // Loop the results of the xmlDoc:
      for (var i = 0; i < xmlDoc.getElementsByTagName("transcript")[0].childNodes.length; i++) {
        console.log(xmlDoc.getElementsByTagName("transcript")[0].childNodes[i].innerHTML);
        HTML_captions += xmlDoc.getElementsByTagName("transcript")[0].childNodes[i].innerHTML + "<br/>";
      }
    } else {
      // Loop the results of the ajax_response;
      for (var i = 0; i < ajax_response.getElementsByTagName("transcript")[0].childNodes.length; i++) {
        console.log(ajax_response.getElementsByTagName("transcript")[0].childNodes[i].innerHTML);
        HTML_captions += ajax_response.getElementsByTagName("transcript")[0].childNodes[i].innerHTML + "<br/>";
      }
    }

    document.getElementById("demo").innerHTML = "<i>Preparing captions...</i>";
    setTimeout(fillData(), 2000);

  } catch (err) {
    console.log(err);
    document.getElementById("demo").innerHTML = ('Error at getCaption function - see console form more details.');
    alert('Error at getCaption function - see console form more details.');
  }
}


// Fill the data "captions" in a HTML "div" control.
function fillData() {
  try {
    document.getElementById("demo").innerHTML = HTML_captions;
  } catch (err) {
    console.log(err);
    document.getElementById("demo").innerHTML = ('Error at fillData function - see console form more details.');
    alert('Error at fillData function - see console form more details.');
  }

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<div id="demo"><i>Loading captions...</i></div>

Just in case you need more information about how you can get automatic closed captions, you can refer to these answers in Stack Overflow:

Mauricio Arias Olave
  • 2,259
  • 4
  • 25
  • 70
  • Mauricio Arias Olave could you just let me know how did you get the link: https://video.google.com/timedtext?type=track&v=zenMEj0cAC4&id=0&lang=en it does not work for all youtube videos ? – Lydia halls Aug 08 '19 at 00:57
  • 1
    @Lydiahalls, you ahve to activate the F12 developer tools "in Google chrome, for example" an d play the video on YouTube, then, check the "network" tab once you pressed the button that activates the captions on the video. You'll see there the requests made. Hope it helps. – Mauricio Arias Olave Aug 10 '19 at 18:08
  • 1
    For some YT videos, neither "https://video.google.com/timedtext?v=... " nor "https://www.youtube.com/api/timedtext?v=..." work when 1- I look at the network tab is it the caption.js that requests one of those url ? 2- if I want to have another language can I just change the "&lang=en&name=CC%20(English)" to "&lang=fr&name=CC%20(French)" for example ? I really appreciate your help Mauricio ;) – Lydia halls Aug 10 '19 at 21:08
  • 1
    it doesn't work for this video for example: https://www.youtube.com/watch?v=9Tl3OmwrSaM 3- do you have any idea why ? Thanks :) – Lydia halls Aug 10 '19 at 21:15
  • @Lydiahalls [link](https://www.youtube.com/api/timedtext?key=yttt1&expire=1565500659&v=9Tl3OmwrSaM&xoaf=1&sparams=asr_langs%2Ccaps%2Cv%2Cxoaf%2Cxorp%2Cexpire&xorp=True&signature=6DC4ADA66EFCD21288C1761868A6DACE08A84D66.51B0F25A833833433415763FD4D8DE51A32A020C&caps=asr&hl=es-419&asr_langs=es%2Cit%2Cru%2Cfr%2Cde%2Cko%2Cen%2Cnl%2Cja%2Cpt&lang=ar&fmt=srv3) – Mauricio Arias Olave Aug 10 '19 at 22:18
  • 1
    Mauricio you'r the best..Now I understand how to get it from Network Tab..many thanks :) – Lydia halls Aug 11 '19 at 00:24
  • @Lydiahalls, you're welcome. I'm glad I was helpful. Have a nice day. – Mauricio Arias Olave Aug 11 '19 at 01:37
  • Does not always work, not sure why (`https://video.google.com/timedtext?type=track&id=0&lang=en&v=E6ZHZ5MKSdU`) – Lee Goddard Sep 13 '20 at 09:31
  • 1
    @LeeGee, in the videoId you supplied, the answer is empty. That might be due to the channel itself. – Mauricio Arias Olave Sep 14 '20 at 13:58
  • 2
    It seems YouTube has added some restrictions to their API. This no longer works. – Peter Olson Nov 12 '21 at 17:10
  • @PeterOlson, you're correct. *Besides web-scraping*, I haven't found a way to get this information (*without the use of the YouTube Data API*). I do recall there was a website that returned the captions with only the link of the video, but, I suspect they use this same way to extract the captions. – Mauricio Arias Olave May 05 '23 at 16:11
  • @MauricioAriasOlave can you explain how you get the captions without using Youtube Data API? – Warren Hogan Jun 19 '23 at 19:21