Public/Invoke-WindowsMediaDownloader.ps1

<#
.SYNOPSIS
Downloads Windows ISO files for specified OS and Type.
 
.DESCRIPTION
The Invoke-WindowsMediaDownloader function downloads Windows ISO files from a specified URI.
It allows users to select the operating system (OS), the type of Windows (such as consumer for client or 2019 for server),
the language, and the destination for the download.
 
.PARAMETER OS
Specifies the operating system for which to download the ISO. Valid options are 'server', '10', and '11'.
 
.PARAMETER Type
Specifies the type of Windows ISO to download. Options include 'consumer', 'business', '2022', '2019', and '2016'.
 
.PARAMETER Lang
Specifies the language for the ISO. If not set, the function will prompt for a language choice.
 
.PARAMETER Destination
Specifies the directory where the ISO file will be downloaded. Default is the user's Downloads folder.
 
.PARAMETER Force
Forces the download of the ISO even if a file with the same name already exists at the destination.
 
.PARAMETER Skip
Skips the download if a file with the same name already exists at the destination.
 
.PARAMETER NoDownload
If set, the function will not download the file but return the final URI for the selected ISO.
 
.EXAMPLE
Invoke-WindowsMediaDownloader -OS '11' -Type 'consumer' -Lang 'en-us'
 
Downloads latest consumer version of Windows 11 with language 'en-us'
 
.EXAMPLE
Invoke-WindowsMediaDownloader -OS '11' -Type 'consumer'
 
Asks to select a language from the availables list and downloads latest consumer version of Windows 11
 
.EXAMPLE
Invoke-WindowsMediaDownloader -OS 'server' -Type '2022'
 
Asks to select a language from the availables list and downloads latest 2022 version of Windows Server
 
.NOTES
Author: innovatodev
 
.LINK
https://github.com/innovatodev/WindowsMediaDownloader
 
#>


function Invoke-WindowsMediaDownloader {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)][ValidateSet('server', '10', '11')][String]$OS,
        [Parameter(Mandatory = $true, Position = 1)][ValidateSet('consumer', 'business', '2022', '2019', '2016')][String]$Type,
        [Parameter()][String]$Lang = "",
        [Parameter()][System.IO.DirectoryInfo]$Destination = "$env:USERPROFILE\Downloads",
        [Parameter()][Switch]$Force,
        [Parameter()][Switch]$Skip,
        [Parameter()][Switch]$NoDownload

    )
    function Select-Option {
        param(
            [parameter(Mandatory = $true, Position = 0)][string]$Message,
            [parameter(Mandatory = $true, Position = 1)][string[]]$Options
        )
        $Selected = $null
        while ($null -eq $Selected) {
            Write-Host "`n$Message" -ForegroundColor Yellow
            for ($i = 0; $i -lt $Options.Length; $i++) {
                Write-Host "`n[" -NoNewline
                Write-Host "$($i+1)" -NoNewline -ForegroundColor Yellow
                Write-Host "]" -NoNewline
                Write-Host " $($Options[$i])" -NoNewline
            }
            $SelectedIndex = [int](Read-Host "`nSelect an option")
            if ($SelectedIndex -gt 0 -and $SelectedIndex -le $Options.Length) {
                $Selected = $Options[$SelectedIndex - 1]
            } else {
                Write-Host "`nInvalid Option" -ForegroundColor Red
            }
        }
        return $Selected
    }
    $uri = "https://massgrave.dev/windows_$($OS)_links.html"
    $response = Invoke-WebRequest -Uri $uri -UseBasicParsing -DisableKeepAlive | Select-Object Links
    $finalURI = ""
    $finalDestination = ""
    $langHash = @{}
    [string[]]$langOptions = @()

    if ([String]::IsNullOrEmpty($Lang)) { $filterURL = "*https://drive.massgrave.dev/*$($OS)*$($Type)*x64*.iso*" }
    else { $filterURL = "*https://drive.massgrave.dev/*$Lang*$($OS)*$($Type)*x64*.iso*" }

    if ([String]::IsNullOrEmpty($Lang)) {
        $selected = $response.Links | Where-Object { $_.outerHTML -like $filterURL }
        $selected.href | ForEach-Object {
            $split = (($_ -split 'https://drive.massgrave.dev/')[1] -Split ('_'))[0]
            $langHash.Add($split, $_)
        }
        $langHash.GetEnumerator() | Sort-Object Value | ForEach-Object { $langOptions += $_.Key }
        $askLang = Select-Option -Message "Please Select a langage" -Options $langOptions
        $splitFileName = ($($langHash["$askLang"]) -split 'https://drive.massgrave.dev/')[1]
        $finalURI = $($langHash["$askLang"])
        $finalDestination = "$Destination\$splitFileName"
        if ($NoDownload) { return $finalURI }
    } else {
        $selected = $response.Links | Where-Object { $_.outerHTML -like $filterURL } | Select-Object -First 1
        if ($null -eq $selected.href) { Write-Error "No ISO Found on $uri" ; return }
        $splitFileName = ($($selected.href) -split 'https://drive.massgrave.dev/')[1]
        $finalURI = $($selected.href)
        $finalDestination = "$Destination\$splitFileName"
        if ($NoDownload) { return $finalURI }
    }

    if (Test-Path $finalDestination) {
        if ($Skip -eq $true) {
            Write-Warning "$finalDestination already exists, skipping."
            return $finalDestination
        } elseif ($Force -eq $true) {
            Write-Host "Downloading: $finalURI" -ForegroundColor Blue
            Write-Host "Destination: $finalDestination" -ForegroundColor Magenta
            try {
                Write-Warning "Removing $finalDestination"
                Remove-Item $finalDestination -Force -Confirm:$false | Out-Null
                Invoke-WebRequest -Uri $finalURI -OutFile $finalDestination
            } catch { Write-Error "Error downloading $finalURI" ; return $null }
        } else { Write-Error "The ISO already exists, please add -Force to overwrite." ; return $null }
    } else {
        Write-Host "Downloading: $finalURI" -ForegroundColor Blue
        Write-Host "Destination: $finalDestination" -ForegroundColor Magenta
        try {
            Invoke-WebRequest -Uri $finalURI -OutFile $finalDestination
        } catch { Write-Error "Error downloading $finalURI" ; return $null }
    }

    Write-Host "Ready: $finalDestination" -ForegroundColor Green
    return $finalDestination
}