Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $BaseUrl = "https://shansnow.rat-smelt.ts.net" $WorkDir = Join-Path $env:TEMP "OfficeODT" $Setup = Join-Path $WorkDir "setup.exe" $Xml = Join-Path $WorkDir "MSO2024.xml" # ---------- Console helpers ---------- $spin = @('|','/','-','\') function Write-Top([string]$msg){ Write-Host "" Write-Host "===============================" -ForegroundColor DarkGray Write-Host " MS Office Online Installer" -ForegroundColor Cyan Write-Host "===============================" -ForegroundColor DarkGray Write-Host $msg -ForegroundColor Yellow } function StatusLine([string]$text){ Write-Host ("`r" + $text.PadRight([Math]::Max(60, $text.Length))) -NoNewline } # ---------- Download with animation + progress ---------- function Download-Pretty { param( [Parameter(Mandatory)] [string]$Url, [Parameter(Mandatory)] [string]$OutFile, [Parameter(Mandatory)] [string]$Label ) # Clean old file if (Test-Path $OutFile) { Remove-Item $OutFile -Force -ErrorAction SilentlyContinue } $wc = New-Object System.Net.WebClient $progress = [pscustomobject]@{ Percent = 0 Bytes = 0 Total = 0 Done = $false Error = $null } $wc.DownloadProgressChanged += { $progress.Percent = $_.ProgressPercentage $progress.Bytes = $_.BytesReceived $progress.Total = $_.TotalBytesToReceive } $wc.DownloadFileCompleted += { if ($_.Error) { $progress.Error = $_.Error } $progress.Done = $true } Write-Host "" Write-Host $Label -ForegroundColor Yellow $wc.DownloadFileAsync([Uri]$Url, $OutFile) $i = 0 $t0 = Get-Date while (-not $progress.Done) { $i = ($i + 1) % $spin.Count $elapsed = [int]((Get-Date) - $t0).TotalSeconds if ($progress.Total -gt 0) { $mbR = [Math]::Round($progress.Bytes/1MB,2) $mbT = [Math]::Round($progress.Total/1MB,2) StatusLine (" {0} {1,3}% ({2} / {3} MB) [{4}s]" -f $spin[$i], $progress.Percent, $mbR, $mbT, $elapsed) } else { $mbR = [Math]::Round($progress.Bytes/1MB,2) StatusLine (" {0} {1} MB [{2}s]" -f $spin[$i], $mbR, $elapsed) } Start-Sleep -Milliseconds 120 } if ($progress.Error) { Write-Host "" throw $progress.Error } Write-Host "`r ✓ Done".PadRight(60) -ForegroundColor Green if (!(Test-Path $OutFile) -or (Get-Item $OutFile).Length -lt 1024) { throw "Download failed or file too small: $OutFile" } } # ---------- Install animation ---------- function Wait-Install { Write-Host "" Write-Host "INSTALLING..." -ForegroundColor Yellow $t0 = Get-Date $dot = 0 while (Get-Process -Name setup -ErrorAction SilentlyContinue) { $dot = ($dot + 1) % 4 $elapsed = New-TimeSpan -Start $t0 -End (Get-Date) $mmss = "{0:00}:{1:00}" -f [int]$elapsed.TotalMinutes, $elapsed.Seconds # blinking dots + spinner $dots = "." * $dot StatusLine (" {0} Installing{1} [elapsed {2}]" -f $spin[$dot], $dots, $mmss) Start-Sleep -Milliseconds 350 } Write-Host "`r ✓ Install finished".PadRight(60) -ForegroundColor Green } # ---------- MAIN ---------- Write-Top "Preparing..." New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null $setupUrl = ($BaseUrl.TrimEnd("/") + "/setup.exe") $xmlUrl = ($BaseUrl.TrimEnd("/") + "/MSO2024.xml") Write-Host "" Write-Host "DOWNLOADING..." -ForegroundColor Yellow Download-Pretty -Url $setupUrl -OutFile $Setup -Label "1) setup.exe" Download-Pretty -Url $xmlUrl -OutFile $Xml -Label "2) MSO2024.xml" # Start install (ODT) Start-Process -FilePath $Setup -ArgumentList "/configure `"$Xml`"" | Out-Null Wait-Install Write-Host "" Write-Host "DONE ✅" -ForegroundColor Green Write-Host ("Saved in: {0}" -f $WorkDir) -ForegroundColor DarkGray