Skip to content

Instantly share code, notes, and snippets.

@alexdrean
Forked from aik099/vimeo-downloader.js
Last active July 20, 2023 22:23
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save alexdrean/a027dd89a90a6d0aa54983f87f34a5c4 to your computer and use it in GitHub Desktop.
Save alexdrean/a027dd89a90a6d0aa54983f87f34a5c4 to your computer and use it in GitHub Desktop.
Download video from Vimeo (chopped m4s files)
// 1. Open the browser developper console on the network tab
// 2. Start the video
// 3. In the dev tab, locate the load of the "master.json" file, copy its full URL
// 4. Run: node vimeo-downloader.js "<URL>"
// (done automatically now) 5. Combine the m4v and m4a files with mkvmerge
const fs = require('fs');
const url = require('url');
const https = require('https');
const { exec } = require('child_process');
let masterUrl = process.argv[2];
if (!masterUrl.endsWith('?base64_init=1')) {
masterUrl+= '?base64_init=1';
}
getJson(masterUrl, (err, json) => {
if (err) {
throw err;
}
const videoData = json.video.sort((v1,v2) => v1.avg_bitrate - v2.avg_bitrate).pop();
const audioData = json.audio.sort((a1,a2) => a1.avg_bitrate - a2.avg_bitrate).pop();
const videoBaseUrl = url.resolve(url.resolve(masterUrl, json.base_url), videoData.base_url);
const audioBaseUrl = url.resolve(url.resolve(masterUrl, json.base_url), audioData.base_url);
processFile('video', videoBaseUrl, videoData.init_segment, videoData.segments, json.clip_id + '.m4v', (err) => {
if (err) {
throw err;
}
processFile('audio', audioBaseUrl, audioData.init_segment, audioData.segments, json.clip_id + '.m4a', (err) => {
if (err) {
throw err;
}
console.log('combining video and audio...');
let combineCmd = 'ffmpeg -i ' + json.clip_id + '.m4v -i ' + json.clip_id + '.m4a -c copy ' + json.clip_id + '.mp4';
exec(combineCmd, (err, stdout, stderr) => {
if (err) {
exec('file ' + json.clip_id + '.m4v ' + json.clip_id + '.m4a', () => throw err);
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
console.log('removing video and audio in favor of combined version...');
fs.unlink(json.clip_id + '.m4v', (err) => {
if (err) {
throw err;
}
fs.unlink(json.clip_id + '.m4a', (err) => {
if (err) {
throw err;
}
console.log('all done');
});
});
});
});
});
});
function processFile(type, baseUrl, initData, segments, filename, cb) {
if (fs.existsSync(filename)) {
console.log(`${type} already exists`);
return cb();
}
const segmentsUrl = segments.map((seg) => baseUrl + seg.url);
const initBuffer = Buffer.from(initData, 'base64');
fs.writeFileSync(filename, initBuffer);
const output = fs.createWriteStream(filename, {flags: 'a'});
combineSegments(type, 0, segmentsUrl, output, (err) => {
if (err) {
return cb(err);
}
output.end();
cb();
});
}
function combineSegments(type, i, segmentsUrl, output, cb) {
if (i >= segmentsUrl.length) {
console.log(`${type} done`);
return cb();
}
console.log(`Download ${type} segment ${i}`);
https.get(segmentsUrl[i], (res) => {
res.on('data', (d) => output.write(d));
res.on('end', () => combineSegments(type, i+1, segmentsUrl, output, cb));
}).on('error', (e) => {
cb(e);
});
}
function getJson(url, cb) {
let data = '';
https.get(url, (res) => {
res.on('data', (d) => data+= d);
res.on('end', () => cb(null, JSON.parse(data)));
}).on('error', (e) => {
cb(e);
});
}
@cesarcf
Copy link

cesarcf commented Aug 31, 2021

@alexdrean can you help me with this?

@jrichardsz
Copy link

same error!

@alexdrean
Copy link
Author

alexdrean commented Oct 3, 2021

Unfortunately I do not have the time to look into this right now. You may try combining video and audio externally, or search ffmpeg forums about this. It seems like the downloaded content is not in .m4v format. I would suggest replacing every instance of ".mp4" with other common video file extensions and see if you can find the correct one. You could also analyze the downloaded video-only with the file command and deduct the correct extension that way. I've updated the script so it does that automatically in case ffmpeg crashes. This should help you figure out what is happening here.

@kevinaso
Copy link

Hi folks, not a very experienced one. Where am Im supposed to input the master.json url? Also it will work just running the script in visual studio code or is it necessary to set up a project in a dedicated IDE?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment