mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
33f0608d84
Eliminates an unnecessary progress queue update that occurs immediately before downloading package XML, as this status message duplicates information already provided in the subsequent log message. This simplifies the progress reporting flow and reduces redundant communication to the progress queue without impacting user-facing functionality.
476 lines
27 KiB
PowerShell
476 lines
27 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Provides functions for discovering, downloading, and processing Lenovo drivers.
|
|
.DESCRIPTION
|
|
This module contains the logic specific to handling Lenovo drivers for the FFU Builder UI. It includes functions to query the Lenovo PSREF (Product Specification Reference) API to find and list available system models based on user search terms. It also provides the core background task for downloading all relevant driver packages for a selected model and Windows release. The download process involves parsing XML catalogs, downloading individual driver executables, silently extracting their contents, and organizing them into a structured folder. The module includes robust error handling, long path mitigation by using temporary extraction locations, and an option to compress the final driver set into a WIM archive.
|
|
#>
|
|
|
|
# Function to get the list of Lenovo models using the PSREF API
|
|
function Get-LenovoDriversModelList {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ModelSearchTerm, # User input for model/machine type
|
|
[Parameter(Mandatory = $true)]
|
|
[hashtable]$Headers,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$UserAgent
|
|
)
|
|
|
|
# Lenovo is special - they prevent access to the PSREF API without a cookie as of July 2025.
|
|
# This cookie must be retrieved via Javascript
|
|
# It appears that the cookie is hard-coded. We'll see how long this lasts.
|
|
# If anyone knows how to reliably get the the model and machine type information from Lenovo, let me know.
|
|
# https://download.lenovo.com/cdrt/td/catalogv2.xml only provides a subset of the information available from PSREF (e.g. it's missing 300w, 500w, and other consumer models).
|
|
|
|
# $lenovoCookie = "X-PSREF-USER-TOKEN=eyJ0eXAiOiJKV1QifQ.bjVTdWk0YklZeUc2WnFzL0lXU0pTeU1JcFo0aExzRXl1UGxHN3lnS1BtckI0ZVU5WEJyVGkvaFE0NmVNU2U1ZjNrK3ZqTEVIZ29nTk1TNS9DQmIwQ0pTN1Q1VytlY1RpNzZTUldXbm4wZ1g2RGJuQWg4MXRkTmxKT2YrOW9LRjBzQUZzV05HM3NpcU92WFVTM0o0blM1SDQyUlVXNThIV1VBS2R0c1B2NjJyQjIrUGxNZ2x6RTRhUjY5UDZWclBX.ZDBmM2EyMWRjZTg2N2JmYWMxZDIxY2NiYjQzMWFhNjg1YjEzZTAxNmU2M2RmN2M5ZjIyZWJhMzZkOWI1OWJhZg"
|
|
|
|
# Wrote a separate function to grab the token. Check the function notes for more details. Keep the above comment for now to see if the cookie ever changes.
|
|
$lenovoCookie = Get-LenovoPSREFToken
|
|
|
|
# Add the cookie to the headers
|
|
$Headers["Cookie"] = $lenovoCookie
|
|
|
|
WriteLog "Querying Lenovo PSREF API for model/machine type: $ModelSearchTerm"
|
|
$url = "https://psref.lenovo.com/api/search/DefinitionFilterAndSearch/Suggest?kw=$([uri]::EscapeDataString($ModelSearchTerm))"
|
|
$models = [System.Collections.Generic.List[PSCustomObject]]::new()
|
|
|
|
try {
|
|
$OriginalVerbosePreference = $VerbosePreference
|
|
$VerbosePreference = 'SilentlyContinue'
|
|
$response = Invoke-WebRequest -Uri $url -UseBasicParsing -Headers $Headers -UserAgent $UserAgent -ErrorAction Stop
|
|
$VerbosePreference = $OriginalVerbosePreference
|
|
WriteLog "PSREF API query complete."
|
|
|
|
$jsonResponse = $response.Content | ConvertFrom-Json
|
|
|
|
if ($null -ne $jsonResponse.data -and $jsonResponse.data.Count -gt 0) {
|
|
foreach ($item in $jsonResponse.data) {
|
|
$productName = $item.ProductName
|
|
$machineTypes = $item.MachineType -split " / " # Split if multiple machine types are listed
|
|
|
|
foreach ($machineTypeRaw in $machineTypes) {
|
|
$machineType = $machineTypeRaw.Trim()
|
|
# Only add if machine type is not empty
|
|
if (-not [string]::IsNullOrWhiteSpace($machineType)) {
|
|
# Create the combined display string
|
|
$displayModel = "$productName ($machineType)"
|
|
# Add each combination as a separate entry
|
|
$models.Add([PSCustomObject]@{
|
|
Make = 'Lenovo'
|
|
Model = $displayModel
|
|
ProductName = $productName
|
|
MachineType = $machineType
|
|
})
|
|
}
|
|
else {
|
|
WriteLog "Skipping entry for product '$productName' due to missing machine type."
|
|
}
|
|
}
|
|
}
|
|
WriteLog "Found $($models.Count) potential model/machine type combinations for '$ModelSearchTerm'."
|
|
}
|
|
else {
|
|
WriteLog "No models found matching '$ModelSearchTerm' in Lenovo PSREF."
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Error querying Lenovo PSREF API: $($_.Exception.Message)"
|
|
# Return empty list on error
|
|
}
|
|
return $models
|
|
}
|
|
# Function to download and extract drivers for a specific Lenovo model (Background Task)
|
|
function Save-LenovoDriversTask {
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[PSCustomObject]$DriverItemData, # Contains Model (ProductName) and MachineType
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$DriversFolder,
|
|
[Parameter(Mandatory = $true)]
|
|
[int]$WindowsRelease,
|
|
[Parameter(Mandatory = $true)]
|
|
[hashtable]$Headers,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$UserAgent,
|
|
[Parameter()]
|
|
[System.Collections.Concurrent.ConcurrentQueue[hashtable]]$ProgressQueue = $null,
|
|
[Parameter()]
|
|
[bool]$CompressToWim = $false,
|
|
[Parameter()]
|
|
[bool]$PreserveSourceOnCompress = $false
|
|
)
|
|
|
|
# The Model property from the UI already contains the combined "ProductName (MachineType)" string
|
|
$identifier = $DriverItemData.Model
|
|
$machineType = $DriverItemData.MachineType
|
|
$make = "Lenovo"
|
|
$sanitizedIdentifier = ConvertTo-SafeName -Name $identifier
|
|
if ($sanitizedIdentifier -ne $identifier) { WriteLog "Sanitized model identifier: '$identifier' -> '$sanitizedIdentifier'" }
|
|
$status = "Starting..."
|
|
$success = $false
|
|
|
|
# Define paths
|
|
$makeDriversPath = Join-Path -Path $DriversFolder -ChildPath $Make
|
|
# Use the identifier (which contains the model name and machine type) and sanitize it for the path
|
|
$modelPath = Join-Path -Path $makeDriversPath -ChildPath $sanitizedIdentifier
|
|
$driverRelativePath = Join-Path -Path $make -ChildPath $sanitizedIdentifier # Relative path for the driver folder
|
|
$tempDownloadPath = Join-Path -Path $makeDriversPath -ChildPath "_TEMP_$($machineType)_$($PID)" # Temp folder for catalog/package XMLs
|
|
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status "Checking..." }
|
|
|
|
try {
|
|
# Check for existing drivers
|
|
$existingDriver = Test-ExistingDriver -Make $make -Model $sanitizedIdentifier -DriversFolder $DriversFolder -Identifier $identifier -ProgressQueue $ProgressQueue
|
|
if ($null -ne $existingDriver) {
|
|
# The return object from Test-ExistingDriver uses 'Model' as the identifier key.
|
|
# We need to return 'Identifier' for Lenovo's logic.
|
|
$existingDriver | Add-Member -MemberType NoteProperty -Name 'Identifier' -Value $identifier -Force
|
|
$existingDriver.PSObject.Properties.Remove('Model')
|
|
|
|
# Special handling for existing folders that need compression
|
|
if ($CompressToWim -and $existingDriver.Status -eq 'Already downloaded') {
|
|
$wimFilePath = Join-Path -Path $makeDriversPath -ChildPath "$($sanitizedIdentifier).wim"
|
|
$wimRelativePath = Join-Path -Path $make -ChildPath "$($sanitizedIdentifier).wim"
|
|
$sourceFolderPath = Join-Path -Path $makeDriversPath -ChildPath $sanitizedIdentifier
|
|
WriteLog "Attempting compression of existing folder '$sourceFolderPath' to '$wimFilePath'."
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status "Compressing existing..." }
|
|
try {
|
|
$null = Compress-DriverFolderToWim -SourceFolderPath $sourceFolderPath -DestinationWimPath $wimFilePath -WimName $identifier -WimDescription "Drivers for $identifier" -PreserveSource:$PreserveSourceOnCompress -ErrorAction Stop
|
|
$existingDriver.Status = "Compression successful"
|
|
$existingDriver.DriverPath = $wimRelativePath
|
|
$existingDriver.Success = $true
|
|
WriteLog "Successfully compressed existing drivers for $identifier to $wimFilePath."
|
|
}
|
|
catch {
|
|
WriteLog "Error compressing existing drivers for $($identifier): $($_.Exception.Message)"
|
|
$existingDriver.Status = "Already downloaded (Compression failed)"
|
|
$existingDriver.Success = $false
|
|
}
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $existingDriver.Status }
|
|
}
|
|
|
|
return $existingDriver
|
|
}
|
|
|
|
# Ensure base directories exist
|
|
if (-not (Test-Path -Path $makeDriversPath)) { New-Item -Path $makeDriversPath -ItemType Directory -Force | Out-Null }
|
|
if (-not (Test-Path -Path $modelPath)) { New-Item -Path $modelPath -ItemType Directory -Force | Out-Null }
|
|
if (-not (Test-Path -Path $tempDownloadPath)) { New-Item -Path $tempDownloadPath -ItemType Directory -Force | Out-Null }
|
|
|
|
# 2. Construct and Download Catalog URL
|
|
$modelRelease = $machineType + "_Win" + $WindowsRelease
|
|
$catalogUrl = "https://download.lenovo.com/catalog/$modelRelease.xml"
|
|
$lenovoCatalogXML = Join-Path -Path $tempDownloadPath -ChildPath "$modelRelease.xml"
|
|
|
|
$status = "Downloading Catalog..."
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
WriteLog "Downloading Lenovo Driver catalog for '$identifier' from $catalogUrl"
|
|
|
|
# Check URL accessibility first
|
|
try {
|
|
$request = [System.Net.WebRequest]::Create($catalogUrl); $request.Method = 'HEAD'; $response = $request.GetResponse(); $response.Close()
|
|
}
|
|
catch { throw "Lenovo Driver catalog URL is not accessible: $catalogUrl. Error: $($_.Exception.Message)" }
|
|
|
|
Start-BitsTransferWithRetry -Source $catalogUrl -Destination $lenovoCatalogXML
|
|
WriteLog "Catalog download Complete: $lenovoCatalogXML"
|
|
|
|
# 3. Parse Catalog and Process Packages
|
|
$status = "Parsing Catalog..."
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
[xml]$xmlContent = Get-Content -Path $lenovoCatalogXML -Encoding UTF8
|
|
|
|
$packages = @($xmlContent.packages.package) # Ensure it's an array
|
|
$totalPackages = $packages.Count
|
|
$processedPackages = 0
|
|
WriteLog "Found $totalPackages packages in catalog for '$identifier'."
|
|
|
|
foreach ($package in $packages) {
|
|
$processedPackages++
|
|
$category = $package.category
|
|
$packageUrl = $package.location # URL to the package's *XML* file
|
|
|
|
# Skip BIOS/Firmware based on category
|
|
if ($category -like 'BIOS*' -or $category -like 'Firmware*') {
|
|
WriteLog "($processedPackages/$totalPackages) Skipping BIOS/Firmware package: $category"
|
|
continue
|
|
}
|
|
|
|
# Sanitize category for path
|
|
$categoryClean = $category -replace '[\\/:"*?<>|]', '_'
|
|
if ($categoryClean -eq 'Motherboard Devices Backplanes core chipset onboard video PCIe switches') {
|
|
$categoryClean = 'Motherboard Devices' # Shorten long category name
|
|
}
|
|
|
|
$packageName = [System.IO.Path]::GetFileName($packageUrl)
|
|
$packageXMLPath = Join-Path -Path $tempDownloadPath -ChildPath $packageName
|
|
$baseURL = $packageUrl -replace [regex]::Escape($packageName), "" # Base URL for the driver file
|
|
|
|
# Download the package XML
|
|
WriteLog "($processedPackages/$totalPackages) Downloading package XML: $packageUrl"
|
|
try {
|
|
Start-BitsTransferWithRetry -Source $packageUrl -Destination $packageXMLPath
|
|
}
|
|
catch {
|
|
$failureMessage = "Failed to download Lenovo package XML '$packageUrl': $($_.Exception.Message)"
|
|
WriteLog "($processedPackages/$totalPackages) $failureMessage"
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue
|
|
throw (New-Object System.Exception($failureMessage, $_.Exception))
|
|
}
|
|
|
|
# Load and parse the package XML
|
|
[xml]$packageXmlContent = Get-Content -Path $packageXMLPath -Encoding UTF8
|
|
$packageType = $packageXmlContent.Package.PackageType.type
|
|
$packageTitleRaw = $packageXmlContent.Package.title.InnerText
|
|
|
|
# Filter out non-driver packages (Type 2 = Driver)
|
|
if ($packageType -ne 2) {
|
|
WriteLog "($processedPackages/$totalPackages) Skipping package '$packageTitleRaw' (Type: $packageType) - Not a driver."
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue # Clean up package XML
|
|
continue
|
|
}
|
|
|
|
# Sanitize title for folder name
|
|
$packageTitle = $packageTitleRaw -replace '[\\/:"*?<>|]', '_' -replace ' - .*', ''
|
|
|
|
# Extract driver file name and extract command
|
|
$driverFileName = $null
|
|
$extractCommand = $null
|
|
try {
|
|
$driverFileName = $packageXmlContent.Package.Files.Installer.File.Name
|
|
$extractCommand = $packageXmlContent.Package.ExtractCommand
|
|
}
|
|
catch {
|
|
WriteLog "($processedPackages/$totalPackages) Error parsing package XML '$packageXMLPath' for file name/command. Skipping. Error: $($_.Exception.Message)"
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue
|
|
continue
|
|
}
|
|
|
|
|
|
# Skip if essential info is missing
|
|
if ([string]::IsNullOrWhiteSpace($driverFileName) -or [string]::IsNullOrWhiteSpace($extractCommand)) {
|
|
WriteLog "($processedPackages/$totalPackages) Skipping package '$packageTitleRaw' - Missing driver file name or extract command in XML."
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue
|
|
continue
|
|
}
|
|
|
|
# Construct paths
|
|
$driverUrl = $baseURL + $driverFileName
|
|
$categoryPath = Join-Path -Path $modelPath -ChildPath $categoryClean
|
|
$downloadFolder = Join-Path -Path $categoryPath -ChildPath $packageTitle # Final destination subfolder
|
|
$driverFilePath = Join-Path -Path $downloadFolder -ChildPath $driverFileName
|
|
$extractFolder = Join-Path -Path $downloadFolder -ChildPath ($driverFileName -replace '\.exe$', '') # Extract to subfolder named after exe
|
|
# Check if already extracted
|
|
if (Test-Path -Path $extractFolder -PathType Container) {
|
|
$extractSize = (Get-ChildItem -Path $extractFolder -Recurse | Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
|
|
if ($extractSize -gt 1KB) {
|
|
WriteLog "($processedPackages/$totalPackages) Driver '$packageTitleRaw' already extracted to '$extractFolder'. Skipping."
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue # Clean up package XML
|
|
continue
|
|
}
|
|
}
|
|
|
|
# Ensure download folder exists
|
|
if (-not (Test-Path -Path $downloadFolder)) {
|
|
New-Item -Path $downloadFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
# Download the driver .exe
|
|
$status = "$processedPackages/$totalPackages Downloading $packageTitle"
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
WriteLog "($processedPackages/$totalPackages) Downloading driver: $driverUrl to $driverFilePath"
|
|
try {
|
|
Start-BitsTransferWithRetry -Source $driverUrl -Destination $driverFilePath
|
|
WriteLog "($processedPackages/$totalPackages) Driver downloaded: $driverFileName"
|
|
}
|
|
catch {
|
|
$failureMessage = "Failed to download driver '$packageTitle' from $($driverUrl): $($_.Exception.Message)"
|
|
WriteLog "($processedPackages/$totalPackages) $failureMessage"
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue
|
|
throw (New-Object System.Exception($failureMessage, $_.Exception))
|
|
}
|
|
|
|
# --- Extraction Logic ---
|
|
$status = "$processedPackages/$totalPackages Extracting $packageTitle"
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
|
|
# Always use a temporary extraction path to avoid long path issues
|
|
$originalExtractFolder = $extractFolder # Store the originally intended final path
|
|
$extractionSucceeded = $false
|
|
$tempExtractBase = $null # Initialize
|
|
|
|
# Create randomized number for use with temp folder name
|
|
$randomNumber = Get-Random -Minimum 1000 -Maximum 9999
|
|
$tempExtractBase = Join-Path $env:TEMP "LenovoDriverExtract_$randomNumber"
|
|
$extractFolder = Join-Path $tempExtractBase ($driverFileName -replace '\.exe$', '') # Actual temp extraction folder
|
|
WriteLog "($processedPackages/$totalPackages) Using temporary extraction path: $extractFolder"
|
|
|
|
# Ensure the base temp directory exists
|
|
if (-not (Test-Path -Path $tempExtractBase)) {
|
|
New-Item -Path $tempExtractBase -ItemType Directory -Force | Out-Null
|
|
}
|
|
# Ensure the target temporary extraction folder exists
|
|
if (-not (Test-Path -Path $extractFolder)) {
|
|
New-Item -Path $extractFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
# Modify the extract command to point to the temporary folder
|
|
$modifiedExtractCommand = $extractCommand -replace '%PACKAGEPATH%', "`"$extractFolder`""
|
|
WriteLog "$processedPackages/$totalPackages Extracting driver: $driverFilePath using command: $modifiedExtractCommand"
|
|
|
|
try {
|
|
Invoke-Process -FilePath $driverFilePath -ArgumentList $modifiedExtractCommand -Wait $true | Out-Null
|
|
WriteLog "($processedPackages/$totalPackages) Driver extracted to temporary path: $extractFolder"
|
|
$extractionSucceeded = $true
|
|
}
|
|
catch {
|
|
$failureMessage = "Failed to extract driver package '$packageTitle': $($_.Exception.Message)"
|
|
WriteLog "($processedPackages/$totalPackages) $failureMessage"
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue
|
|
if ($tempExtractBase -and (Test-Path -Path $tempExtractBase)) {
|
|
Remove-Item -Path $tempExtractBase -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
throw (New-Object System.Exception($failureMessage, $_.Exception))
|
|
}
|
|
|
|
# --- Post-Extraction Handling (Move from Temp to Final Destination) ---
|
|
if ($extractionSucceeded) {
|
|
WriteLog "($processedPackages/$totalPackages) Performing post-extraction move from temp to final destination..."
|
|
try {
|
|
# Ensure the *original* final destination folder exists and is empty
|
|
if (Test-Path -Path $originalExtractFolder) {
|
|
WriteLog "($processedPackages/$totalPackages) Clearing existing final destination folder: $originalExtractFolder"
|
|
Get-ChildItem -Path $originalExtractFolder -Recurse | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
WriteLog "($processedPackages/$totalPackages) Creating final destination folder: $originalExtractFolder"
|
|
New-Item -Path $originalExtractFolder -ItemType Directory -Force | Out-Null
|
|
}
|
|
|
|
# Get all items (files and folders) directly inside the temp extraction folder
|
|
$extractedItems = Get-ChildItem -Path $extractFolder -ErrorAction Stop
|
|
|
|
foreach ($item in $extractedItems) {
|
|
$itemName = $item.Name
|
|
$finalDestinationPath = $null
|
|
|
|
# Check if it's a directory containing 'Liteon'
|
|
if ($item.PSIsContainer -and $itemName -like '*Liteon*') {
|
|
# Rename Liteon folders with a random number suffix
|
|
$randomNumber = Get-Random -Minimum 1000 -Maximum 9999
|
|
$finalFolderName = "Liteon_$randomNumber"
|
|
$finalDestinationPath = Join-Path -Path $originalExtractFolder -ChildPath $finalFolderName
|
|
WriteLog "($processedPackages/$totalPackages) Moving Liteon folder '$itemName' to '$finalDestinationPath'"
|
|
}
|
|
else {
|
|
# For other files/folders, move them directly
|
|
$finalDestinationPath = Join-Path -Path $originalExtractFolder -ChildPath $itemName
|
|
WriteLog "($processedPackages/$totalPackages) Moving item '$itemName' to '$finalDestinationPath'"
|
|
}
|
|
|
|
# Perform the move
|
|
try {
|
|
Move-Item -Path $item.FullName -Destination $finalDestinationPath -Force -ErrorAction Stop
|
|
}
|
|
catch {
|
|
$failureMessage = "Failed to move extracted item '$($item.FullName)' to '$finalDestinationPath': $($_.Exception.Message)"
|
|
WriteLog "($processedPackages/$totalPackages) $failureMessage"
|
|
throw (New-Object System.Exception($failureMessage, $_.Exception))
|
|
}
|
|
} # End foreach ($item in $extractedItems)
|
|
|
|
if ($extractionSucceeded) {
|
|
WriteLog "($processedPackages/$totalPackages) All driver contents moved successfully from temp to final destination."
|
|
}
|
|
else {
|
|
WriteLog "($processedPackages/$totalPackages) Some driver contents failed to move. Check logs."
|
|
}
|
|
|
|
}
|
|
catch {
|
|
WriteLog "($processedPackages/$totalPackages) Error during post-extraction move: $($_.Exception.Message). Files might remain in temp."
|
|
$extractionSucceeded = $false # Mark as failed for cleanup logic below
|
|
}
|
|
finally {
|
|
# Clean up the base temporary directory regardless of move success/failure
|
|
if ($tempExtractBase -and (Test-Path -Path $tempExtractBase)) {
|
|
WriteLog "($processedPackages/$totalPackages) Cleaning up temporary extraction base: $tempExtractBase"
|
|
Remove-Item -Path $tempExtractBase -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
}
|
|
|
|
# --- Final Cleanup ---
|
|
# Delete the downloaded .exe only if extraction AND move were successful
|
|
if ($extractionSucceeded) {
|
|
WriteLog "($processedPackages/$totalPackages) Deleting driver installation file: $driverFilePath"
|
|
Remove-Item -Path $driverFilePath -Force -ErrorAction SilentlyContinue
|
|
}
|
|
else {
|
|
WriteLog "($processedPackages/$totalPackages) Keeping driver installation file due to extraction/move failure: $driverFilePath"
|
|
}
|
|
# Always delete the package XML
|
|
WriteLog "($processedPackages/$totalPackages) Deleting package XML file: $packageXMLPath"
|
|
Remove-Item -Path $packageXMLPath -Force -ErrorAction SilentlyContinue
|
|
if (-not $extractionSucceeded) {
|
|
throw (New-Object System.Exception("Failed to extract driver '$packageTitle'. See log for details."))
|
|
}
|
|
|
|
} # End foreach package
|
|
|
|
# --- Compress to WIM if requested (after all drivers processed) ---
|
|
if ($CompressToWim) {
|
|
$status = "Compressing..."
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
$wimFileName = "$($sanitizedIdentifier).wim" # Use sanitized identifier for filename
|
|
$destinationWimPath = Join-Path -Path $makeDriversPath -ChildPath $wimFileName
|
|
$driverRelativePath = Join-Path -Path $make -ChildPath $wimFileName # Update relative path to the WIM file
|
|
WriteLog "Compressing '$modelPath' to '$destinationWimPath'..."
|
|
try {
|
|
$compressResult = Compress-DriverFolderToWim -SourceFolderPath $modelPath -DestinationWimPath $destinationWimPath -WimName $identifier -WimDescription $identifier -PreserveSource:$PreserveSourceOnCompress -ErrorAction Stop
|
|
if ($compressResult) {
|
|
WriteLog "Compression successful for '$identifier'."
|
|
$status = "Completed & Compressed"
|
|
}
|
|
else {
|
|
WriteLog "Compression failed for '$identifier'. Check verbose/error output from Compress-DriverFolderToWim."
|
|
$status = "Completed (Compression Failed)"
|
|
}
|
|
}
|
|
catch {
|
|
WriteLog "Error during compression for '$identifier': $($_.Exception.Message)"
|
|
$status = "Completed (Compression Error)"
|
|
}
|
|
}
|
|
else {
|
|
$status = "Completed"
|
|
}
|
|
# --- End Compression ---
|
|
|
|
$success = $true # Mark success as download/extract was okay
|
|
|
|
}
|
|
catch {
|
|
$status = "Error: $($_.Exception.Message)"
|
|
WriteLog "Error saving Lenovo drivers for '$identifier': $($_.Exception.ToString())"
|
|
$success = $false
|
|
Remove-DriverModelFolder -DriversFolder $DriversFolder -TargetFolder $modelPath -Description $identifier
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $success; DriverPath = $null }
|
|
}
|
|
finally {
|
|
# Clean up the main catalog XML and temp folder
|
|
WriteLog "Cleaning up temporary download folder: $tempDownloadPath"
|
|
Remove-Item -Path $tempDownloadPath -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
# Enqueue the final status (success or error) before returning
|
|
if ($null -ne $ProgressQueue) { Invoke-ProgressUpdate -ProgressQueue $ProgressQueue -Identifier $identifier -Status $status }
|
|
|
|
# Return the final status
|
|
return [PSCustomObject]@{ Identifier = $identifier; Status = $status; Success = $success; DriverPath = $driverRelativePath }
|
|
}
|
|
|
|
Export-ModuleMember -Function * |