Skip to content

Instantly share code, notes, and snippets.

@emilwojcik93
Last active September 15, 2025 20:25
Show Gist options
  • Save emilwojcik93/868fbc801667e65e334679fec62b6879 to your computer and use it in GitHub Desktop.
Save emilwojcik93/868fbc801667e65e334679fec62b6879 to your computer and use it in GitHub Desktop.

Revisions

  1. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 528 additions and 0 deletions.
    528 changes: 528 additions & 0 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,528 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }
  2. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 537 deletions.
    537 changes: 0 additions & 537 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -1,537 +0,0 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  3. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 26 additions and 24 deletions.
    50 changes: 26 additions & 24 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -1,20 +1,24 @@
    # VideoCompressor - Quick Start Guide

    **🎬 GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879
    **GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879

    ## 🚀 TLDR - One-Line Install to PowerShell Profile
    ## TLDR - Two Simple Commands

    **Copy, paste, and run this single command in PowerShell:**
    **Step 1: Download script to profile directory**
    ```powershell
    $profileDir = Split-Path $PROFILE -Parent; if(!(Test-Path $profileDir)){md $profileDir -Force}; iwr 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1' -OutFile "$profileDir\VideoCompressor.ps1" -UseBasicParsing
    ```

    **Step 2: Add to PowerShell profile**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& { $d = Split-Path $PROFILE -Parent; if (!(Test-Path $d)) { md $d -Force }; $u = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'; $f = Join-Path $d 'VideoCompressor.ps1'; iwr $u -OutFile $f -UseBasicParsing; if (!(Select-String 'VideoCompressor.ps1' $PROFILE -Quiet 2>$null)) { Add-Content $PROFILE \"`n. '$f'\" -Encoding UTF8 }; Write-Host '✅ VideoCompressor installed! Restart PowerShell and use: Compress-Video `"video.mp4`\"' -ForegroundColor Green }"
    if(!(Select-String -Path $PROFILE -Pattern 'VideoCompressor.ps1' -Quiet -ErrorAction SilentlyContinue)){Add-Content -Path $PROFILE -Value "`n. '$((Split-Path $PROFILE -Parent))\VideoCompressor.ps1'" -Encoding UTF8}
    ```

    **That's it!** Restart PowerShell and use: `Compress-Video "video.mp4"`
    **Step 3: Restart PowerShell** and use: `Compress-Video "video.mp4"`

    ---

    ## 📋 Detailed Installation Options
    ## Detailed Installation Options

    ### Option 1: Install to PowerShell Profile (Recommended)

    @@ -34,8 +38,6 @@ powershell.exe -ExecutionPolicy Bypass -Command "& {
    }"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Run directly from gist without installing:**
    @@ -58,7 +60,7 @@ powershell.exe -ExecutionPolicy Bypass -Command "
    "
    ```

    ## Quick Examples
    ## Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    @@ -74,39 +76,39 @@ Compress-Video "MyVideo.mp4" -CompressionMode Quality
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does
    ## What It Does

    - **Auto-installs FFmpeg** if not found
    - **Hardware acceleration** (Intel/NVIDIA)
    - **Reduces file size by 60-80%**
    - **Maintains visual quality**
    - **Optimized for cloud storage**
    - **Works with all video formats**
    - Auto-installs FFmpeg if not found
    - Hardware acceleration (Intel/NVIDIA)
    - Reduces file size by 60-80%
    - Maintains visual quality
    - Optimized for cloud storage
    - Works with all video formats

    ## 📋 Requirements
    ## Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ Profile Installation Details
    ## Profile Installation Details

    The one-liner installs VideoCompressor to your PowerShell profile by:
    The two-step installation:

    1. **Script Location**: `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    2. **Profile Entry**: Adds `. 'path\to\VideoCompressor.ps1'` to your `$PROFILE`
    3. **Auto-Loading**: Script loads automatically when PowerShell starts
    1. **Downloads script** to `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    2. **Adds profile entry** `. 'path\to\VideoCompressor.ps1'` to your `$PROFILE`
    3. **Auto-loads** when PowerShell starts

    **Profile Path Examples:**
    - `C:\Users\YourName\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`
    - `C:\Users\YourName\Documents\PowerShell\Microsoft.PowerShell_profile.ps1` (PowerShell 7+)

    ## 🔧 For Developers
    ## For Developers

    1. **Clone this gist**: `git clone https://gist.github.com/868fbc801667e65e334679fec62b6879.git`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform!
  4. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 135 deletions.
    135 changes: 0 additions & 135 deletions GIST_COMMENT.md
    Original file line number Diff line number Diff line change
    @@ -1,135 +0,0 @@
    # VideoCompressor - Installation & Usage Guide

    🎬 **GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879

    ## 🚀 TLDR - One-Line Install

    **Copy and paste this command in PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& { $d = Split-Path $PROFILE -Parent; if (!(Test-Path $d)) { md $d -Force }; $u = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'; $f = Join-Path $d 'VideoCompressor.ps1'; iwr $u -OutFile $f -UseBasicParsing; if (!(Select-String 'VideoCompressor.ps1' $PROFILE -Quiet 2>$null)) { Add-Content $PROFILE \"`n. '$f'\" -Encoding UTF8 }; Write-Host '✅ VideoCompressor installed! Restart PowerShell and use: Compress-Video `"video.mp4`\"' -ForegroundColor Green }"
    ```

    **Then restart PowerShell and use:** `Compress-Video "video.mp4"`

    ---

    ## 📋 Detailed Installation Options

    ### Option 1: Install to PowerShell Profile (Recommended)
    **One-time setup for permanent availability:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path $PROFILE -Parent
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    $scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    $localPath = Join-Path $profileDir 'VideoCompressor.ps1'
    try {
    Invoke-WebRequest -Uri $scriptUrl -OutFile $localPath -UseBasicParsing
    Write-Host 'VideoCompressor downloaded successfully!' -ForegroundColor Green
    $profileContent = if (Test-Path $PROFILE) { Get-Content $PROFILE -Raw } else { '' }
    $dotSourceLine = '. `"$localPath`"'
    if ($profileContent -notlike "*VideoCompressor.ps1*") {
    Add-Content -Path $PROFILE -Value "`n# VideoCompressor Auto-Load`n$dotSourceLine" -Encoding UTF8
    Write-Host 'Added to PowerShell profile. Restart PowerShell or run: . $PROFILE' -ForegroundColor Yellow
    } else {
    Write-Host 'VideoCompressor already in profile!' -ForegroundColor Yellow
    }
    Write-Host 'Installation complete! Usage: Compress-Video `"video.mp4`"' -ForegroundColor Green
    }
    catch {
    Write-Error "Failed to download: $_"
    }
    }"
    ```

    After installation, just use: `Compress-Video "your-video.mp4"`

    ### Option 2: Run Directly from Gist (No Installation)

    **Basic usage:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'video.mp4'"
    ```

    **With compression mode:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast"
    ```

    **Advanced with parameters:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## 🎯 Compression Modes

    | Mode | Speed | Quality | Best For |
    |------|-------|---------|----------|
    | `Fast` | ⚡⚡⚡ | ⭐⭐⭐ | Large batches, quick processing |
    | `Balanced` | ⚡⚡ | ⭐⭐⭐⭐ | **Default** - Best balance |
    | `Quality` || ⭐⭐⭐⭐⭐ | Archival, best compression |

    ## ⚡ Key Features

    - **Auto FFmpeg Install**: Installs FFmpeg via winget if missing
    - **Hardware Acceleration**: Intel QuickSync, NVIDIA NVENC support
    - **Cloud Optimized**: Perfect for OneDrive, YouTube, SharePoint
    - **Smart Scaling**: Auto-scales >1080p videos maintaining aspect ratio
    - **Batch Processing**: Easy to process multiple files

    ## 💡 Quick Examples

    ```powershell
    # Default balanced compression
    Compress-Video "MyVideo.mp4"
    # Fast mode for quick processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality compression
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output location
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\MyCompressed"
    # Batch process all MP4 files
    Get-ChildItem "*.mp4" | ForEach-Object { Compress-Video $_.Name }
    ```

    ## 🛠️ Requirements

    - Windows 10/11 + PowerShell 5.1+
    - FFmpeg (auto-installed if missing)
    - Internet connection (for FFmpeg download)

    ## 📊 Typical Results

    - **File size reduction**: 60-80% smaller
    - **Quality**: Visually lossless for most content
    - **Compatibility**: Works with all major platforms
    - **Speed**: Hardware acceleration when available

    Perfect for sharing large recordings, uploading to cloud storage, or preparing videos for web streaming! 🎬

    ## 🔧 How the One-Liner Works

    The TLDR command installs VideoCompressor by:
    1. **Creates profile directory** if it doesn't exist
    2. **Downloads script** to `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    3. **Updates $PROFILE** to auto-load the script (if not already present)
    4. **Ready to use** after restarting PowerShell
  5. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 537 additions and 0 deletions.
    537 changes: 537 additions & 0 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,537 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  6. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 537 deletions.
    537 changes: 0 additions & 537 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -1,537 +0,0 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  7. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 537 additions and 0 deletions.
    537 changes: 537 additions & 0 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,537 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  8. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 537 deletions.
    537 changes: 0 additions & 537 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -1,537 +0,0 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  9. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 537 additions and 0 deletions.
    537 changes: 537 additions & 0 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,537 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  10. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 537 deletions.
    537 changes: 0 additions & 537 deletions VideoCompressor.ps1
    Original file line number Diff line number Diff line change
    @@ -1,537 +0,0 @@
    # VideoCompressor - PowerShell Video Compression Tool
    # Optimized for cloud storage platforms (OneDrive, YouTube, SharePoint, etc.)

    function Test-FFmpegInstallation {
    <#
    .SYNOPSIS
    Checks if FFmpeg is available and installs it if not found
    #>

    # Check if ffmpeg is available in PATH
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    # If not found in PATH, check winget packages directory
    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg not found in PATH. Checking winget packages..." -ForegroundColor Yellow
    $wingetPath = "$env:LOCALAPPDATA\Microsoft\WinGet\Packages"

    if (Test-Path $wingetPath) {
    # Look for FFmpeg packages
    $ffmpegDirs = @(
    "Gyan.FFmpeg_Microsoft.Winget.Source_8wekyb3d8bbwe",
    "BtbN.FFmpeg*",
    "*FFmpeg*"
    )

    foreach ($pattern in $ffmpegDirs) {
    $packageDirs = Get-ChildItem -Path $wingetPath -Directory -Name $pattern -ErrorAction SilentlyContinue

    foreach ($packageDir in $packageDirs) {
    $fullPackagePath = Join-Path $wingetPath $packageDir

    # Look for bin directories recursively
    $binDirs = Get-ChildItem -Path $fullPackagePath -Recurse -Directory -Name "bin" -ErrorAction SilentlyContinue

    foreach ($binDir in $binDirs) {
    $binPath = Join-Path $fullPackagePath $binDir
    $ffmpegExe = Join-Path $binPath "ffmpeg.exe"
    $ffprobeExe = Join-Path $binPath "ffprobe.exe"

    if ((Test-Path $ffmpegExe) -and (Test-Path $ffprobeExe)) {
    Write-Host "Found FFmpeg installation at: $binPath" -ForegroundColor Green

    # Add to current session PATH
    if ($env:PATH -notlike "*$binPath*") {
    $env:PATH += ";$binPath"
    Write-Host "Added to current session PATH: $binPath" -ForegroundColor Green
    }

    # Ask user if they want to add to permanent PATH
    $addToPermanentPath = Read-Host "Add FFmpeg to permanent system PATH? (y/n)"
    if ($addToPermanentPath -eq 'y' -or $addToPermanentPath -eq 'Y') {
    try {
    # Get current user PATH
    $userPath = [System.Environment]::GetEnvironmentVariable("PATH", "User")
    if ($userPath -notlike "*$binPath*") {
    $newUserPath = "$userPath;$binPath"
    [System.Environment]::SetEnvironmentVariable("PATH", $newUserPath, "User")
    Write-Host "Added to permanent user PATH. Restart PowerShell for changes to take effect." -ForegroundColor Green
    } else {
    Write-Host "FFmpeg is already in permanent PATH." -ForegroundColor Yellow
    }
    } catch {
    Write-Host "Failed to update permanent PATH: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Verify the commands are now available
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    $ffprobeAvailable = Get-Command ffprobe -ErrorAction SilentlyContinue

    if ($ffmpegAvailable -and $ffprobeAvailable) {
    Write-Host "FFmpeg is now available!" -ForegroundColor Green
    return $true
    }
    }
    }
    }
    }
    }
    }

    if (-not $ffmpegAvailable -or -not $ffprobeAvailable) {
    Write-Host "FFmpeg is not recognized as an internal or external command." -ForegroundColor Red
    Write-Host "FFmpeg is required for video compression." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Available installation options via winget:" -ForegroundColor Cyan
    Write-Host "1. Gyan.FFmpeg (MSVC build - Best Windows compatibility)" -ForegroundColor Green
    Write-Host "2. BtbN.FFmpeg.GPL (MinGW build - Latest features)" -ForegroundColor Yellow
    Write-Host ""

    $install = Read-Host "Do you want to install FFmpeg now? (y/n)"

    if ($install -eq 'y' -or $install -eq 'Y') {
    Write-Host "Installing FFmpeg (Gyan.FFmpeg)..." -ForegroundColor Yellow

    try {
    winget install Gyan.FFmpeg --accept-package-agreements --accept-source-agreements

    # Refresh PATH in current session
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")

    # Verify installation
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue

    if (-not $ffmpegAvailable) {
    Write-Host "Primary installation failed. Trying alternative package (BtbN.FFmpeg.GPL)..." -ForegroundColor Yellow
    winget install BtbN.FFmpeg.GPL --accept-package-agreements --accept-source-agreements

    # Refresh PATH again
    $env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
    Start-Sleep -Seconds 3
    $ffmpegAvailable = Get-Command ffmpeg -ErrorAction SilentlyContinue
    }

    if ($ffmpegAvailable) {
    Write-Host "FFmpeg installed successfully!" -ForegroundColor Green
    Write-Host "You may need to restart PowerShell for PATH changes to take effect." -ForegroundColor Yellow
    return $true
    }
    else {
    Write-Host "FFmpeg installation failed. Please install manually or restart PowerShell." -ForegroundColor Red
    Write-Host "Manual installation: Download from https://ffmpeg.org/download.html" -ForegroundColor Yellow
    return $false
    }
    }
    catch {
    Write-Host "Error installing FFmpeg: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host "Please try installing manually:" -ForegroundColor Yellow
    Write-Host " winget install Gyan.FFmpeg" -ForegroundColor Cyan
    Write-Host " or: winget install BtbN.FFmpeg.GPL" -ForegroundColor Cyan
    return $false
    }
    }
    else {
    Write-Host "FFmpeg installation cancelled. Cannot proceed without FFmpeg." -ForegroundColor Red
    return $false
    }
    }

    return $true
    }

    function Get-VideoProperties {
    <#
    .SYNOPSIS
    Uses ffprobe to analyze video properties for optimal compression
    #>
    param(
    [Parameter(Mandatory = $true)]
    [string]$VideoPath
    )

    try {
    # Get video information using ffprobe
    $probeOutput = ffprobe -v quiet -print_format json -show_streams -show_format "$VideoPath" | ConvertFrom-Json

    $videoStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "video" } | Select-Object -First 1
    $audioStream = $probeOutput.streams | Where-Object { $_.codec_type -eq "audio" } | Select-Object -First 1

    $properties = @{
    Duration = [math]::Round([double]$probeOutput.format.duration, 2)
    Width = [int]$videoStream.width
    Height = [int]$videoStream.height
    FrameRate = if ($videoStream.r_frame_rate) {
    $fps = $videoStream.r_frame_rate.Split('/')
    [math]::Round([double]$fps[0] / [double]$fps[1], 2)
    } else { 30 }
    VideoBitrate = if ($videoStream.bit_rate) { [int]$videoStream.bit_rate } else { 0 }
    AudioBitrate = if ($audioStream.bit_rate) { [int]$audioStream.bit_rate } else { 128000 }
    VideoCodec = $videoStream.codec_name
    AudioCodec = if ($audioStream) { $audioStream.codec_name } else { "none" }
    FileSize = [math]::Round((Get-Item $VideoPath).Length / 1MB, 2)
    }

    return $properties
    }
    catch {
    Write-Host "Error analyzing video: $($_.Exception.Message)" -ForegroundColor Red
    return $null
    }
    }

    function Get-OptimalCompressionSettings {
    <#
    .SYNOPSIS
    Determines optimal compression settings based on video properties and target platform
    #>
    param(
    [Parameter(Mandatory = $true)]
    [hashtable]$VideoProperties,

    [Parameter(Mandatory = $false)]
    [string]$CompressionMode = "Balanced"
    )

    $width = $VideoProperties.Width
    $height = $VideoProperties.Height
    $fps = $VideoProperties.FrameRate
    $duration = $VideoProperties.Duration

    # Determine target resolution (maintain aspect ratio)
    $targetWidth = $width
    $targetHeight = $height

    # Optimize for cloud storage - limit resolution for better streaming
    if ($width -gt 1920 -or $height -gt 1080) {
    # Scale down to 1080p max while maintaining aspect ratio
    $aspectRatio = $width / $height
    if ($aspectRatio -gt (16/9)) {
    $targetWidth = 1920
    $targetHeight = [math]::Round(1920 / $aspectRatio / 2) * 2 # Even number
    } else {
    $targetHeight = 1080
    $targetWidth = [math]::Round(1080 * $aspectRatio / 2) * 2 # Even number
    }
    }

    # Optimize frame rate
    $targetFps = $fps
    if ($fps -gt 60) { $targetFps = 60 }
    elseif ($fps -gt 30 -and $duration -gt 600) { $targetFps = 30 } # Long videos benefit from 30fps

    # Determine CRF based on content type and duration
    $crf = 23 # Default balanced quality
    if ($duration -lt 300) { $crf = 21 } # Short videos - higher quality
    elseif ($duration -gt 3600) { $crf = 25 } # Long videos - more compression

    # Audio bitrate optimization
    $audioBitrate = "128k"
    if ($VideoProperties.AudioBitrate -gt 0) {
    if ($VideoProperties.AudioBitrate -le 96000) { $audioBitrate = "96k" }
    elseif ($VideoProperties.AudioBitrate -ge 256000) { $audioBitrate = "192k" }
    }

    # Choose preset and CRF based on compression mode
    switch ($CompressionMode) {
    "Fast" {
    $preset = "fast"
    $crf = $crf + 2 # Slightly higher CRF for faster encoding
    }
    "Quality" {
    $preset = "slow"
    $crf = $crf - 1 # Lower CRF for better quality
    }
    default { # "Balanced"
    $preset = "medium"
    # Keep calculated CRF as is
    }
    }

    # Try to detect hardware acceleration capabilities (only if ffmpeg is available)
    $hwEncoder = $null
    $hwAvailable = $false

    if (Get-Command ffmpeg -ErrorAction SilentlyContinue) {
    # Check for Intel QuickSync (common on Intel CPUs like Core Ultra)
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_qsv -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_qsv"
    $hwAvailable = $true
    }
    } catch { }

    # Check for NVIDIA NVENC if QuickSync not available
    if (-not $hwAvailable) {
    try {
    $null = ffmpeg -hide_banner -f lavfi -i testsrc2=duration=1:size=320x240:rate=1 -c:v h264_nvenc -f null - 2>$null
    if ($LASTEXITCODE -eq 0) {
    $hwEncoder = "h264_nvenc"
    $hwAvailable = $true
    }
    } catch { }
    }
    }

    $settings = @{
    Resolution = "${targetWidth}x${targetHeight}"
    FrameRate = $targetFps
    CRF = $crf
    AudioBitrate = $audioBitrate
    Preset = $preset
    Profile = "high"
    Level = "4.0"
    HardwareEncoder = $hwEncoder
    HardwareAvailable = $hwAvailable
    }

    return $settings
    }

    function Get-OutputDirectory {
    <#
    .SYNOPSIS
    Determines the best output directory (OneDrive or Videos folder)
    #>

    # Check if OneDrive is available and service is running
    $oneDriveAvailable = $false

    if ($env:OneDrive -and (Test-Path $env:OneDrive)) {
    # Check if OneDrive service is running
    $oneDriveService = Get-Process -Name "OneDrive" -ErrorAction SilentlyContinue
    if ($oneDriveService) {
    $oneDriveAvailable = $true
    $outputDir = Join-Path $env:OneDrive "CompressedVideos"
    }
    }

    if (-not $oneDriveAvailable) {
    # Fallback to Videos directory using .NET method
    try {
    $videosPath = [System.Environment]::GetFolderPath('MyVideos')
    $outputDir = Join-Path $videosPath "CompressedVideos"
    }
    catch {
    # Final fallback to user profile
    $outputDir = Join-Path $env:USERPROFILE "Videos\CompressedVideos"
    }
    }

    # Create directory if it doesn't exist
    if (-not (Test-Path $outputDir)) {
    New-Item -ItemType Directory -Path $outputDir -Force | Out-Null
    Write-Host "Created output directory: $outputDir" -ForegroundColor Green
    }

    return $outputDir
    }

    function Compress-Video {
    <#
    .SYNOPSIS
    Compresses video files with optimal settings for cloud storage platforms
    .DESCRIPTION
    This function compresses video files using FFmpeg with settings optimized for
    cloud storage platforms like OneDrive, YouTube, SharePoint, and MS Stream.
    .PARAMETER SourceVideo
    Path to the source video file to compress
    .PARAMETER OutputDirectory
    Optional custom output directory. If not specified, uses OneDrive or Videos folder
    .PARAMETER CompressionMode
    Choose compression speed/quality balance: Fast, Balanced, Quality
    - Fast: Quick processing, good quality (faster than real-time)
    - Balanced: Good balance of speed and compression (default)
    - Quality: Best compression, slower processing
    .EXAMPLE
    Compress-Video "C:\Videos\MyVideo.mp4"
    .EXAMPLE
    Compress-Video -SourceVideo ".\Local Development Environment Setup Guide.mp4" -CompressionMode Fast
    #>

    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true, Position = 0)]
    [string]$SourceVideo,

    [Parameter(Mandatory = $false)]
    [string]$OutputDirectory,

    [Parameter(Mandatory = $false)]
    [ValidateSet("Fast", "Balanced", "Quality")]
    [string]$CompressionMode = "Balanced"
    )

    # Check if FFmpeg is available
    if (-not (Test-FFmpegInstallation)) {
    return
    }

    # Validate source file
    if (-not (Test-Path $SourceVideo)) {
    Write-Host "Error: Source video file not found: $SourceVideo" -ForegroundColor Red
    return
    }

    Write-Host "Analyzing video properties..." -ForegroundColor Yellow
    $videoProps = Get-VideoProperties -VideoPath $SourceVideo

    if (-not $videoProps) {
    Write-Host "Failed to analyze video properties." -ForegroundColor Red
    return
    }

    # Display video information
    Write-Host "`nVideo Information:" -ForegroundColor Cyan
    Write-Host "Duration: $($videoProps.Duration) seconds"
    Write-Host "Resolution: $($videoProps.Width)x$($videoProps.Height)"
    Write-Host "Frame Rate: $($videoProps.FrameRate) fps"
    Write-Host "File Size: $($videoProps.FileSize) MB"
    Write-Host "Video Codec: $($videoProps.VideoCodec)"
    Write-Host "Audio Codec: $($videoProps.AudioCodec)"

    # Get optimal compression settings
    $compressionSettings = Get-OptimalCompressionSettings -VideoProperties $videoProps -CompressionMode $CompressionMode

    Write-Host "`nOptimal Compression Settings:" -ForegroundColor Cyan
    Write-Host "Compression Mode: $CompressionMode"
    Write-Host "Target Resolution: $($compressionSettings.Resolution)"
    Write-Host "Target Frame Rate: $($compressionSettings.FrameRate) fps"
    Write-Host "CRF Quality: $($compressionSettings.CRF)"
    Write-Host "Audio Bitrate: $($compressionSettings.AudioBitrate)"
    Write-Host "Preset: $($compressionSettings.Preset)"

    if ($compressionSettings.HardwareAvailable) {
    Write-Host "Hardware Encoder: $($compressionSettings.HardwareEncoder) (faster processing)" -ForegroundColor Green
    } else {
    Write-Host "Hardware Encoder: Software only (h264)" -ForegroundColor Yellow
    }

    # Determine output directory and file path
    if (-not $OutputDirectory) {
    $OutputDirectory = Get-OutputDirectory
    }

    $sourceFileName = [System.IO.Path]::GetFileNameWithoutExtension($SourceVideo)
    $outputFileName = "${sourceFileName}_compressed.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName

    # Check if output file already exists
    if (Test-Path $outputPath) {
    $counter = 1
    do {
    $outputFileName = "${sourceFileName}_compressed_${counter}.mp4"
    $outputPath = Join-Path $OutputDirectory $outputFileName
    $counter++
    } while (Test-Path $outputPath)
    }

    Write-Host "`nStarting compression..." -ForegroundColor Green
    Write-Host "Output will be saved to: $outputPath" -ForegroundColor Yellow

    # Build FFmpeg command with optimal settings
    if ($compressionSettings.HardwareAvailable -and $CompressionMode -ne "Quality") {
    # Use hardware encoder for faster processing (except in Quality mode)
    $videoCodec = $compressionSettings.HardwareEncoder

    # Calculate target bitrate based on resolution and frame rate
    $pixelCount = ($compressionSettings.Resolution -split 'x')[0] * ($compressionSettings.Resolution -split 'x')[1]
    $targetBitrate = [math]::Round(($pixelCount * $compressionSettings.FrameRate * 0.1) / 1000) # Rough estimate
    if ($targetBitrate -lt 1000) { $targetBitrate = 1000 }
    if ($targetBitrate -gt 8000) { $targetBitrate = 8000 }

    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", $videoCodec
    "-b:v", "${targetBitrate}k"
    "-maxrate", "$([math]::Round($targetBitrate * 1.5))k"
    "-bufsize", "$([math]::Round($targetBitrate * 2))k"
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    } else {
    # Use software encoder with CRF
    $ffmpegArgs = @(
    "-i", "`"$SourceVideo`""
    "-c:v", "libx264"
    "-preset", $compressionSettings.Preset
    "-crf", $compressionSettings.CRF
    "-profile:v", $compressionSettings.Profile
    "-level", $compressionSettings.Level
    "-vf", "scale=$($compressionSettings.Resolution)"
    "-r", $compressionSettings.FrameRate
    "-c:a", "aac"
    "-b:a", $compressionSettings.AudioBitrate
    "-movflags", "+faststart"
    "-avoid_negative_ts", "make_zero"
    "-fflags", "+genpts"
    "-y"
    "`"$outputPath`""
    )
    }

    try {
    $startTime = Get-Date
    Write-Host "Executing: ffmpeg $($ffmpegArgs -join ' ')" -ForegroundColor Gray

    # Execute FFmpeg command
    & ffmpeg @ffmpegArgs

    $endTime = Get-Date
    $processingTime = ($endTime - $startTime).TotalSeconds

    if (Test-Path $outputPath) {
    $outputFileSize = [math]::Round((Get-Item $outputPath).Length / 1MB, 2)
    $compressionRatio = [math]::Round((1 - ($outputFileSize / $videoProps.FileSize)) * 100, 1)

    Write-Host "`nCompression completed successfully!" -ForegroundColor Green
    Write-Host "Processing time: $([math]::Round($processingTime, 1)) seconds"
    Write-Host "Original size: $($videoProps.FileSize) MB"
    Write-Host "Compressed size: $outputFileSize MB"
    Write-Host "Compression ratio: $compressionRatio%"
    Write-Host "Output location: $outputPath"

    # Open output directory
    $openDir = Read-Host "`nOpen output directory? (y/n)"
    if ($openDir -eq 'y' -or $openDir -eq 'Y') {
    Start-Process "explorer.exe" -ArgumentList $OutputDirectory
    }
    }
    else {
    Write-Host "Compression failed - output file not found." -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error during compression: $($_.Exception.Message)" -ForegroundColor Red
    }
    }

    # Export the main function (only when loaded as a module)
    if ($MyInvocation.MyCommand.Path -and $MyInvocation.MyCommand.Path.EndsWith('.psm1')) {
    Export-ModuleMember -Function Compress-Video
    }

    # Display information when script is loaded
    Write-Host "`nVideoCompressor loaded successfully!" -ForegroundColor Green
    Write-Host "Usage Examples:" -ForegroundColor Yellow
    Write-Host " Compress-Video `"video.mp4`" # Balanced mode (default)" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Fast # Fast processing" -ForegroundColor White
    Write-Host " Compress-Video `"video.mp4`" -CompressionMode Quality # Best quality" -ForegroundColor White
    Write-Host "Optimized for: OneDrive, YouTube, SharePoint, MS Stream" -ForegroundColor Cyan
    Write-Host "Hardware acceleration: Auto-detected (Intel QuickSync, NVIDIA NVENC)" -ForegroundColor Green
  11. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 135 additions and 0 deletions.
    135 changes: 135 additions & 0 deletions GIST_COMMENT.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    # VideoCompressor - Installation & Usage Guide

    🎬 **GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879

    ## 🚀 TLDR - One-Line Install

    **Copy and paste this command in PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& { $d = Split-Path $PROFILE -Parent; if (!(Test-Path $d)) { md $d -Force }; $u = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'; $f = Join-Path $d 'VideoCompressor.ps1'; iwr $u -OutFile $f -UseBasicParsing; if (!(Select-String 'VideoCompressor.ps1' $PROFILE -Quiet 2>$null)) { Add-Content $PROFILE \"`n. '$f'\" -Encoding UTF8 }; Write-Host '✅ VideoCompressor installed! Restart PowerShell and use: Compress-Video `"video.mp4`\"' -ForegroundColor Green }"
    ```

    **Then restart PowerShell and use:** `Compress-Video "video.mp4"`

    ---

    ## 📋 Detailed Installation Options

    ### Option 1: Install to PowerShell Profile (Recommended)
    **One-time setup for permanent availability:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path $PROFILE -Parent
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    $scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    $localPath = Join-Path $profileDir 'VideoCompressor.ps1'
    try {
    Invoke-WebRequest -Uri $scriptUrl -OutFile $localPath -UseBasicParsing
    Write-Host 'VideoCompressor downloaded successfully!' -ForegroundColor Green
    $profileContent = if (Test-Path $PROFILE) { Get-Content $PROFILE -Raw } else { '' }
    $dotSourceLine = '. `"$localPath`"'
    if ($profileContent -notlike "*VideoCompressor.ps1*") {
    Add-Content -Path $PROFILE -Value "`n# VideoCompressor Auto-Load`n$dotSourceLine" -Encoding UTF8
    Write-Host 'Added to PowerShell profile. Restart PowerShell or run: . $PROFILE' -ForegroundColor Yellow
    } else {
    Write-Host 'VideoCompressor already in profile!' -ForegroundColor Yellow
    }
    Write-Host 'Installation complete! Usage: Compress-Video `"video.mp4`"' -ForegroundColor Green
    }
    catch {
    Write-Error "Failed to download: $_"
    }
    }"
    ```

    After installation, just use: `Compress-Video "your-video.mp4"`

    ### Option 2: Run Directly from Gist (No Installation)

    **Basic usage:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'video.mp4'"
    ```

    **With compression mode:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast"
    ```

    **Advanced with parameters:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## 🎯 Compression Modes

    | Mode | Speed | Quality | Best For |
    |------|-------|---------|----------|
    | `Fast` | ⚡⚡⚡ | ⭐⭐⭐ | Large batches, quick processing |
    | `Balanced` | ⚡⚡ | ⭐⭐⭐⭐ | **Default** - Best balance |
    | `Quality` || ⭐⭐⭐⭐⭐ | Archival, best compression |

    ## ⚡ Key Features

    - **Auto FFmpeg Install**: Installs FFmpeg via winget if missing
    - **Hardware Acceleration**: Intel QuickSync, NVIDIA NVENC support
    - **Cloud Optimized**: Perfect for OneDrive, YouTube, SharePoint
    - **Smart Scaling**: Auto-scales >1080p videos maintaining aspect ratio
    - **Batch Processing**: Easy to process multiple files

    ## 💡 Quick Examples

    ```powershell
    # Default balanced compression
    Compress-Video "MyVideo.mp4"
    # Fast mode for quick processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality compression
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output location
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\MyCompressed"
    # Batch process all MP4 files
    Get-ChildItem "*.mp4" | ForEach-Object { Compress-Video $_.Name }
    ```

    ## 🛠️ Requirements

    - Windows 10/11 + PowerShell 5.1+
    - FFmpeg (auto-installed if missing)
    - Internet connection (for FFmpeg download)

    ## 📊 Typical Results

    - **File size reduction**: 60-80% smaller
    - **Quality**: Visually lossless for most content
    - **Compatibility**: Works with all major platforms
    - **Speed**: Hardware acceleration when available

    Perfect for sharing large recordings, uploading to cloud storage, or preparing videos for web streaming! 🎬

    ## 🔧 How the One-Liner Works

    The TLDR command installs VideoCompressor by:
    1. **Creates profile directory** if it doesn't exist
    2. **Downloads script** to `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    3. **Updates $PROFILE** to auto-load the script (if not already present)
    4. **Ready to use** after restarting PowerShell
  12. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 135 deletions.
    135 changes: 0 additions & 135 deletions GIST_COMMENT.md
    Original file line number Diff line number Diff line change
    @@ -1,135 +0,0 @@
    # VideoCompressor - Installation & Usage Guide

    🎬 **GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879

    ## 🚀 TLDR - One-Line Install

    **Copy and paste this command in PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& { $d = Split-Path $PROFILE -Parent; if (!(Test-Path $d)) { md $d -Force }; $u = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'; $f = Join-Path $d 'VideoCompressor.ps1'; iwr $u -OutFile $f -UseBasicParsing; if (!(Select-String 'VideoCompressor.ps1' $PROFILE -Quiet 2>$null)) { Add-Content $PROFILE \"`n. '$f'\" -Encoding UTF8 }; Write-Host '✅ VideoCompressor installed! Restart PowerShell and use: Compress-Video `"video.mp4`\"' -ForegroundColor Green }"
    ```

    **Then restart PowerShell and use:** `Compress-Video "video.mp4"`

    ---

    ## 📋 Detailed Installation Options

    ### Option 1: Install to PowerShell Profile (Recommended)
    **One-time setup for permanent availability:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path $PROFILE -Parent
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    $scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    $localPath = Join-Path $profileDir 'VideoCompressor.ps1'
    try {
    Invoke-WebRequest -Uri $scriptUrl -OutFile $localPath -UseBasicParsing
    Write-Host 'VideoCompressor downloaded successfully!' -ForegroundColor Green
    $profileContent = if (Test-Path $PROFILE) { Get-Content $PROFILE -Raw } else { '' }
    $dotSourceLine = '. `"$localPath`"'
    if ($profileContent -notlike "*VideoCompressor.ps1*") {
    Add-Content -Path $PROFILE -Value "`n# VideoCompressor Auto-Load`n$dotSourceLine" -Encoding UTF8
    Write-Host 'Added to PowerShell profile. Restart PowerShell or run: . $PROFILE' -ForegroundColor Yellow
    } else {
    Write-Host 'VideoCompressor already in profile!' -ForegroundColor Yellow
    }
    Write-Host 'Installation complete! Usage: Compress-Video `"video.mp4`"' -ForegroundColor Green
    }
    catch {
    Write-Error "Failed to download: $_"
    }
    }"
    ```

    After installation, just use: `Compress-Video "your-video.mp4"`

    ### Option 2: Run Directly from Gist (No Installation)

    **Basic usage:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'video.mp4'"
    ```

    **With compression mode:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast"
    ```

    **Advanced with parameters:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## 🎯 Compression Modes

    | Mode | Speed | Quality | Best For |
    |------|-------|---------|----------|
    | `Fast` | ⚡⚡⚡ | ⭐⭐⭐ | Large batches, quick processing |
    | `Balanced` | ⚡⚡ | ⭐⭐⭐⭐ | **Default** - Best balance |
    | `Quality` || ⭐⭐⭐⭐⭐ | Archival, best compression |

    ## ⚡ Key Features

    - **Auto FFmpeg Install**: Installs FFmpeg via winget if missing
    - **Hardware Acceleration**: Intel QuickSync, NVIDIA NVENC support
    - **Cloud Optimized**: Perfect for OneDrive, YouTube, SharePoint
    - **Smart Scaling**: Auto-scales >1080p videos maintaining aspect ratio
    - **Batch Processing**: Easy to process multiple files

    ## 💡 Quick Examples

    ```powershell
    # Default balanced compression
    Compress-Video "MyVideo.mp4"
    # Fast mode for quick processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality compression
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output location
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\MyCompressed"
    # Batch process all MP4 files
    Get-ChildItem "*.mp4" | ForEach-Object { Compress-Video $_.Name }
    ```

    ## 🛠️ Requirements

    - Windows 10/11 + PowerShell 5.1+
    - FFmpeg (auto-installed if missing)
    - Internet connection (for FFmpeg download)

    ## 📊 Typical Results

    - **File size reduction**: 60-80% smaller
    - **Quality**: Visually lossless for most content
    - **Compatibility**: Works with all major platforms
    - **Speed**: Hardware acceleration when available

    Perfect for sharing large recordings, uploading to cloud storage, or preparing videos for web streaming! 🎬

    ## 🔧 How the One-Liner Works

    The TLDR command installs VideoCompressor by:
    1. **Creates profile directory** if it doesn't exist
    2. **Downloads script** to `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    3. **Updates $PROFILE** to auto-load the script (if not already present)
    4. **Ready to use** after restarting PowerShell
  13. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 135 additions and 0 deletions.
    135 changes: 135 additions & 0 deletions GIST_COMMENT.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    # VideoCompressor - Installation & Usage Guide

    🎬 **GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879

    ## 🚀 TLDR - One-Line Install

    **Copy and paste this command in PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& { $d = Split-Path $PROFILE -Parent; if (!(Test-Path $d)) { md $d -Force }; $u = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'; $f = Join-Path $d 'VideoCompressor.ps1'; iwr $u -OutFile $f -UseBasicParsing; if (!(Select-String 'VideoCompressor.ps1' $PROFILE -Quiet 2>$null)) { Add-Content $PROFILE \"`n. '$f'\" -Encoding UTF8 }; Write-Host '✅ VideoCompressor installed! Restart PowerShell and use: Compress-Video `"video.mp4`\"' -ForegroundColor Green }"
    ```

    **Then restart PowerShell and use:** `Compress-Video "video.mp4"`

    ---

    ## 📋 Detailed Installation Options

    ### Option 1: Install to PowerShell Profile (Recommended)
    **One-time setup for permanent availability:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path $PROFILE -Parent
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    $scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    $localPath = Join-Path $profileDir 'VideoCompressor.ps1'
    try {
    Invoke-WebRequest -Uri $scriptUrl -OutFile $localPath -UseBasicParsing
    Write-Host 'VideoCompressor downloaded successfully!' -ForegroundColor Green
    $profileContent = if (Test-Path $PROFILE) { Get-Content $PROFILE -Raw } else { '' }
    $dotSourceLine = '. `"$localPath`"'
    if ($profileContent -notlike "*VideoCompressor.ps1*") {
    Add-Content -Path $PROFILE -Value "`n# VideoCompressor Auto-Load`n$dotSourceLine" -Encoding UTF8
    Write-Host 'Added to PowerShell profile. Restart PowerShell or run: . $PROFILE' -ForegroundColor Yellow
    } else {
    Write-Host 'VideoCompressor already in profile!' -ForegroundColor Yellow
    }
    Write-Host 'Installation complete! Usage: Compress-Video `"video.mp4`"' -ForegroundColor Green
    }
    catch {
    Write-Error "Failed to download: $_"
    }
    }"
    ```

    After installation, just use: `Compress-Video "your-video.mp4"`

    ### Option 2: Run Directly from Gist (No Installation)

    **Basic usage:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'video.mp4'"
    ```

    **With compression mode:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast"
    ```

    **Advanced with parameters:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## 🎯 Compression Modes

    | Mode | Speed | Quality | Best For |
    |------|-------|---------|----------|
    | `Fast` | ⚡⚡⚡ | ⭐⭐⭐ | Large batches, quick processing |
    | `Balanced` | ⚡⚡ | ⭐⭐⭐⭐ | **Default** - Best balance |
    | `Quality` || ⭐⭐⭐⭐⭐ | Archival, best compression |

    ## ⚡ Key Features

    - **Auto FFmpeg Install**: Installs FFmpeg via winget if missing
    - **Hardware Acceleration**: Intel QuickSync, NVIDIA NVENC support
    - **Cloud Optimized**: Perfect for OneDrive, YouTube, SharePoint
    - **Smart Scaling**: Auto-scales >1080p videos maintaining aspect ratio
    - **Batch Processing**: Easy to process multiple files

    ## 💡 Quick Examples

    ```powershell
    # Default balanced compression
    Compress-Video "MyVideo.mp4"
    # Fast mode for quick processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality compression
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output location
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\MyCompressed"
    # Batch process all MP4 files
    Get-ChildItem "*.mp4" | ForEach-Object { Compress-Video $_.Name }
    ```

    ## 🛠️ Requirements

    - Windows 10/11 + PowerShell 5.1+
    - FFmpeg (auto-installed if missing)
    - Internet connection (for FFmpeg download)

    ## 📊 Typical Results

    - **File size reduction**: 60-80% smaller
    - **Quality**: Visually lossless for most content
    - **Compatibility**: Works with all major platforms
    - **Speed**: Hardware acceleration when available

    Perfect for sharing large recordings, uploading to cloud storage, or preparing videos for web streaming! 🎬

    ## 🔧 How the One-Liner Works

    The TLDR command installs VideoCompressor by:
    1. **Creates profile directory** if it doesn't exist
    2. **Downloads script** to `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    3. **Updates $PROFILE** to auto-load the script (if not already present)
    4. **Ready to use** after restarting PowerShell
  14. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 112 additions and 0 deletions.
    112 changes: 112 additions & 0 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,112 @@
    # VideoCompressor - Quick Start Guide

    **🎬 GitHub Gist**: https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879

    ## 🚀 TLDR - One-Line Install to PowerShell Profile

    **Copy, paste, and run this single command in PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& { $d = Split-Path $PROFILE -Parent; if (!(Test-Path $d)) { md $d -Force }; $u = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'; $f = Join-Path $d 'VideoCompressor.ps1'; iwr $u -OutFile $f -UseBasicParsing; if (!(Select-String 'VideoCompressor.ps1' $PROFILE -Quiet 2>$null)) { Add-Content $PROFILE \"`n. '$f'\" -Encoding UTF8 }; Write-Host '✅ VideoCompressor installed! Restart PowerShell and use: Compress-Video `"video.mp4`\"' -ForegroundColor Green }"
    ```

    **That's it!** Restart PowerShell and use: `Compress-Video "video.mp4"`

    ---

    ## 📋 Detailed Installation Options

    ### Option 1: Install to PowerShell Profile (Recommended)

    **Standard installation command:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path `$PROFILE -Parent
    if (-not (Test-Path `$profileDir)) { New-Item -ItemType Directory -Path `$profileDir -Force }
    `$scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    `$localPath = Join-Path `$profileDir 'VideoCompressor.ps1'
    Invoke-WebRequest -Uri `$scriptUrl -OutFile `$localPath -UseBasicParsing
    Add-Content -Path `$PROFILE -Value `"`n. ```"`$localPath```"`" -Encoding UTF8
    Write-Host 'VideoCompressor installed!' -ForegroundColor Green
    }"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Run directly from gist without installing:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    ### Option 3: Advanced with Parameters

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ Profile Installation Details

    The one-liner installs VideoCompressor to your PowerShell profile by:

    1. **Script Location**: `$(Split-Path $PROFILE -Parent)\VideoCompressor.ps1`
    2. **Profile Entry**: Adds `. 'path\to\VideoCompressor.ps1'` to your `$PROFILE`
    3. **Auto-Loading**: Script loads automatically when PowerShell starts

    **Profile Path Examples:**
    - `C:\Users\YourName\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1`
    - `C:\Users\YourName\Documents\PowerShell\Microsoft.PowerShell_profile.ps1` (PowerShell 7+)

    ## 🔧 For Developers

    1. **Clone this gist**: `git clone https://gist.github.com/868fbc801667e65e334679fec62b6879.git`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  15. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 64 deletions.
    64 changes: 0 additions & 64 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -1,64 +0,0 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  16. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 64 additions and 0 deletions.
    64 changes: 64 additions & 0 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,64 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  17. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 64 deletions.
    64 changes: 0 additions & 64 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -1,64 +0,0 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  18. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 64 additions and 0 deletions.
    64 changes: 64 additions & 0 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,64 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  19. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 117 deletions.
    117 changes: 0 additions & 117 deletions GIST_COMMENT.md
    Original file line number Diff line number Diff line change
    @@ -1,117 +0,0 @@
    # VideoCompressor - Installation & Usage Guide

    ## 🚀 Quick Start Options

    ### Option 1: Install to PowerShell Profile (Recommended)
    **One-time setup for permanent availability:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path $PROFILE -Parent
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    $scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    $localPath = Join-Path $profileDir 'VideoCompressor.ps1'
    try {
    Invoke-WebRequest -Uri $scriptUrl -OutFile $localPath -UseBasicParsing
    Write-Host 'VideoCompressor downloaded successfully!' -ForegroundColor Green
    $profileContent = if (Test-Path $PROFILE) { Get-Content $PROFILE -Raw } else { '' }
    $dotSourceLine = '. `"$localPath`"'
    if ($profileContent -notlike "*VideoCompressor.ps1*") {
    Add-Content -Path $PROFILE -Value "`n# VideoCompressor Auto-Load`n$dotSourceLine" -Encoding UTF8
    Write-Host 'Added to PowerShell profile. Restart PowerShell or run: . $PROFILE' -ForegroundColor Yellow
    } else {
    Write-Host 'VideoCompressor already in profile!' -ForegroundColor Yellow
    }
    Write-Host 'Installation complete! Usage: Compress-Video `"video.mp4`"' -ForegroundColor Green
    }
    catch {
    Write-Error "Failed to download: $_"
    }
    }"
    ```

    After installation, just use: `Compress-Video "your-video.mp4"`

    ### Option 2: Run Directly from Gist (No Installation)

    **Basic usage:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'video.mp4'"
    ```

    **With compression mode:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast"
    ```

    **Advanced with parameters:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## 🎯 Compression Modes

    | Mode | Speed | Quality | Best For |
    |------|-------|---------|----------|
    | `Fast` | ⚡⚡⚡ | ⭐⭐⭐ | Large batches, quick processing |
    | `Balanced` | ⚡⚡ | ⭐⭐⭐⭐ | **Default** - Best balance |
    | `Quality` || ⭐⭐⭐⭐⭐ | Archival, best compression |

    ## ⚡ Key Features

    - **Auto FFmpeg Install**: Installs FFmpeg via winget if missing
    - **Hardware Acceleration**: Intel QuickSync, NVIDIA NVENC support
    - **Cloud Optimized**: Perfect for OneDrive, YouTube, SharePoint
    - **Smart Scaling**: Auto-scales >1080p videos maintaining aspect ratio
    - **Batch Processing**: Easy to process multiple files

    ## 💡 Quick Examples

    ```powershell
    # Default balanced compression
    Compress-Video "MyVideo.mp4"
    # Fast mode for quick processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality compression
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output location
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\MyCompressed"
    # Batch process all MP4 files
    Get-ChildItem "*.mp4" | ForEach-Object { Compress-Video $_.Name }
    ```

    ## 🛠️ Requirements

    - Windows 10/11 + PowerShell 5.1+
    - FFmpeg (auto-installed if missing)
    - Internet connection (for FFmpeg download)

    ## 📊 Typical Results

    - **File size reduction**: 60-80% smaller
    - **Quality**: Visually lossless for most content
    - **Compatibility**: Works with all major platforms
    - **Speed**: Hardware acceleration when available

    Perfect for sharing large recordings, uploading to cloud storage, or preparing videos for web streaming! 🎬

    ---

    **Note**: Replace the gist URL with your actual gist URL when creating your own version.
  20. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 64 deletions.
    64 changes: 0 additions & 64 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -1,64 +0,0 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://raw.githubusercontent.com/emilwojcik93/VideoCompressor-PowerShell/main/Install-VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/YOUR_GIST_ID/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  21. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 64 additions and 0 deletions.
    64 changes: 64 additions & 0 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,64 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://raw.githubusercontent.com/emilwojcik93/VideoCompressor-PowerShell/main/Install-VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/YOUR_GIST_ID/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  22. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 64 deletions.
    64 changes: 0 additions & 64 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -1,64 +0,0 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://raw.githubusercontent.com/emilwojcik93/VideoCompressor-PowerShell/main/Install-VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/YOUR_GIST_ID/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  23. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 117 additions and 0 deletions.
    117 changes: 117 additions & 0 deletions GIST_COMMENT.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,117 @@
    # VideoCompressor - Installation & Usage Guide

    ## 🚀 Quick Start Options

    ### Option 1: Install to PowerShell Profile (Recommended)
    **One-time setup for permanent availability:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& {
    $profileDir = Split-Path $PROFILE -Parent
    if (-not (Test-Path $profileDir)) { New-Item -ItemType Directory -Path $profileDir -Force }
    $scriptUrl = 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'
    $localPath = Join-Path $profileDir 'VideoCompressor.ps1'
    try {
    Invoke-WebRequest -Uri $scriptUrl -OutFile $localPath -UseBasicParsing
    Write-Host 'VideoCompressor downloaded successfully!' -ForegroundColor Green
    $profileContent = if (Test-Path $PROFILE) { Get-Content $PROFILE -Raw } else { '' }
    $dotSourceLine = '. `"$localPath`"'
    if ($profileContent -notlike "*VideoCompressor.ps1*") {
    Add-Content -Path $PROFILE -Value "`n# VideoCompressor Auto-Load`n$dotSourceLine" -Encoding UTF8
    Write-Host 'Added to PowerShell profile. Restart PowerShell or run: . $PROFILE' -ForegroundColor Yellow
    } else {
    Write-Host 'VideoCompressor already in profile!' -ForegroundColor Yellow
    }
    Write-Host 'Installation complete! Usage: Compress-Video `"video.mp4`"' -ForegroundColor Green
    }
    catch {
    Write-Error "Failed to download: $_"
    }
    }"
    ```

    After installation, just use: `Compress-Video "your-video.mp4"`

    ### Option 2: Run Directly from Gist (No Installation)

    **Basic usage:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video 'video.mp4'"
    ```

    **With compression mode:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast"
    ```

    **Advanced with parameters:**
    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "
    $params = @{
    SourceVideo = 'C:\Users\$env:USERNAME\Downloads\video.mp4'
    CompressionMode = 'Quality'
    OutputDirectory = 'C:\Users\$env:USERNAME\OneDrive\CompressedVideos'
    }
    & ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1')))
    Compress-Video @params
    "
    ```

    ## 🎯 Compression Modes

    | Mode | Speed | Quality | Best For |
    |------|-------|---------|----------|
    | `Fast` | ⚡⚡⚡ | ⭐⭐⭐ | Large batches, quick processing |
    | `Balanced` | ⚡⚡ | ⭐⭐⭐⭐ | **Default** - Best balance |
    | `Quality` || ⭐⭐⭐⭐⭐ | Archival, best compression |

    ## ⚡ Key Features

    - **Auto FFmpeg Install**: Installs FFmpeg via winget if missing
    - **Hardware Acceleration**: Intel QuickSync, NVIDIA NVENC support
    - **Cloud Optimized**: Perfect for OneDrive, YouTube, SharePoint
    - **Smart Scaling**: Auto-scales >1080p videos maintaining aspect ratio
    - **Batch Processing**: Easy to process multiple files

    ## 💡 Quick Examples

    ```powershell
    # Default balanced compression
    Compress-Video "MyVideo.mp4"
    # Fast mode for quick processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality compression
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output location
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\MyCompressed"
    # Batch process all MP4 files
    Get-ChildItem "*.mp4" | ForEach-Object { Compress-Video $_.Name }
    ```

    ## 🛠️ Requirements

    - Windows 10/11 + PowerShell 5.1+
    - FFmpeg (auto-installed if missing)
    - Internet connection (for FFmpeg download)

    ## 📊 Typical Results

    - **File size reduction**: 60-80% smaller
    - **Quality**: Visually lossless for most content
    - **Compatibility**: Works with all major platforms
    - **Speed**: Hardware acceleration when available

    Perfect for sharing large recordings, uploading to cloud storage, or preparing videos for web streaming! 🎬

    ---

    **Note**: Replace the gist URL with your actual gist URL when creating your own version.
  24. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 64 additions and 0 deletions.
    64 changes: 64 additions & 0 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,64 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://raw.githubusercontent.com/emilwojcik93/VideoCompressor-PowerShell/main/Install-VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/YOUR_GIST_ID/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  25. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 64 deletions.
    64 changes: 0 additions & 64 deletions QUICK_START.md
    Original file line number Diff line number Diff line change
    @@ -1,64 +0,0 @@
    # VideoCompressor - Quick Start Guide

    ## 🚀 Installation Options

    ### Option 1: One-Click Install to Profile (Recommended)

    **Copy and paste this command into PowerShell:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "iex (irm 'https://raw.githubusercontent.com/emilwojcik93/VideoCompressor-PowerShell/main/Install-VideoCompressor.ps1')"
    ```

    After installation, restart PowerShell and use: `Compress-Video "video.mp4"`

    ### Option 2: Direct Execution (No Installation)

    **Compress any video file directly:**

    ```powershell
    powershell.exe -ExecutionPolicy Bypass -Command "& ([ScriptBlock]::Create((irm 'https://gist.github.com/emilwojcik93/YOUR_GIST_ID/raw/VideoCompressor.ps1'))); Compress-Video 'your-video.mp4'"
    ```

    **Replace `YOUR_GIST_ID` with your actual gist ID**

    ## ⚡ Quick Examples

    ```powershell
    # Basic compression (balanced mode)
    Compress-Video "MyVideo.mp4"
    # Fast processing
    Compress-Video "MyVideo.mp4" -CompressionMode Fast
    # Best quality
    Compress-Video "MyVideo.mp4" -CompressionMode Quality
    # Custom output folder
    Compress-Video "MyVideo.mp4" -OutputDirectory "C:\Compressed"
    ```

    ## 🎯 What It Does

    -**Auto-installs FFmpeg** if not found
    -**Hardware acceleration** (Intel/NVIDIA)
    -**Reduces file size by 60-80%**
    -**Maintains visual quality**
    -**Optimized for cloud storage**
    -**Works with all video formats**

    ## 📋 Requirements

    - Windows 10/11
    - PowerShell 5.1+
    - Internet connection (for FFmpeg auto-install)

    ## 🛠️ For Developers

    1. **Create your own gist**: Run `.\Create-Gist.ps1`
    2. **Install locally**: Run `.\Install-VideoCompressor.ps1`
    3. **Customize**: Edit `VideoCompressor.ps1` as needed

    ---

    Perfect for preparing videos for **OneDrive**, **YouTube**, **SharePoint**, or any cloud platform! 🎬
  26. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 0 additions and 108 deletions.
    108 changes: 0 additions & 108 deletions Create-Gist.ps1
    Original file line number Diff line number Diff line change
    @@ -1,108 +0,0 @@
    #Requires -Version 5.1

    <#
    .SYNOPSIS
    Creates a GitHub Gist for the VideoCompressor script
    .DESCRIPTION
    Uses GitHub CLI (gh) to create a public gist with the VideoCompressor script and documentation.
    Requires GitHub CLI to be installed and authenticated.
    .EXAMPLE
    .\Create-Gist.ps1
    #>

    Write-Host "VideoCompressor Gist Creator" -ForegroundColor Cyan
    Write-Host "============================" -ForegroundColor Cyan
    Write-Host ""

    # Check if GitHub CLI is available
    try {
    $ghStatus = gh auth status 2>$null
    Write-Host "GitHub CLI authentication status:" -ForegroundColor Green
    gh auth status
    Write-Host ""
    }
    catch {
    Write-Host "GitHub CLI (gh) not found or not authenticated!" -ForegroundColor Red
    Write-Host ""
    Write-Host "Please install GitHub CLI and authenticate:" -ForegroundColor Yellow
    Write-Host "1. Download from: https://cli.github.com/" -ForegroundColor White
    Write-Host "2. Run: gh auth login" -ForegroundColor White
    Write-Host ""
    exit 1
    }

    # Check if script file exists
    $scriptPath = Join-Path $PSScriptRoot "VideoCompressor.ps1"
    if (-not (Test-Path $scriptPath)) {
    Write-Host "VideoCompressor.ps1 not found in current directory!" -ForegroundColor Red
    Write-Host "Expected path: $scriptPath" -ForegroundColor Yellow
    exit 1
    }

    # Prepare gist description
    $description = "VideoCompressor - PowerShell video compression tool optimized for cloud storage (OneDrive, YouTube, SharePoint). Features automatic FFmpeg installation, hardware acceleration, and smart compression settings."

    # Create the gist
    Write-Host "Creating GitHub Gist..." -ForegroundColor Yellow
    Write-Host "Description: $description" -ForegroundColor Gray
    Write-Host ""

    try {
    # Create public gist with the main script
    $gistResult = gh gist create --public --desc $description $scriptPath

    if ($LASTEXITCODE -eq 0) {
    Write-Host "Gist created successfully!" -ForegroundColor Green
    Write-Host "URL: $gistResult" -ForegroundColor Cyan
    Write-Host ""

    # Extract gist ID from URL
    $gistId = ($gistResult -split '/')[-1]
    Write-Host "Gist ID: $gistId" -ForegroundColor Yellow
    Write-Host ""

    # Show usage examples with the actual gist URL
    $rawUrl = "https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1"

    Write-Host "Usage Examples:" -ForegroundColor Cyan
    Write-Host ""

    Write-Host "1. Install to PowerShell Profile:" -ForegroundColor Yellow
    Write-Host "powershell.exe -ExecutionPolicy Bypass -Command `"& {" -ForegroundColor Gray
    Write-Host " `$profileDir = Split-Path `$PROFILE -Parent" -ForegroundColor Gray
    Write-Host " if (-not (Test-Path `$profileDir)) { New-Item -ItemType Directory -Path `$profileDir -Force }" -ForegroundColor Gray
    Write-Host " Invoke-WebRequest -Uri '$rawUrl' -OutFile (Join-Path `$profileDir 'VideoCompressor.ps1') -UseBasicParsing" -ForegroundColor Gray
    Write-Host " Add-Content -Path `$PROFILE -Value `"`n. ```"(Join-Path (Split-Path `$PROFILE -Parent) 'VideoCompressor.ps1')```"`" -Encoding UTF8" -ForegroundColor Gray
    Write-Host "}`"" -ForegroundColor Gray
    Write-Host ""

    Write-Host "2. Run Directly from Gist:" -ForegroundColor Yellow
    Write-Host "powershell.exe -ExecutionPolicy Bypass -Command `"& ([ScriptBlock]::Create((irm '$rawUrl'))); Compress-Video 'video.mp4'`"" -ForegroundColor Gray
    Write-Host ""

    Write-Host "3. With Parameters:" -ForegroundColor Yellow
    Write-Host "powershell.exe -ExecutionPolicy Bypass -Command `"& ([ScriptBlock]::Create((irm '$rawUrl'))); Compress-Video -SourceVideo 'video.mp4' -CompressionMode Fast`"" -ForegroundColor Gray
    Write-Host ""

    # Suggest adding to gist description/comments
    Write-Host "Suggested Gist Comment/Description:" -ForegroundColor Cyan
    Write-Host "Copy the content from GIST_COMMENT.md and add it as a comment to your gist for user instructions." -ForegroundColor Yellow

    # Open gist in browser
    $openGist = Read-Host "Open gist in browser? (y/n)"
    if ($openGist -eq 'y' -or $openGist -eq 'Y') {
    Start-Process $gistResult
    }
    }
    else {
    Write-Host "Failed to create gist. Exit code: $LASTEXITCODE" -ForegroundColor Red
    }
    }
    catch {
    Write-Host "Error creating gist: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host ""
    Write-Host "Gist creation script completed." -ForegroundColor Cyan
  27. emilwojcik93 revised this gist Sep 15, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion Create-Gist.ps1
    Original file line number Diff line number Diff line change
    @@ -64,7 +64,7 @@ try {
    Write-Host ""

    # Show usage examples with the actual gist URL
    $rawUrl = "https://gist.github.com/$env:USERNAME/$gistId/raw/VideoCompressor.ps1"
    $rawUrl = "https://gist.github.com/emilwojcik93/868fbc801667e65e334679fec62b6879/raw/VideoCompressor.ps1"

    Write-Host "Usage Examples:" -ForegroundColor Cyan
    Write-Host ""
  28. emilwojcik93 revised this gist Sep 15, 2025. No changes.
  29. emilwojcik93 revised this gist Sep 15, 2025. No changes.
  30. emilwojcik93 revised this gist Sep 15, 2025. No changes.