Last active
March 16, 2025 08:52
-
-
Save shanept/eb081c0d65d848cc48a4a79809c9794e to your computer and use it in GitHub Desktop.
Vimeo DASH stream downloader
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
/*** | |
* This will create 2 separate files: | |
* - Vimeo Rip.mp4 - The video file | |
* - Vimeo Rip.mp4a - The associated audio | |
* | |
* This is by Vimeo's design. These files simply need to be merged. | |
* The following ffmpeg command may be used: | |
* | |
* ffmpeg -i ".\Vimeo Rip.mp4" -i ".\Vimeo Rip.mp4a" -c copy output.mp4 | |
*/ | |
(function() { | |
var url = playerConfig.request.files.dash.cdns[playerConfig.request.files.dash.default_cdn].url; | |
console.info("Ripping DASH stream from VIMEO. This may take some time..."); | |
// First we load the stream manifest | |
var xhr = new XMLHttpRequest | |
xhr.onreadystatechange = function() { | |
if (xhr.readyState == 4) { | |
if (xhr.status == 200) { | |
handleStream(xhr.responseText); | |
} else { | |
console.error("STATUS: " + xhr.status); | |
} | |
} | |
}; | |
xhr.open("GET", url); | |
xhr.send(); | |
function handleStream(streamData) { | |
// Now we pick the highest bitrate stream | |
var stream = JSON.parse(streamData); | |
var base_url = new URL(stream.base_url, url); | |
stream.video.sort(function(x,y) { | |
if (x.width > y.width) return -1; | |
if (x.width < y.width) return +1; | |
return 0; | |
}); | |
stream.audio.sort(function(a, b) { | |
if (a.bitrate > b.bitrate) return -1; | |
if (a.bitrate < b.bitrate) return +1; | |
return 0; | |
}); | |
audioRip(videoRip); | |
function videoRip(cb) { | |
var videoStream = stream.video.filter(v => v.hasOwnProperty('index_segment'))[0]; | |
var videoUrl = base_url + videoStream.base_url; | |
// Now we start gluing the stream together... We need to figure out how many bytes this file is. | |
// We can do this by looking at the end range of the last segment. | |
var lastByte = videoStream.segments.at(-1).url.split('-')[1]; | |
// Now we can forge our URL range to begin at byte 0 and finish at lastByte | |
var forgedBasePath = videoStream.index_segment.split('range=')[0] + 'range=0-' + lastByte; | |
console.info('Picking ' + videoStream.height + 'p stream'); | |
console.debug('Video file size ' + lastByte + 'B. Beginning download now.'); | |
(function() { | |
let x = new XMLHttpRequest(); | |
x.responseType = "blob"; | |
x.onreadystatechange = function() { | |
if (x.readyState == 4 && x.status == 200) { | |
saveData(x.response, "Vimeo Rip.mp4"); | |
if (cb) { | |
cb(); | |
} | |
} | |
}; | |
x.open("GET", videoUrl + forgedBasePath); | |
x.send(); | |
})(); | |
} | |
function audioRip(cb) { | |
var audioStream = stream.audio.filter(a => a.hasOwnProperty('index_segment'))[0]; | |
var audioUrl = base_url + audioStream.base_url; | |
var lastByte = audioStream.segments.at(-1).url.split('-')[1]; | |
var forgedBasePath = audioStream.index_segment.split('range=')[0] + 'range=0-' + lastByte; | |
console.info('Picking ' + audioStream.bitrate + ' bitrate ' + audioStream.codecs + ' stream'); | |
console.debug('Audio file size ' + lastByte + 'B. Beginning download now.'); | |
(function() { | |
let x = new XMLHttpRequest(); | |
x.responseType = "blob"; | |
x.onreadystatechange = function() { | |
if (x.readyState == 4 && x.status == 200) { | |
saveData(x.response, "Vimeo Rip.mp4a"); | |
if (cb) { | |
cb(); | |
} | |
} | |
}; | |
x.open("GET", audioUrl + forgedBasePath); | |
x.send(); | |
})(); | |
} | |
} | |
function saveData(data, name) { | |
var a = document.createElement("a"); | |
document.body.appendChild(a); | |
a.style = "display: none"; | |
url = window.URL.createObjectURL(data); | |
a.href = url; | |
a.download = name; | |
a.click(); | |
window.URL.revokeObjectURL(url); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment