Skip to main content

Downloads the latest FFmpeg Win64 static build from GitHub then extracts ffmpeg.exe, ffprobe.exe, and ffplay.exe to ./bin.

# #############################################################################
# FFmpeg update script
#
# Downloads the latest FFmpeg Win64 static build from https://github.com/BtbN/FFmpeg-Builds/releases/latest
# then extracts ffmpeg.exe, ffprobe.exe, and ffplay.exe to ./bin
#
# Usage:
# PS> .\update-ffmpeg.ps1
#
# Author....: Jon LaBelle
# Date......: May 19, 2021
# Snippet...: https://jonlabelle.com/snippets/view/powershell/download-latest-ffmpeg-static-build
# #############################################################################

$SCRIPTDIR = Split-Path $script:MyInvocation.MyCommand.Path

# disables showing progress indicator (https://stackoverflow.com/a/18770794)
$ProgressPreference = 'SilentlyContinue'

function DownloadWithRetry([string] $Url, [string] $DownloadLocation, [int] $Retries)
{
    while ($true)
    {
        try
        {
            Invoke-WebRequest $Url -OutFile $DownloadLocation
            break
        }
        catch
        {
            $exceptionMessage = $_.Exception.Message
            Write-Output "Failed to download '$Url': $exceptionMessage"
            if ($Retries -gt 0)
            {
                $Retries--
                Write-Output "Waiting 10 seconds before retrying. Retries left: $Retries"
                Start-Sleep -Seconds 10
            }
            else
            {
                $exception = $_.Exception
                throw $exception
            }
        }
    }
}

function ExtractFromArchive([string] $Path, [string] $DestinationPath, [string] $Filter)
{
    # ensure the output folder exists
    $exists = Test-Path -Path $DestinationPath
    if ($exists -eq $false)
    {
        $null = New-Item -Path $DestinationPath -ItemType Directory -Force
        Write-Output "Created output directory: $DestinationPath"
    }

    # load ZIP methods
    Add-Type -AssemblyName System.IO.Compression.FileSystem

    # open ZIP archive for reading
    $zip = [System.IO.Compression.ZipFile]::OpenRead($Path)

    # find all files in ZIP that match the filter (i.e. file extension)
    $zip.Entries | Where-Object { $_.FullName -like $Filter } | ForEach-Object {
        # extract the selected items from the ZIP archive and copy them to the out folder
        $fileName = $_.Name
        $outFile = Join-Path -Path $DestinationPath -ChildPath $fileName
        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $outFile, $true)
        Write-Output "Extracted $fileName to $outFile"
    }

    # close ZIP file
    $zip.Dispose()
}

function GetLatestRelease([string] $DownloadLocation)
{
    $owner = "BtbN"
    $repo = "FFmpeg-Builds"
    $releasesApiUrl = "https://api.github.com/repos/$owner/$repo/releases/latest"
    $reWindows64Gpl = "ffmpeg\-N\-[0-9]+\-[A-Z-a-z0-9]+\-win64\-gpl\.zip" # ex: "ffmpeg-N-102557-g11b489d592-win64-gpl.zip"

    $response = Invoke-RestMethod -Method Get -Uri $releasesApiUrl

    $downloadUrls = $response.assets.browser_download_url
    $downloadUrl = $downloadUrls | Where-Object { $_ -match $reWindows64Gpl } | Select-Object -First 1

    if ($downloadUrl.length -eq 0)
    {
        Write-Error "Latest release not found."
        exit 1
    }

    Write-Output "Downloading latest release from '$downloadUrl' to '$DownloadLocation' ..."
    DownloadWithRetry -Url $downloadUrl -DownloadLocation $DownloadLocation -Retries 5
}

function main()
{
    # Download latest release
    $downloadLocation = Join-Path -Path $SCRIPTDIR -ChildPath "latest.zip"
    GetLatestRelease -DownloadLocation $downloadLocation

    # Extract *.exe files to ./bin directory
    $outputDirectory = Join-Path -Path $SCRIPTDIR -ChildPath "bin"
    ExtractFromArchive -Path $downloadLocation -DestinationPath $outputDirectory -Filter "*.exe"
}

main