Youtube upload API and cordova / phonegap

吃可爱长大的小学妹 提交于 2019-12-11 03:25:14

问题


I am writing a cordova app that lets the user record a video and upload it to youtube via their api.

If I use a file input and access the file via

$('#file').get(0).files[0]

I receive a file object which is able to uploaded without a problem.

If I record a video I receive a medialist object. I can then call window.resolveLocalFileSystemURL( video[0].localURL , success, fail);

On the success callback I receive a filelist object. Which also will not be accepted.

On this filelistobject I can call data.file(success,fail) which finally returns me a file object. But when trying to upload to youtube I am getting a 308 error.

I suspect it has to do with permissions of accessing the local file path. If anyone has experience with this I would love to hear a way around it.

Here is the upload code:

UploadVideo.prototype.ready = function(accessToken, video) {
    this.accessToken = accessToken;
    this.gapi = gapi;
    this.authenticated = true;
    $this = this;

function result(data){
    function success(data){

        data.name = "VID_20150329_160037.mp4";
        console.log(data)
        $this.uploadFile( data );
    }
    data.file(success)

}
function fail(data){
    console.log(data)
}
window.resolveLocalFileSystemURL( video[0].localURL , result, fail);


//this.uploadFile( $('#file').get(0).files[0] );

// $('#button').on("click", this.handleUploadClicked.bind(this));

回答1:


Using this blogpost I managed to get metadata and file upload working: http://lithostech.com/2013/10/upload-google-youtube-api-v3-cors/

By logging out the FileTransfer header information before it was sent, I could see the format was different:

Content-Disposition: form-data; name="part"
{"snippet":{"title":"Video title","description":"Video description","tags":"Video tags","categoryId":22},"status":{"privacyStatus":"unlisted"}}

Should be:

Content-Disposition: form-data; name=""; filename="file.json"
Content-Type: application/json
{"snippet":{"title":"Video title","description":"Video description","tags":"Video tags","categoryId":22},"status":{"privacyStatus":"unlisted"}}

Here is an updated version including working metadata:

function uploadVideo(fileURL) {
    var options = new FileUploadOptions();
    options.fileKey = 'file';
    options.fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
    options.mimeType = 'video/mpg';
    options.chunkedMode = false;
    options.headers = {
        Authorization: 'Bearer ' + accessToken
    };
    options.params = {
        "": {
            snippet: {
                title: 'Video title',
                description: 'Video description',
                tags: 'Video tags',
                categoryId: 22
            },
            status: {
                privacyStatus: 'unlisted'
            }
        }
    };
    var ft = new FileTransfer();
    ft.upload(fileURL, 'https://www.googleapis.com/upload/youtube/v3/videos?part=snippet,status', function (data) {
        console.log('upload success', data);
    }, function (e) {
        console.log('upload error', e);
    }, options, true);
    ft.onprogress = function (progressEvent) {
        console.log('onprogress: ' + ((progressEvent.loaded / progressEvent.total) * 100) + '%');
    };
}

note that I modified my FileTransfer plugin slightly too:

FileTransfer.java lines 374 - 376

beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append("\";");
beforeData.append(" filename=\"").append("file.json").append('"').append(LINE_END);
beforeData.append("Content-Type: ").append("application/json").append(LINE_END).append(LINE_END);

After modifying a plugin in cordova/phonegap/ionic you will need to reload it. I do this by removing the platform and adding it again:

cordova platform remove android; cordova platform add android;



回答2:


I finally got it working. I needed to use the cordova filetransfer plugin

here is the code

function postVideo(accessToken, fileURI) {
  var metadata = {
    snippet: {
      title: "test",
      description: "test",
      tags: ["youtube-cors-upload"],
      categoryId: 21
    },
    status: {
      privacyStatus: "unlisted"
    }
  }

  var options = new FileUploadOptions();
  options.fileKey = "file";
  options.fileName = 'test';
  options.mimeType = "video/mp4";
  options.chunkedMode = false;

  options.headers = {
    Authorization: "Bearer " + accessToken,
    "Access-Control-Allow-Origin": "http://meteor.local"
  };

  var params = new Object();
  params.part = Object.keys(metadata).join(',')

  options.params = params;
  console.log(options)
  var ft = new FileTransfer();
  ft.upload(fileURI, "https://www.googleapis.com/upload/youtube/v3/videos?part=snippet", win, fail, options, true);

  ft.onprogress = function(progressEvent) {
    if (progressEvent.lengthComputable) {
      // console.log(progressEvent)
      // loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
    } else {
      console.log('something not loading')
        // loadingStatus.increment();
    }
    console.log(progressEvent.loaded / progressEvent.total);
  };
}

function win(r) {
  console.log(r)
  console.log("Code = " + r.responseCode);
  console.log("Response = " + r.response);
  console.log("Sent = " + r.bytesSent);
}

function fail(error) {
  console.log(error)
    // alert("An error has occurred: Code = " + error.code);
  console.log("upload error source " + error.source);
  console.log("upload error target " + error.target);
}



回答3:


Thank you for sharing information.

I had the same situation when I want to upload selected file in device by using the cordova plugin.

My case is not 308 error but it is the data of file which returned by cordova-file plugin have not right format as Blob format of input file HTML.

So that, There is simple solution to fix issue. It is convert data of file to right Blob format of input file HTML.

I have used sample from Youtube Data API and reference links as below:

  • https://developers.google.com/youtube/v3/docs/videos/insert#examples
  • https://github.com/youtube/api-samples/tree/master/javascript

The main idea of solution for fix this issue

1) Convert data of file to Blob format of input file HTML.
2) Modify content of upload_video.js file.
3) Modify content of cors_upload.js file.

The detail of solution

1) Convert data of file to Blob format of input file HTML:

○ Input content is file which returned by cordova-file plugin
○ Use FileReader HTML5 to read file content as ArrayBuffer.
○ Use Blob object to create new content with data is converted.

*Convert data sample code

UploadVideo.prototype.convertToBlob = function (file, onSuccess) {
          var reader = new FileReader();
          reader.onload = (function (event) {
            var blob = new Blob([new Uint8Array(this.result)], { type: file.type });
            onSuccess(blob);
          });
          reader.readAsArrayBuffer(file);
        };


2) Modify content of upload_video.js file:

○ Create "myFile" property for UploadVideo object which store selected file.
○ Retrieve data content which returned by cordova-file plugin and convert it to Blob format.

*Read file content and convert content to Blob sample code

 var self_ = this; //the instance of UploadVideo object
        window.resolveLocalFileSystemURL(fileUri, function (fileEntry) {
              //success
              fileEntry.file(function(file){
                   //Store selected file to re-use when upload event
            self_.myFile = file;
            //We could use file as input of  convertToBlob ()
            self_.convertToBlob (file, function(blob){
                self_.myFile.newBlob  = blob;
            });
              });
          }, function (response) {
              //error
          });

○ Need to sure new Blob content is already for input of this.uploadFile() in UploadVideo.prototype.handleUploadClicked() method.

*Modifiy source code in handleUploadClicked()

Replace:
   this.uploadFile($('#file').get(0).files[0]);
By new:
   this.uploadFile(this.myFile);



3) Modify content of cors_upload.js file:

○ Modify new upload content from MediaUploader.prototype.sendFile_() method

*Modify source code in sendFile_()

Replace:
   var content = this.file;
By new:
   var content = this.file.newBlob;



That is all my solution to fix this issue. With simple modification of source code.

Best Regards,
Dang Quynh.




回答4:


Continuing off of Kim T's answer, here's how to get it to work on iOS:

Modify the CDVFileTransfer.m file located in the Plugins directory of your xCode project with the code from this pull request. The modifications look like this:

src/ios/CDVFileTransfer.m

@@ -25,6 +25,7 @@ Licensed to the Apache Software Foundation (ASF) under one
+#import <Foundation/NSJSONSerialization.h>

@@ -203,6 +204,11 @@ - (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData
+        // if it is a valid json object get the NSString representation
 +        if ([NSJSONSerialization isValidJSONObject:val]){
 +            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:val options:NSJSONWritingPrettyPrinted error:nil];
 +            val = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
 +        }

Then in that same file, within the requestForUploadCommand function as it iterates over the keys in options, change this line:

[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];

To this:

[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"file.json\"\r\nContent-Type: application/json\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];

It probably doesn't matter, but I changed options:NSJSONWritingPrettyPrinted to options:0 when using the code from the aforementioned pull request.

As soon as I made these changes, re-built the project in xCode and launched it on my wife's iPad, video upload to YouTube with meta tag information worked like a charm.




回答5:


The accepted answer is wrong and does not work. The correct answer is you should use the 2 part resumable upload method detailed in depth here: https://developers.google.com/youtube/v3/guides/using_resumable_upload_protocol

example:

  var videoResourceYT = //yt video resource goes here
  }

  videoResourceYT = JSON.stringify(videoResourceYT)
  var ftoptions = new FileUploadOptions();
  var ft = new FileTransfer();
  $.ajax({
    url:"https://www.googleapis.com/upload/youtube/v3/videos?uploadType=resumable&part=snippet,status",
    method:"POST",
    data: videoResourceYT,
    contentType:"application/json; charset=UTF-8",
        beforeSend: function (request) {
              request.setRequestHeader("Authorization", "Bearer " + token);
              request.setRequestHeader("X-Upload-Content-Length", /*file length, not actually needed*/);
              request.setRequestHeader("X-Upload-Content-Type", "video/*");
        },
        success: function(ytuploadData, ytTextStatus, ytRequest){
          var nextUrlYT = ytRequest.getResponseHeader('Location');
          console.log(nextUrlYT)

          ftoptions.mimeType = "video/*";
          ftoptions.chunkedMode = false;
          ftoptions.headers = {
              "Authorization": 'Bearer ' + token,
              "Content-Type": "video/*"
          };
          ftoptions.httpMethod = "PUT"


          //?part=snippet,status
          ft.upload(/*options go here*/);
        },
        error: function(e1,e2,e3){
        }
  })


来源:https://stackoverflow.com/questions/29330360/youtube-upload-api-and-cordova-phonegap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!