Created
June 2, 2026 07:37
-
-
Save Mirochiu/927b3b99e1d71959d4084bbcbc78ee9a to your computer and use it in GitHub Desktop.
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
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # ========================================================= | |
| # FFmpeg Static Build Auto Updater | |
| # Test on Ubuntu 22.04 | |
| # Source: https://johnvansickle.com/ffmpeg/ | |
| # ========================================================= | |
| BASE_URL="https://johnvansickle.com/ffmpeg/releases" | |
| FILE_NAME="ffmpeg-release-amd64-static.tar.xz" | |
| INSTALL_DIR="/usr/local/bin" | |
| TMP_DIR="$(mktemp -d)" | |
| BACKUP_DIR="/usr/local/bin/ffmpeg-backup-$(date +%Y%m%d-%H%M%S)" | |
| cleanup() { | |
| # Safety guard against accidental rm -rf misuse | |
| # ${TMP_DIR:-} prevents unbound variable errors | |
| # -n ensures the variable is not empty | |
| # -d ensures the target is a directory | |
| # Restrict deletion scope to /tmp/ only | |
| # Prevent root directory deletion (/) | |
| # Use -- to prevent path being interpreted as options | |
| if [[ -n "${TMP_DIR:-}" ]] \ | |
| && [[ -d "$TMP_DIR" ]] \ | |
| && [[ "$TMP_DIR" == /tmp/* ]] \ | |
| && [[ "$TMP_DIR" != "/" ]]; then | |
| rm -rf -- "$TMP_DIR" | |
| else | |
| echo "WARNING: Skip unsafe cleanup path: '$TMP_DIR'" | |
| fi | |
| } | |
| trap cleanup EXIT | |
| # --------------------------------------------------------- | |
| # Download helper | |
| # --------------------------------------------------------- | |
| download() { | |
| local url="$1" | |
| local output="$2" | |
| if command -v curl >/dev/null 2>&1; then | |
| curl -L --progress-bar -o "$output" "$url" | |
| elif command -v wget >/dev/null 2>&1; then | |
| wget -q --show-progress -O "$output" "$url" | |
| else | |
| echo "ERROR: curl or wget required" | |
| exit 1 | |
| fi | |
| } | |
| # --------------------------------------------------------- | |
| # normalize version string | |
| # e.g. | |
| # 7.0.2-static -> 7.0.2 | |
| # 7.1-amd64-static -> 7.1 | |
| # ffmpeg-7.1.1-xxx -> 7.1.1 | |
| # --------------------------------------------------------- | |
| normalize_version() { | |
| echo "$1" \ | |
| | grep -oE '[0-9]+(\.[0-9]+){1,2}' \ | |
| | head -n1 | |
| } | |
| get_version() { | |
| local ffmpeg_bin="${1:-ffmpeg}" | |
| # resolve from PATH if not absolute/relative path | |
| if [[ "$ffmpeg_bin" != */* ]]; then | |
| ffmpeg_bin=$(command -v "$ffmpeg_bin" || true) | |
| fi | |
| if [[ ! -x "$ffmpeg_bin" ]]; then | |
| echo "ERROR: ffmpeg not executable: $ffmpeg_bin" >&2 | |
| return 1 | |
| fi | |
| local raw | |
| raw=$("$ffmpeg_bin" -version 2>/dev/null | head -n1 | awk '{print $3}') | |
| if [[ -z "$raw" ]]; then | |
| echo "ERROR: cannot detect version from $ffmpeg_bin" >&2 | |
| return 1 | |
| fi | |
| normalize_version "$raw" | |
| # RAW_CURRENT=$(ffmpeg -version | head -n1 | awk '{print $3}') | |
| # normalize_version "$RAW_CURRENT" | |
| } | |
| # --------------------------------------------------------- | |
| # Root check | |
| # --------------------------------------------------------- | |
| if [[ $EUID -ne 0 ]]; then | |
| echo "Please run with sudo:" | |
| echo "sudo $0" | |
| exit 1 | |
| fi | |
| # --------------------------------------------------------- | |
| # Current installed version | |
| # --------------------------------------------------------- | |
| CURRENT_VERSION=$(get_version) | |
| echo "Current version: ${CURRENT_VERSION}" | |
| # only allow release version format: | |
| # 7.1 | |
| # 7.1.1 | |
| # 6.0 | |
| if [[ "$CURRENT_VERSION" != "none" ]]; then | |
| if ! [[ "$CURRENT_VERSION" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]]; then | |
| echo | |
| echo "ERROR: Unknown FFmpeg version format" | |
| echo "Detected version: ${CURRENT_VERSION}" | |
| echo | |
| echo "Refusing automatic update for safety." | |
| exit 1 | |
| fi | |
| fi | |
| # --------------------------------------------------------- | |
| # Download latest release checksum | |
| # --------------------------------------------------------- | |
| cd "$TMP_DIR" | |
| echo "Fetching latest release & info..." | |
| download "${BASE_URL}/${FILE_NAME}.md5" "${FILE_NAME}.md5" | |
| download "${BASE_URL}/${FILE_NAME}" "${FILE_NAME}" | |
| # --------------------------------------------------------- | |
| # Verify MD5 | |
| # --------------------------------------------------------- | |
| EXPECTED_MD5=$(cut -d ' ' -f1 "${FILE_NAME}.md5") | |
| ACTUAL_MD5=$(md5sum "${FILE_NAME}" | cut -d ' ' -f1) | |
| if [[ "$EXPECTED_MD5" != "$ACTUAL_MD5" ]]; then | |
| echo "ERROR: MD5 verification failed" | |
| echo "Expected: $EXPECTED_MD5" | |
| echo "Actual : $ACTUAL_MD5" | |
| exit 1 | |
| fi | |
| echo "MD5 verification passed" | |
| # --------------------------------------------------------- | |
| # Extract | |
| # --------------------------------------------------------- | |
| echo "Extracting..." | |
| tar -xf "${FILE_NAME}" | |
| EXTRACT_DIR=$(find . -maxdepth 1 -type d -name "ffmpeg-*-amd64-static" | head -n1) | |
| if [[ -z "$EXTRACT_DIR" ]]; then | |
| echo "ERROR: Cannot find extracted directory" | |
| exit 1 | |
| fi | |
| REMOTE_RAW=$("${EXTRACT_DIR}/ffmpeg" -version | head -n1 | awk '{print $3}') | |
| LATEST_VERSION=$(echo "$REMOTE_RAW" | grep -oE '[0-9]+(\.[0-9]+){1,2}' | head -n1) | |
| echo "Latest version : ${LATEST_VERSION}" | |
| if ! [[ "$LATEST_VERSION" =~ ^[0-9]+(\.[0-9]+){1,2}$ ]]; then | |
| echo | |
| echo "ERROR: Invalid remote version format" | |
| echo "Remote version: ${LATEST_VERSION}" | |
| exit 1 | |
| fi | |
| # --------------------------------------------------------- | |
| # Compare versions | |
| # --------------------------------------------------------- | |
| if [[ "$CURRENT_VERSION" == "$LATEST_VERSION" ]]; then | |
| echo | |
| echo "FFmpeg already up to date." | |
| exit 0 | |
| fi | |
| echo | |
| echo "Updating FFmpeg..." | |
| echo | |
| # --------------------------------------------------------- | |
| # Backup | |
| # --------------------------------------------------------- | |
| echo "Backing up existing binaries..." | |
| mkdir -p "$BACKUP_DIR" | |
| for bin in ffmpeg ffprobe ffplay qt-faststart; do | |
| if [[ -f "${INSTALL_DIR}/${bin}" ]]; then | |
| cp "${INSTALL_DIR}/${bin}" "${BACKUP_DIR}/" | |
| fi | |
| done | |
| # --------------------------------------------------------- | |
| # Install | |
| # --------------------------------------------------------- | |
| echo "Installing new binaries..." | |
| install -m 755 "${EXTRACT_DIR}/ffmpeg" "${INSTALL_DIR}/ffmpeg" | |
| install -m 755 "${EXTRACT_DIR}/ffprobe" "${INSTALL_DIR}/ffprobe" | |
| if [[ -f "${EXTRACT_DIR}/ffplay" ]]; then | |
| install -m 755 "${EXTRACT_DIR}/ffplay" "${INSTALL_DIR}/ffplay" | |
| fi | |
| if [[ -f "${EXTRACT_DIR}/qt-faststart" ]]; then | |
| install -m 755 "${EXTRACT_DIR}/qt-faststart" "${INSTALL_DIR}/qt-faststart" | |
| fi | |
| # --------------------------------------------------------- | |
| # Done | |
| # --------------------------------------------------------- | |
| NEW_VERSION=$(get_version) | |
| echo | |
| echo "=======================================" | |
| echo "FFmpeg update completed" | |
| echo "Old version : ${CURRENT_VERSION}" | |
| echo "New version : ${NEW_VERSION}" | |
| echo "Backup path : ${BACKUP_DIR}" | |
| echo "=======================================" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment