Created
September 17, 2018 13:06
-
-
Save mooyoul/4e04531a7a614ec0c524822a4d04202e to your computer and use it in GitHub Desktop.
Youtube Upload example w/express
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Login</title> | |
</head> | |
<body> | |
<a href="/auth">Click here to sign in</a> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "youtube-upload", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "MooYeol Prescott Lee <[email protected]>", | |
"license": "MIT", | |
"dependencies": { | |
"express": "^4.16.3", | |
"express-session": "^1.15.6", | |
"googleapis": "^33.0.0" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const crypto = require('crypto'); | |
const http = require('http'); | |
const app = require('express')(); | |
const session = require('express-session'); | |
const { google } = require('googleapis'); | |
const path = require('path'); | |
const qs = require('querystring'); | |
// get your own oauth2 credentails on https://console.developers.google.com | |
const GOOGLE_OAUTH2_CLIENT_ID = 'YOUR_CLIENT_ID'; | |
const GOOGLE_OAUTH2_CLIENT_SECRET = 'YOUR_CLIENT_SECRET'; | |
const GOOGLE_OAUTH2_CALLBACK_URL = 'YOUR_CALLBACK_URL'; | |
const GOOGLE_OAUTH2_REQUIRED_SCOPES = [ // DO NOT MODIFY | |
'https://www.googleapis.com/auth/youtube.upload', | |
]; | |
const googleOAuth2Client = new google.auth.OAuth2( | |
GOOGLE_OAUTH2_CLIENT_ID, | |
GOOGLE_OAUTH2_CLIENT_SECRET, | |
GOOGLE_OAUTH2_CALLBACK_URL, | |
); | |
app.use(session({ | |
secret: crypto.randomBytes(32).toString('hex'), | |
saveUninitialized: false, | |
resave: false, | |
})); | |
app.get('/', (req, res) => { | |
if (req.session.auth) { | |
return res.redirect(302, '/upload'); | |
} | |
res.sendFile(path.join(__dirname, 'index.html')); | |
}); | |
app.get('/upload', (req, res) => { | |
if (!req.session.auth) { | |
return res.redirect(302, '/'); | |
} | |
res.sendFile(path.join(__dirname, 'upload.html')); | |
}); | |
app.post('/api/video', async (req, res) => { | |
if (!req.session.auth) { | |
return res.sendStatus(401); | |
} | |
console.log('reading file information'); | |
const fileSize = req.get('content-length'); | |
console.log('got file size: %d bytes', fileSize); | |
try { | |
// Do not use global oauth2 client to prevent | |
// usage of unexpected credentials on race condition. | |
const auth = new google.auth.OAuth2( | |
GOOGLE_OAUTH2_CLIENT_ID, | |
GOOGLE_OAUTH2_CLIENT_SECRET, | |
GOOGLE_OAUTH2_CALLBACK_URL, | |
); | |
auth.setCredentials(req.session.auth); | |
const youtube = google.youtube({ version: 'v3', auth }); | |
console.log('starting upload...'); | |
const { data } = await youtube.videos.insert({ | |
part: [ | |
'id', | |
'snippet', | |
'status' | |
].join(), | |
notifySubscribers: false, // i don't want to notify to channel subscribers | |
requestBody: { | |
snippet: { | |
title: 'Video Title', | |
description: [ | |
'Video Description', | |
'', | |
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', | |
].join('\n'), | |
}, | |
status: { | |
privacyStatus: 'unlisted', // i don't want to show this video on public | |
}, | |
}, | |
media: { // pass-through entire request body to youtube server | |
body: req, | |
}, | |
}); | |
res.send(data); | |
} catch (e) { | |
console.error('Upload filed: ', e.stack); | |
console.error('Received: ', e.response.status, e.response.data); | |
res.sendStatus(500); | |
} | |
}); | |
// Routes for OAuth2 Flow | |
app.get('/auth', (req, res) => { | |
const state = req.session.state = crypto.randomBytes(16).toString('hex'); | |
const redirectionUrl = googleOAuth2Client.generateAuthUrl({ | |
access_type: 'online', | |
prompt: 'select_account', | |
response_type: 'code', | |
scope: GOOGLE_OAUTH2_REQUIRED_SCOPES, | |
state, | |
}); | |
res.redirect(302, redirectionUrl); | |
}); | |
app.get('/auth/callback', async (req, res) => { | |
const { code, state, error } = req.query; | |
// google returned an error | |
if (error) { | |
return res.redirect(302, `/?${qs.stringify({ error })}`); | |
} | |
// perform state validation | |
if (!state || state !== req.session.state) { | |
return res.redirect(302, `/?${qs.stringify({ error: 'INVALID_STATE' })}`); | |
} | |
// get oauth2 access token | |
try { | |
const { tokens } = await googleOAuth2Client.getToken(code); | |
console.log('got token: ', tokens); | |
req.session.auth = tokens; | |
res.redirect(302, '/upload'); | |
} catch (e) { | |
console.error('got error from google: ', e.stack); | |
return res.redirect(302, `/?${qs.stringify({ error: e.toString() })}`); | |
} | |
}); | |
const server = http.createServer(app); | |
server.listen(process.env.PORT || 3001, '0.0.0.0', () => { | |
const { address, port } = server.address(); | |
console.log('server listening in %s:%s', address, port); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Upload</title> | |
</head> | |
<body> | |
<form onsubmit="upload(event)"> | |
<input type="file" id="file"> | |
<button type="submit">Upload</button> | |
</form> | |
<pre id="code"></pre> | |
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script> | |
<script type="text/javascript"> | |
async function upload(e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
const fileEl = document.querySelector('#file'); | |
const codeEl = document.querySelector('#code'); | |
if (!fileEl.files.length) { | |
alert('Please select file'); | |
return; | |
} | |
const res = await axios({ | |
method: 'POST', | |
url: '/api/video', | |
data: fileEl.files[0], | |
}); | |
codeEl.innerText = JSON.stringify(res, null, 2); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment