I am trying to allow users to upload YouTube videos from my website, but am not using the Javascript API, as users need to be able to upload content to my channel, rather than using their own accounts, and I couldn't figure out how to do that with the JS API.
Anyway, the end result is that I have an HTML element posting the video. The video gets uploaded, but ends up with title "unknown" and no description, etc. Not sure what I am doing wrong. Here is the payload of the HTTP POST (as multipart/form-data
) that's getting sent to https://www.googleapis.com/upload/youtube/v3/videos?part=snippet,status&access_token=[accesstoken] :
------WebKitFormBoundaryqKACn63lpjqwi0sA
Content-Disposition: form-data; name="status[privacyStatus]"
unlisted
------WebKitFormBoundaryqKACn63lpjqwi0sA
Content-Disposition: form-data; name="snippet[title]"
Test Video Title
------WebKitFormBoundaryqKACn63lpjqwi0sA
Content-Disposition: form-data; name="snippet[description]"
Test video description
------WebKitFormBoundaryqKACn63lpjqwi0sA
Content-Disposition: form-data; name="videoFile"; filename="test_vide_upload.mp4"
Content-Type: video/mp4
------WebKitFormBoundaryqKACn63lpjqwi0sA--
I am assuming that the form names for snippet
and status
fields are wrong, but I've tried various options and can't get anything to work. I also tried sending a single form field data
with the JSON-encoded value {"snippet":{"description":"Test video description","title":"Test Video Title"},"status":{"privacyStatus":"unlisted"}}
but that did not work either.
I do always get a successful JSON response from the API, with the snippet, and status, and the video is uploaded, but the snippet and status have not been set, they are either empty or default values. Any clues?
If you want to send it in a single POST you need to send the metadata (i.e. snippet and status arrays) in a JSON blob object (e.g. via JSON.stringify), essentially as a second file upload. You probably also ought to have the access token set in the header (surprised it's working in the URL):
headers:
{
Authorization: 'Bearer ' + [accesstoken]
}
There is an excellent JS/CORS example here:
https://code.google.com/p/youtube-api-samples/source/browse/yt-upload-javascript/index.js
You can do away with the
window.oauth2Callback = function(authResult) {...}
bit since you already have the access token you need, but otherwise it should work for you (I have a proof of concept doing the exact same thing, borrowing heavily from this code).
There is also some useful/related content here:
http://lithostech.com/2013/10/upload-google-youtube-api-v3-cors/
Steve appears to have worked out the necessity of posting metadata as a binary object by inspecting the ruby client libary's output. And according to this stackoverflow post:
Upload video on Youtube using curl and api v3
the same thing can be accomplished via two posts (see Chad Befus' answer), the first of which contains metadata, and second of which contains the video file. I have no experience with that method.
I was able to get this work in the end only by making 2 API calls - one to upload the video (as a multipart/form-data
POST) and getting the resulting ID, and a second to update the video by that ID (as an application/json
PUT with snippet
in the body).
I started with an HTML form like this:
<form id="upload-yt-video" action="https://www.googleapis.com/upload/youtube/v3/videos?part=snippet&access_token=YOUR_TOKEN_HERE" method="post" enctype="multipart/form-data">
<input type="text" name="title" placeholder="Video title">
<textarea name="description" placeholder="Video description"></textarea>
<input type="file" accept="video/*" name="videoFile">
<input type="submit" value="Upload Video">
</form>
That form uploads the video all on its own, and then in JS you can capture the form submission result to get the video ID, then make a second AJAX call in pure JS to update it:
var YOUTUBE_API_TOKEN = 'YOUR_TOKEN_HERE';
var YOUTUBE_VID_CATEGORY_ID = 24; // "entertainment" as an example - note that a category ID is required
var YOUTUBE_VID_PRIVACY_STATUS = 'unlisted';
$('#upload-yt-video').ajaxForm({
success: function(res, status) {
if (status !== 'success' || ! res.id) {
console.error('problem uploading the video, response status:', status, 'response:', res);
return;
}
console.log('form submission successful, video uploaded to youtube! response:', res);
updateYouTubeVideo({
id: res.id,
token: YOUTUBE_API_TOKEN,
title: $('#upload-yt-video [name=title]').val(),
description: $('#upload-yt-video [name=description]').val()
}, function(err, res) {
if (err) {
console.error('problem uploading the video - error while updating video attributes after upload:', err);
}
else {
console.log('video updated! upload complete. response:', res);
}
});
},
error: function() {
console.error('form submission failed', arguments);
}
});
function updateYouTubeVideo(args, cb) {
if (!args || !args.id || !args.token) {
console.error('missing args: must at least include id and token');
return;
}
var apiArgs = {
id: args.id,
snippet: {
description: args.description || '',
title: args.title || 'Video ' + new Date().toDateString(),
categoryId: YOUTUBE_VID_CATEGORY_ID
},
status: {
privacyStatus: YOUTUBE_VID_PRIVACY_STATUS
}
};
$.ajax({
type: 'PUT',
url: 'https://www.googleapis.com/youtube/v3/videos?part=snippet,status',
contentType: 'application/json',
headers: {
Authorization: 'Bearer ' + args.token
},
data: JSON.stringify(apiArgs),
dataType: 'text',
success: function(data) {
if ($.type(data) === "string") data = JSON.parse(data);
cb(null, data);
},
error: function(request, err){
cb(err);
}
});
}
Note #1 - this uses this jQuery plugin to capture the initial form submit.
Disclaimer #1 - I adapted this from a codebase I have that's no longer live, so I wasn't able to test it, so there may be minor typos or bugs in here.
Disclaimer #2 - This worked in September 2014 - there may have been updates to the API since then!