ZipFolders.ps1

<#
.SYNOPSIS
    Compresses all first-level subfolders within a specified parent folder into individual ZIP archives using 7-Zip.
.DESCRIPTION
    Used this for archiving to the archive drive.
    This script takes a parent folder path as an argument. It iterates through all
    immediate subdirectories within that parent folder. Each subdirectory is then
    compressed into its own .zip file, named after the subdirectory, and saved
    in the user's Downloads folder.
    You must have 7-Zip installed for this script to work.
.PARAMETER ParentFolderPath
    The full path to the folder containing the subfolders you want to compress.
.EXAMPLE
    .\Compress-SubFolders.ps1 -ParentFolderPath "C:\MyProjects"
    This will find all subfolders directly under C:\MyProjects (e.g., C:\MyProjects\ProjectA, C:\MyProjects\ProjectB)
    and create ProjectA.zip and ProjectB.zip in the Downloads folder.
.NOTES
    Author: GitHub Copilot
    Date: 2025-05-12
    Ensure 7-Zip is installed and the path to 7z.exe is correct or 7z.exe is in your system PATH.
#>

param (
    [Parameter(Mandatory=$true, ValueFromPipeline=$false)]
    [string]$ParentFolderPath
)

# --- Configuration: 7-Zip Path ---
$sevenZipExePaths = @(
    "C:\Program Files\7-Zip\7z.exe",      # Common 64-bit install path
    "C:\Program Files (x86)\7-Zip\7z.exe" # Common 32-bit install path
)
$sevenZipExe = $null

foreach ($path in $sevenZipExePaths) {
    if (Test-Path $path -PathType Leaf) {
        $sevenZipExe = $path
        break
    }
}

if (-not $sevenZipExe) {
    if (Get-Command 7z.exe -ErrorAction SilentlyContinue) {
        $sevenZipExe = "7z.exe" # Found in PATH
        Write-Host "Using 7z.exe found in system PATH."
    } else {
        Write-Error "7-Zip executable (7z.exe) not found. Please install 7-Zip or ensure it's in your system PATH."
        Write-Error "Checked paths: $($sevenZipExePaths -join ', ') and system PATH."
        exit 1
    }
} else {
    Write-Host "Using 7-Zip at: $sevenZipExe"
}

# --- Get Downloads Folder ---
try {
    $downloadsFolder = [IO.Path]::Combine($env:USERPROFILE, "Downloads")
    if (-not (Test-Path $downloadsFolder -PathType Container)) {
        Write-Error "Downloads folder not found at '$downloadsFolder'."
        exit 1
    }

    # Create a subfolder named "ZipArchives" inside Downloads
    $archivesFolder = [IO.Path]::Combine($downloadsFolder, "ZipArchives")
    if (-not (Test-Path $archivesFolder -PathType Container)) {
        New-Item -ItemType Directory -Path $archivesFolder | Out-Null
    }

    # Create a folder named after the source folder in the ZipArchives folder
    $sourceFolderName = [IO.Path]::GetFileName($ParentFolderPath.TrimEnd('\'))
    $outputFolder = [IO.Path]::Combine($archivesFolder, $sourceFolderName)

    # Ensure the folder name is unique by appending a numeric identifier if necessary
    $counter = 1
    while (Test-Path $outputFolder -PathType Container) {
        $outputFolder = [IO.Path]::Combine($archivesFolder, "$sourceFolderName ($counter)")
        $counter++
    }

    New-Item -ItemType Directory -Path $outputFolder | Out-Null
    Write-Host "Outputting ZIP files to: $outputFolder"
} catch {
    Write-Error "Error getting Downloads folder path: $($_.Exception.Message)"
    exit 1
}

# --- Validate Parent Folder ---
if (-not (Test-Path $ParentFolderPath -PathType Container)) {
    Write-Error "The specified parent folder does not exist or is not a directory: '$ParentFolderPath'"
    exit 1
}

# --- Get First-Level Subfolders ---
$subFolders = Get-ChildItem -Path $ParentFolderPath -Directory -ErrorAction SilentlyContinue
if (-not $subFolders) {
    Write-Warning "No subfolders found in '$ParentFolderPath'."
    exit 0
}

Write-Host "Found the following subfolders to compress in '$ParentFolderPath':"
$subFolders | ForEach-Object { Write-Host "- $($_.Name)" }
Write-Host "" # Newline for better readability

# --- Process Each Subfolder Concurrently ---
$maxConcurrentJobs = 4
$jobs = @() # Initialize as a standard PowerShell array

foreach ($folder in $subFolders) {
    # Wait if the number of running jobs reaches the limit
    while (($jobs | Where-Object { $_.State -eq 'Running' }).Count -ge $maxConcurrentJobs) {
        $runningJobsCount = ($jobs | Where-Object { $_.State -eq 'Running' }).Count
        Write-Host "Waiting for available job slots... Running jobs: $runningJobsCount"
        Start-Sleep -Seconds 1
    }

    $folderName = $folder.Name
    $folderFullPath = $folder.FullName
    $zipFileName = "$($folderName).zip"
    $outputZipPath = Join-Path -Path $outputFolder -ChildPath $zipFileName

    Write-Host "Starting compression for folder: $folderName"

    # Start a new job for compressing the folder
    $job = Start-Job -ScriptBlock {
        param ($sevenZipExe, $folderFullPath, $outputZipPath)

        $arguments = @(
            "a",
            "-tzip",
            "-r",
            "`"$outputZipPath`"",
            "`"$folderFullPath\*`""
        )

        try {
            # Run the 7-Zip process without redirecting output
            $process = Start-Process -FilePath $sevenZipExe -ArgumentList $arguments -Wait -NoNewWindow -PassThru
            if ($process.ExitCode -eq 0) {
                Write-Output "Successfully compressed '$folderFullPath' to '$outputZipPath'."
            } else {
                Write-Output "7-Zip process exited with code $($process.ExitCode) for folder '$folderFullPath'."
            }
        } catch {
            Write-Output "Error running 7-Zip for folder '$folderFullPath': $($_.Exception.Message)"
        }
    } -ArgumentList $sevenZipExe, $folderFullPath, $outputZipPath

    $jobs += $job # Append the job to the array
}

# Wait for all jobs to complete
Write-Host "Waiting for all compression jobs to complete..."
while (($jobs | Where-Object { $_.State -eq 'Running' }).Count -gt 0) {
    $runningJobsCount = ($jobs | Where-Object { $_.State -eq 'Running' }).Count
    Write-Host "Running jobs: $runningJobsCount"
    Start-Sleep -Seconds 1
}

# Clean up completed jobs
$jobs | ForEach-Object {
    # $jobOutput = Receive-Job -Job $_ -Wait
    # Write-Host $jobOutput
    Remove-Job -Job $_
}

# Copy files from the parent folder to the output folder
Write-Host "Copying files from parent folder to output folder..."
$parentFolderFiles = Get-ChildItem -Path $ParentFolderPath -File -ErrorAction SilentlyContinue

if ($parentFolderFiles) {
    foreach ($file in $parentFolderFiles) {
        $destinationPath = Join-Path -Path $outputFolder -ChildPath $file.Name
        try {
            Copy-Item -Path $file.FullName -Destination $destinationPath -Force
            Write-Host "Copied file: $($file.Name) to $outputFolder"
        } catch {
            Write-Error "Failed to copy file: $($file.Name). Error: $($_.Exception.Message)"
        }
    }
} else {
    Write-Host "No files found in the parent folder to copy."
}

Write-Host "All folders have been compressed. Total: $($jobs.Count)"
Write-Host "ZIP files are located in: $outputFolder"