├── README.md ├── LICENSE ├── discord-video.bat └── discord-video.sh /README.md: -------------------------------------------------------------------------------- 1 | # discord-video 2 | A simple bash and batch script that uses ffmpeg to compress videos to fit under 25 MB (Discord free upload limit) 3 | 4 | ## Dependencies 5 | * ffmpeg 6 | 7 | ## Usage 8 | 9 | ### Linux 10 | 11 | ``./discord-video.sh `` 12 | 13 | ### Windows 14 | ``discord-video.bat `` 15 | 16 | Output will then be a file ending with -compressed.webm 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 David Jenne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /discord-video.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set /A MAX_VIDEO_SIZE=187500000 4 | set /A MAX_AUDIO_SIZE=12500000 5 | 6 | if "%~1" == "" goto nofile 7 | 8 | rem Check if file is video, get duration if it is 9 | 10 | for /f "delims=" %%i in ('@ffprobe ^-hide^_banner "%~1" ^-show^_entries format^=duration -v quiet -of csv^=^"p^=0^"') do ( 11 | SET var=%%i 12 | ) 13 | 14 | if [%var%]==[] goto invalidfile 15 | 16 | echo "%~1" is a video file and is %var% seconds long 17 | 18 | rem Calculate bitrate 19 | 20 | for /f "tokens=1,2 delims=." %%a in ("%var%") do ( 21 | set first_part=%%a 22 | set second_part=%%b 23 | ) 24 | 25 | set /a rounded=%first_part% 26 | 27 | set /A VIDEO_BITRATE=%MAX_VIDEO_SIZE% / %rounded% 28 | set /A AUDIO_BITRATE=%MAX_AUDIO_SIZE% / %rounded% 29 | set /A SHOULD_BITRATE=%VIDEO_BITRATE% + %AUDIO_BITRATE% 30 | 31 | echo video should have a bitrate of %SHOULD_BITRATE% kbps 32 | 33 | ffmpeg -hide_banner -i "%~1" -c:v libvpx-vp9 -row-mt 1 -b:v "%VIDEO_BITRATE%" -pix_fmt yuv420p -vf scale=1280:720 -pass 1 -an -f null NUL && ffmpeg -hide_banner -i "%~1" -c:v libvpx-vp9 -cpu-used 3 -row-mt 1 -b:v "%VIDEO_BITRATE%" -pix_fmt yuv420p -vf scale=1280:720 -c:a libopus -b:a "%AUDIO_BITRATE%" -pass 2 "%~1-compressed.mp4" 34 | 35 | goto end 36 | 37 | :nofile 38 | echo Error: No video file selected 39 | echo Usage: %0 ^ 40 | pause 41 | exit /B 1 42 | 43 | :invalidfile 44 | echo Error: "%~1" is not a video file 45 | pause 46 | exit /B 1 47 | 48 | :end 49 | echo "Done." 50 | pause 51 | exit /B 0 -------------------------------------------------------------------------------- /discord-video.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 200 000 000 bits / video length = target bitrate 4 | 5 | MAX_VIDEO_SIZE="187500000" 6 | #MAX_AUDIO_SIZE="12500000" 7 | 8 | # Check argument 9 | 10 | if [[ "$1" == "" ]]; then 11 | echo "Error: No video file selected" 12 | echo "Usage: $0 " 13 | exit 14 | fi 15 | 16 | # Check if file is video, get duration if it is 17 | 18 | DURATION=$(ffprobe -hide_banner "$1" -show_entries format=duration -v quiet -of csv="p=0") 19 | 20 | if [[ "$DURATION" == "" ]]; then 21 | echo Error, ffprobe returned no duration. "$1" is possibly not a video file 22 | exit 23 | fi 24 | 25 | echo "$1" is a video file and is "$DURATION" seconds long 26 | 27 | WIDTH=$(ffprobe -hide_banner "$1" -v quiet -show_entries stream=width -of csv=p=0) 28 | HEIGHT=$(ffprobe -hide_banner "$1" -v quiet -show_entries stream=height -of csv=p=0) 29 | ROTATION=$(ffprobe -hide_banner "$1" -v quiet -show_entries stream_side_data=rotation -of csv=p=0) 30 | 31 | WIDTH=${WIDTH%%,} 32 | HEIGHT=${HEIGHT%%,} 33 | ROTATION=${ROTATION%%,} 34 | [ "$ROTATION" ] || ROTATION=0 35 | 36 | if [ "$WIDTH" -gt "$HEIGHT" ] && [ "${ROTATION##-}" != 90 ]; then 37 | [ "$WIDTH" -gt 1280 ] && WIDTH=1280 38 | HEIGHT=-1 39 | echo "$1 is horizontal video; scaling to $WIDTH x $HEIGHT" 40 | else 41 | WIDTH=-1 42 | [ "$HEIGHT" -gt 1280 ] && HEIGHT=1280 43 | echo "$1 is vertical video; scaling to $WIDTH x $HEIGHT" 44 | fi 45 | 46 | # Calculate bitrate 47 | 48 | ADJUSTED_DURATION=$(printf "%.0f\n" "$DURATION") 49 | VIDEO_BITRATE=$((MAX_VIDEO_SIZE / ADJUSTED_DURATION)) 50 | #AUDIO_BITRATE=$(echo $((MAX_AUDIO_SIZE / ADJUSTED_DURATION))) 51 | 52 | set -e 53 | 54 | ffmpeg \ 55 | -hide_banner \ 56 | -i "$1" \ 57 | -c:v libvpx-vp9 \ 58 | -row-mt 1 \ 59 | -b:v "$VIDEO_BITRATE" \ 60 | -pix_fmt yuv420p \ 61 | -vf scale=$WIDTH:$HEIGHT \ 62 | -pass 1 \ 63 | -an \ 64 | -f null \ 65 | /dev/null 66 | 67 | ffmpeg \ 68 | -hide_banner \ 69 | -i "$1" \ 70 | -c:v libvpx-vp9 \ 71 | -cpu-used 3 \ 72 | -row-mt 1 \ 73 | -b:v "$VIDEO_BITRATE" \ 74 | -pix_fmt yuv420p \ 75 | -vf scale=$WIDTH:$HEIGHT \ 76 | -pass 2 \ 77 | "$1-compressed.mp4" 78 | --------------------------------------------------------------------------------