Skip to content

Instantly share code, notes, and snippets.

@jonatasleon
Last active July 21, 2023 10:38
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jonatasleon/8e80592cfee9e769236089a4d40beaeb to your computer and use it in GitHub Desktop.
Save jonatasleon/8e80592cfee9e769236089a4d40beaeb to your computer and use it in GitHub Desktop.
Vimeo video downloader with Python 3
#!/bin/env python3
import argparse
import base64
import os
import re
import subprocess
import sys
from tempfile import mkstemp
import requests
from tqdm import tqdm
if len(sys.argv) > 1:
master_json_url = sys.argv[1]
output_file = sys.argv[2]
else:
print('master.json must be passed as argument', file=sys.stderr)
exit(1)
base_url = master_json_url[:master_json_url.rfind('/', 0, -26) + 1]
resp = requests.get(master_json_url)
content = resp.json()
heights = [(i, d['height']) for (i, d) in enumerate(content['video'])]
idx, _ = max(heights, key=lambda h: h[1])
video = content['video'][idx]
video_base_url = base_url + video['base_url']
print('base url:', video_base_url)
filenameVideo = mkstemp(prefix='.mp4')[1]
print ('saving to {}'.format(filenameVideo))
video_file = open(filenameVideo, 'wb')
init_segment = base64.b64decode(video['init_segment'])
video_file.write(init_segment)
for segment in tqdm(video['segments']):
segment_url = video_base_url + segment['url']
resp = requests.get(segment_url, stream=True)
if resp.status_code != 200:
print('not 200!')
print(resp)
print(segment_url)
break
for chunk in resp:
video_file.write(chunk)
video_file.flush()
video_file.close()
# Audio download here
bitrate = [(i, d['bitrate']) for (i, d) in enumerate(content['audio'])]
print('Bitrate', bitrate)
idx, _ = max(bitrate, key=lambda x: x[1])
audio = content['audio'][idx]
audio_base_url = base_url + audio['base_url']
print('Base url:', audio_base_url)
filenameAudio = mkstemp(prefix='.mp4')[1]
print('Saving AUDIO to %s' % filenameAudio)
audio_file = open(filenameAudio, 'wb')
init_segment = base64.b64decode(audio['init_segment'])
audio_file.write(init_segment)
for segment in tqdm(audio['segments']):
segment_url = audio_base_url + segment['url']
segment_url = re.sub(r'/[a-zA-Z0-9_-]*/\.\./',r'/',segment_url.rstrip())
resp = requests.get(segment_url, stream=True)
if resp.status_code != 200:
print('not 200!')
print(resp)
print(segment_url)
break
for chunk in resp:
audio_file.write(chunk)
audio_file.flush()
audio_file.close()
print('Combining video and audio...')
cmd = 'ffmpeg -y -i '
cmd += filenameAudio
cmd += ' -i '
cmd += filenameVideo
cmd += ' ' + output_file
subprocess.call(cmd, shell=True)
print('Mixing Done!')
# Delete the remaining single audio and video files
os.remove(filenameAudio)
os.remove(filenameVideo)
print("Temporary files removed!")
print("Done!")
@jonatasleon
Copy link
Author

Shorter URL for raw: https://git.io/JJ4rV

@jonatasleon
Copy link
Author

jonatasleon commented Jul 24, 2020

Instructions

pip install requests tqdm

curl -o vimeo-download.py -L https://git.io/JJ0sN
chmod u+x vimeo-download.py

./vimeo-download <master.json url>

@bezik46
Copy link

bezik46 commented Sep 29, 2020

Traceback (most recent call last):
File "C:\windows\system32\vimeo-download.py", line 16, in
output_file = sys.argv[2]
IndexError: list index out of range

@jonatasleon
Copy link
Author

How did you call the script in command line?

Did you did that? ./vimeo-download <master.json url> Where master.json url is url that load the metadata necessary to web player play the video.

@woe
Copy link

woe commented Sep 29, 2020

Traceback (most recent call last):
File "C:\windows\system32\vimeo-download.py", line 16, in
output_file = sys.argv[2]
IndexError: list index out of range

it expects 2 arguments
json as the first and the name of the output file as second

so do something like this
./vimeo-download.py https://......master.json?base64_init=1 output.mp4

@TinyFix
Copy link

TinyFix commented Feb 24, 2021

Thank you for script!

Unfortunately I was unable to get video because of /video/ and /audio/ part in master.json url:

...5fa435624613465457624/sep/video/234251241,324535,234254,23452352,53b36b3d/audio/5745456,3245345/master.json

So I've added line

base_url = base_url.split('/video/', 1)[0] + '/video/'

before line 29:

video_base_url = base_url + video['base_url']

And video urls became correct.

UPDATE: mkstemp argument 'prefix' is wrong - it leads to temp files like ''.mp4q3inl_k0".
mkstemp(suffix='.mp4')[1] might be better :)

@p-baum
Copy link

p-baum commented Feb 26, 2021

Also add:

cmd += ' -c:v copy -c:a copy '

after cmd += filenameVideo

to avoid re-encoding

@bendaho
Copy link

bendaho commented Jun 15, 2023

Where can i get master.json url for example for this video https://player.vimeo.com/video/819183591

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