Skip to content

Instantly share code, notes, and snippets.

@MrSimbax
Last active December 6, 2021 09:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrSimbax/b22d68f08d4b469ae837fa7bf65544fb to your computer and use it in GitHub Desktop.
Save MrSimbax/b22d68f08d4b469ae837fa7bf65544fb to your computer and use it in GitHub Desktop.
A script to convert a video to a desired file size.
#!/usr/bin/env python
# A script to convert a video to a desired filesize.
# Example use case: 8 MB Discord upload limit
#
# Requirements:
# - Python 3
# - ffmpeg should be in PATH
# - ffmpeg-python
import argparse
import os
import ffmpeg
import math
def get_output_path_from_input_path(in_path, postfix):
in_dir = os.path.dirname(in_path)
in_basename = os.path.basename(in_path)
out_name = os.path.splitext(in_basename)[0] + postfix
out_ext = os.path.splitext(in_basename)[1]
out_basename = out_name + out_ext
return os.path.join(in_dir, out_basename)
def convert_mb_to_kbit(size_in_mb):
return size_in_mb * 8000
def get_video_duration_in_seconds(in_path):
metadata = ffmpeg.probe(in_path)
return math.ceil(float(metadata['format']['duration']))
def calc_output_bitrate(desired_file_size_in_kbit,
video_duration_in_seconds,
audio_bitrate_in_kbit_per_sec):
return int(desired_file_size_in_kbit / video_duration_in_seconds) - audio_bitrate_in_kbit_per_sec
def null_output_path():
if os.name == 'nt':
return 'NUL'
else:
return '/dev/null'
def print_ffmpeg_args(stream):
print('ffmpeg ' + ' '.join(ffmpeg.get_args(stream)))
def as_kbit_ffmpeg_arg(bitrate):
return str(bitrate) + 'k'
def main():
parser = argparse.ArgumentParser(description='Convert a video to the desired file size by using ffmpeg (by default 8 MB)')
parser.add_argument('input', help='Path to the video to be converted')
parser.add_argument('-o', '--output', help='Path to the final video', type=str)
parser.add_argument('-s', '--output_size', help='The desired size of the video in MB', type=float, default=8)
parser.add_argument('-a', '--output_audio_bitrate', help='The desired audio bitrate in kb/s', type=int, default=128)
parser.add_argument('-r', '--output_height', help='The desired resolution height', type=int)
args = parser.parse_args()
if not args.output:
args.output = get_output_path_from_input_path(args.input, postfix='_small')
print('Output path: ' + args.output)
desired_bitrate = calc_output_bitrate(
convert_mb_to_kbit(args.output_size),
get_video_duration_in_seconds(args.input),
args.output_audio_bitrate)
print('Calculated video bitrate: {} kbit/s'.format(desired_bitrate))
# Based on https://trac.ffmpeg.org/wiki/Encode/H.264#twopass
# 1st pass
ffmpeg_args = {
'pass': 1,
'an': None,
'c:v': 'libx264',
'b:v': as_kbit_ffmpeg_arg(desired_bitrate),
'format': 'mp4'
}
if args.output_height:
ffmpeg_args['filter:v'] = 'scale=-1:{}'.format(args.output_height)
stream = ffmpeg.input(args.input)
stream = ffmpeg.output(
stream,
null_output_path(),
**ffmpeg_args)
stream = ffmpeg.overwrite_output(stream)
print_ffmpeg_args(stream)
out, _ = ffmpeg.run(stream, capture_stdout=True)
print(out)
# 2nd pass
ffmpeg_args = {
'pass': 2,
'c:a': 'aac',
'b:a': as_kbit_ffmpeg_arg(args.output_audio_bitrate),
'c:v': 'libx264',
'b:v': as_kbit_ffmpeg_arg(desired_bitrate),
'format': 'mp4'
}
if args.output_height:
ffmpeg_args['filter:v'] = 'scale=-1:{}'.format(args.output_height)
stream = ffmpeg.input(args.input)
stream = ffmpeg.output(
stream,
args.output,
**ffmpeg_args)
stream = ffmpeg.overwrite_output(stream)
print_ffmpeg_args(stream)
out, _ = ffmpeg.run(stream, capture_stdout=True)
print(out)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment