Home > Back-end >  Google Scripts: Urlfetchapp with Youtube API having trouble uploading videos with different owners i
Google Scripts: Urlfetchapp with Youtube API having trouble uploading videos with different owners i

Time:01-21

I am currently trying to automate uploading videos from a shared drive folder to Youtube using the following code from this SO post:

function upload() {
  var driveurl = `https://drive.google.com/file/d/AAAAAAAAAAAAAAAAAAAAAAAAA/view?usp=share_link`
  var match = driveurl.match(/([a-z0-9_-]{25,})[$/&?]/i).pop();
  var url = `https://www.googleapis.com/drive/v3/files/` match `?alt=media`; 
  var title = "title";
  var description = "test";
  var topics = "TEST";
  var blob,mp4_fileId,part,requestResource,response;
  var options = {},snippet = {};

  /*
    You will need to create a GCP standard project and associate it with this Apps Script project-
    In the new code editor click the settings cog wheel and scroll down to:
    Google Cloud Platform (GCP) Project -
    You may get an error:
    In order to change your project, you will need to configure the OAuth consent screen. Configure your OAuth Consent details.
    
    And if you do not have a Google Workspace account then you wont be able to set up the GCP project as "INTERNAL"
    You will need to enable the Google Drive API and the YouTube API in the associated GCP project -
  */

  /*
    This code needs the file ID of the MP4 file in your Google Drive - 
    To get the file ID of an MP4 video file in Google Drive, right click the MP4 in your Google Drive
    and choose, "Get link"
    The link will look like this:
    https://drive.google.com/file/d/FILE_ID_IS_HERE/view?usp=sharing
    In the URL is the file ID
  */

  options = {
    "method" : "get",
    "headers" : {"Authorization": "Bearer "   ScriptApp.getOAuthToken()},
    "muteHttpExceptions":true
  }
  var video = UrlFetchApp.fetch(url,options);
 response = YouTube.Videos.insert({
            snippet:{
                title: title,
                description: description,
                tags: topics
            },
            status:{
              privacyStatus: "public",
            },
       },
       "snippet,status",
       video
   );
   Logger.log(JSON.stringify(response));    
Logger.log(response.id)
}

The code works great when I am the owner of the file. However, when I try and grab a file from another user in the same folder, the code no longer works. It tries to push a video to youtube but results in the error "Processing abandoned" visible in my youtube channel. If I copy the file and become the owner of the copy, it works fine so it is not something special with the specific video.

I thought of trying something like this The function Drive.File.get(ID) does not produce the correct object type i.e. it needs to be a blob but I cannot figure out how to get it to there. DriveApp.getFileById(match).getBlob() is my thought, but I have to wait till tomorrow to test due to hitting my upload limit for YouTube today and I'm not optimistic with the link provided.

It seems like there is some error associated with permissions, but I am not fully sure how to proceed to fix it. Any thoughts would be greatly appreciated.

CodePudding user response:

Changing the code: var video = UrlFetchApp.fetch(url,options); to: var video = DriveApp.getFileById(match).getBlob() solves the issue completely. However, I have learned there is a 50MB file size upload limit using this process, which seems small for videos. However, if anyone is trying to upload any videos smaller than that to their channel. I cleaned up the code and this works well

function upload4() {
  var driveurl = `https://drive.google.com/file/d/AAAAAAAAAAAAAAAAAAAAAAAAA?usp=share_link`
  var match = driveurl.match(/([a-z0-9_-]{25,})[$/&?]/i).pop();
  var title = "title";
  var description = "test";
  var topics = "TEST";
  var video = DriveApp.getFileById(match).getBlob();
  var response = YouTube.Videos.insert({
            snippet:{
                title: title,
                description: description,
                tags: topics
            },
            status:{
              privacyStatus: "private",
            },
       },
       "snippet,status",
       video
   );
   Logger.log(JSON.stringify(response));    
Logger.log(response.id)
}

CodePudding user response:

I believe your goal is as follows.

From your following reply,

we have files that are no larger than 100MB. So <100MB for the file size.

I understood that the file size of the files you want to upload is less than 100 MB.

In this case, it is required to upload it to YouTube with the resumable upload. In this case, I have already posted it on my blog. Ref Author: me But, when I searched it in Stackoverflow, I couldn't find it. So, here, I would like to post it as an answer.

Sample script.

This sample script uses Drive API and YouTube API. So, please enable them at Advanced Google services. And, please copy and paste the following sample script to the script editor of Google Apps Script.

In your showing script in your question, I think that match is the file ID.

function myFunction() {
  // Please set the file ID of video file and the metadata for uploading it to YouTube.
  const fileId = "###";
  const metadata = {
    snippet: { description: "Upload sample.", title: "Sample uploaded video." },
    status: { privacyStatus: "private" },
  };

  // 1. Retrieve location.
  const headers = { authorization: "Bearer "   ScriptApp.getOAuthToken() };
  const url1 = encodeURI(
    "https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status"
  );
  const res1 = UrlFetchApp.fetch(url1, {
    headers,
    payload: JSON.stringify(metadata),
    contentType: "application/json",
    muteHttpExceptions: true,
  });
  if (res1.getResponseCode() != 200) {
    console.log(res1.getContentText());
    return;
  }
  const location = res1.getAllHeaders()["Location"];
  console.log("Got Location.");

  // 2. Calculate chunks.
  const chunkSize = 20971520; // 20 MB
  const { fileSize } = Drive.Files.get(fileId, {
    supportsAllDrives: true,
    fields: "fileSize",
  });
  const chunks = [...Array(Math.ceil(fileSize / chunkSize))].map((_, i, a) => [
    i * chunkSize,
    i == a.length - 1 ? fileSize - 1 : (i   1) * chunkSize - 1,
  ]);
  console.log("Calculated chunk data.");

  // 3. Retrieve chunks from Google Drive with the partial download and uploading chunks to YouTube with the resumable upload.
  const url2 = `https://www.googleapis.com/drive/v3/files/${fileId}?alt=media&supportsAllDrives=true`;
  let res2;
  const len = chunks.length;
  let responseObject;
  chunks.forEach((e, i) => {
    console.log(`Now... ${i   1}/${len}`);
    const params2 = {
      headers: {
        authorization: headers.authorization,
        range: `bytes=${e[0]}-${e[1]}`,
      },
    };
    res2 = UrlFetchApp.fetch(url2, params2).getContent();
    const params3 = {
      headers: { "Content-Range": `bytes ${e[0]}-${e[1]}/${fileSize}` },
      payload: res2,
      muteHttpExceptions: true,
    };
    console.log("Downloaded a chunk, and upload a chunk.");
    const res3 = UrlFetchApp.fetch(location, params3);
    const statusCode = res3.getResponseCode();
    if (statusCode == 200) {
      console.log("Done.");
      // console.log(res3.getContentText());
      responseObject = JSON.parse(res3.getContentText());
    } else if (statusCode == 308) {
      console.log("Upload the next chunk.");
      res2.splice(0, res2.length);
    } else {
      throw new Error(res3.getContentText());
    }
  });
  // console.log(responseObject); // Here, you can retrieve the response object.
  console.log(responseObject.id); // Here, you can retrieve the video ID.

  // Please don't remove the following comment line. This comment line is important for running this script.
  // YouTube.Videos.insert(); // This is used for automatically detecting the scope of "https://www.googleapis.com/auth/youtube" with the script editor.
}

Testing:

When this script is run, you can see the following process in the log.

Got Location.
Calculated chunk data.
Now... 1/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 2/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 3/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 4/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 5/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 6/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 7/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 8/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 9/10
Downloaded a chunk, and upload a chunk.
Upload the next chunk.
Now... 10/10
Downloaded a chunk, and upload a chunk.
Done.
{
  "kind": "youtube#video",
  "etag": "###",
  "id": "###",
  "snippet": {,,,},
  "status": {,,,}
}

IMPORTANT

  • In this sample script, the chunk size is 20 MB as a sample. You can modify this. But, when this size is large, an error might occur. Please be careful about this.
  • If you have no YouTube channel, you cannot upload the video file.
  • When your account is not confirmed by Google side, video data of more than 15 minutes cannot be uploaded. Please be careful about this.
  • In the current stage, the maximum execution time of Google Apps Script is 6 minutes. So, when you want to upload a large video data, the data might not be able to be completely uploaded, because of the maximum execution time. Please be careful about this.
    • If you want to upload the video data with more large size, I think that you can achieve it by executing the script with the time-driven trigger. But, in this case, it is required to modify the above script. Please be careful about this. In this post, the simple sample script is introduced.
    • I confirmed that when I tested 100 MB, no error occurs. So, about your situation, I thought that this script can be used.

References

  • Related