mirror of
https://github.com/SpotX-Official/SpotX.git
synced 2026-04-20 10:44:35 +10:00
389 lines
11 KiB
PowerShell
389 lines
11 KiB
PowerShell
$rawScriptUrl = 'https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1'
|
|
$mirrorScriptUrl = 'https://spotx-official.github.io/SpotX/run.ps1'
|
|
$downloadHost = 'loadspot.amd64fox1.workers.dev'
|
|
$defaultLatestFull = '1.2.86.502.g8cd7fb22'
|
|
$stableFull = '1.2.13.661.ga588f749'
|
|
|
|
$reportLines = New-Object 'System.Collections.Generic.List[string]'
|
|
$sensitivePatterns = New-Object 'System.Collections.Generic.List[object]'
|
|
|
|
function Add-SensitivePattern {
|
|
param(
|
|
[string]$Pattern,
|
|
[string]$Replacement = '[redacted]'
|
|
)
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($Pattern)) {
|
|
$script:sensitivePatterns.Add([PSCustomObject]@{
|
|
Pattern = $Pattern
|
|
Replacement = $Replacement
|
|
}) | Out-Null
|
|
}
|
|
}
|
|
|
|
function Protect-DiagnosticText {
|
|
param(
|
|
[AllowNull()]
|
|
[string]$Text
|
|
)
|
|
|
|
if ($null -eq $Text) {
|
|
return $null
|
|
}
|
|
|
|
$value = [string]$Text
|
|
|
|
foreach ($item in $script:sensitivePatterns) {
|
|
$value = [regex]::Replace($value, $item.Pattern, $item.Replacement)
|
|
}
|
|
|
|
$value
|
|
}
|
|
|
|
function Add-ReportLine {
|
|
param(
|
|
[AllowEmptyString()]
|
|
[string]$Line = ''
|
|
)
|
|
|
|
[void]$script:reportLines.Add((Protect-DiagnosticText -Text $Line))
|
|
}
|
|
|
|
function Add-ReportSection {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Title
|
|
)
|
|
|
|
if ($script:reportLines.Count -gt 0) {
|
|
Add-ReportLine
|
|
}
|
|
|
|
Add-ReportLine ("== {0} ==" -f $Title)
|
|
}
|
|
|
|
function Add-CommandOutput {
|
|
param(
|
|
[string[]]$Lines
|
|
)
|
|
|
|
foreach ($line in $Lines) {
|
|
Add-ReportLine ([string]$line)
|
|
}
|
|
}
|
|
|
|
function Invoke-WebRequestCompat {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Uri
|
|
)
|
|
|
|
$params = @{
|
|
Uri = $Uri
|
|
ErrorAction = 'Stop'
|
|
}
|
|
|
|
if ($PSVersionTable.PSVersion.Major -lt 6) {
|
|
$params.UseBasicParsing = $true
|
|
}
|
|
|
|
Invoke-WebRequest @params
|
|
}
|
|
|
|
function Invoke-WebClientStep {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Title,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Uri
|
|
)
|
|
|
|
Invoke-PowerShellStep -Title $Title -Action {
|
|
$wc = New-Object System.Net.WebClient
|
|
$stream = $null
|
|
|
|
try {
|
|
$stream = $wc.OpenRead($Uri)
|
|
$buffer = New-Object byte[] 1
|
|
[void]$stream.Read($buffer, 0, 1)
|
|
|
|
$lines = New-Object 'System.Collections.Generic.List[string]'
|
|
$lines.Add('WEBCLIENT_OK') | Out-Null
|
|
$lines.Add(("Url: {0}" -f $Uri)) | Out-Null
|
|
|
|
if ($wc.ResponseHeaders) {
|
|
foreach ($headerName in $wc.ResponseHeaders.AllKeys) {
|
|
$lines.Add(("{0}: {1}" -f $headerName, $wc.ResponseHeaders[$headerName])) | Out-Null
|
|
}
|
|
}
|
|
|
|
$lines
|
|
}
|
|
finally {
|
|
if ($stream) {
|
|
$stream.Dispose()
|
|
}
|
|
|
|
$wc.Dispose()
|
|
}
|
|
}
|
|
}
|
|
|
|
function Copy-TextToClipboard {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Text
|
|
)
|
|
|
|
try {
|
|
if (Get-Command Set-Clipboard -ErrorAction SilentlyContinue) {
|
|
Set-Clipboard -Value $Text -ErrorAction Stop
|
|
return $true
|
|
}
|
|
|
|
if (Get-Command clip.exe -ErrorAction SilentlyContinue) {
|
|
$Text | clip.exe
|
|
return ($LASTEXITCODE -eq 0)
|
|
}
|
|
}
|
|
catch {
|
|
return $false
|
|
}
|
|
|
|
return $false
|
|
}
|
|
|
|
function Get-DiagnosticArchitecture {
|
|
$arch = $env:PROCESSOR_ARCHITEW6432
|
|
if ([string]::IsNullOrWhiteSpace($arch)) {
|
|
$arch = $env:PROCESSOR_ARCHITECTURE
|
|
}
|
|
|
|
switch -Regex ($arch) {
|
|
'ARM64' { return 'arm64' }
|
|
'64' { return 'x64' }
|
|
default { return 'x86' }
|
|
}
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($env:COMPUTERNAME)) {
|
|
Add-SensitivePattern -Pattern ([regex]::Escape($env:COMPUTERNAME)) -Replacement '[redacted-host]'
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($env:USERNAME)) {
|
|
Add-SensitivePattern -Pattern ([regex]::Escape($env:USERNAME)) -Replacement '[redacted-user]'
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($env:USERPROFILE)) {
|
|
Add-SensitivePattern -Pattern ([regex]::Escape($env:USERPROFILE)) -Replacement '[redacted-profile]'
|
|
}
|
|
|
|
Add-SensitivePattern -Pattern '(?<![\d.])(?:\d{1,3}\.){3}\d{1,3}(?![\d.])' -Replacement '[redacted-ipv4]'
|
|
Add-SensitivePattern -Pattern '(?i)(?<![0-9a-f:])((?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?:(?::[0-9a-f]{1,4}){1,6})|:(?:(?::[0-9a-f]{1,4}){1,7}|:))(?![0-9a-f:])' -Replacement '[redacted-ipv6]'
|
|
|
|
function Format-ExceptionDetails {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[System.Exception]$Exception
|
|
)
|
|
|
|
$details = New-Object 'System.Collections.Generic.List[string]'
|
|
$current = $Exception
|
|
|
|
while ($null -ne $current) {
|
|
[void]$details.Add($current.Message)
|
|
$current = $current.InnerException
|
|
}
|
|
|
|
$details
|
|
}
|
|
|
|
function Invoke-CurlStep {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Title,
|
|
[Parameter(Mandatory = $true)]
|
|
[string[]]$Arguments
|
|
)
|
|
|
|
Add-ReportSection -Title $Title
|
|
|
|
if (-not (Get-Command curl.exe -ErrorAction SilentlyContinue)) {
|
|
Add-ReportLine 'curl.exe not found'
|
|
return
|
|
}
|
|
|
|
try {
|
|
$output = & curl.exe '-sS' @Arguments 2>&1
|
|
Add-CommandOutput -Lines ($output | ForEach-Object { [string]$_ })
|
|
Add-ReportLine ("ExitCode: {0}" -f $LASTEXITCODE)
|
|
}
|
|
catch {
|
|
Add-CommandOutput -Lines (Format-ExceptionDetails -Exception $_.Exception)
|
|
}
|
|
}
|
|
|
|
function Invoke-PowerShellStep {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Title,
|
|
[Parameter(Mandatory = $true)]
|
|
[scriptblock]$Action
|
|
)
|
|
|
|
Add-ReportSection -Title $Title
|
|
|
|
try {
|
|
$result = & $Action
|
|
|
|
if ($null -eq $result) {
|
|
Add-ReportLine
|
|
return
|
|
}
|
|
|
|
if ($result -is [System.Array]) {
|
|
Add-CommandOutput -Lines ($result | ForEach-Object { [string]$_ })
|
|
return
|
|
}
|
|
|
|
Add-ReportLine ([string]$result)
|
|
}
|
|
catch {
|
|
Add-CommandOutput -Lines (Format-ExceptionDetails -Exception $_.Exception)
|
|
}
|
|
}
|
|
|
|
$architecture = Get-DiagnosticArchitecture
|
|
$latestFull = $defaultLatestFull
|
|
$bootstrapResults = New-Object 'System.Collections.Generic.List[object]'
|
|
|
|
foreach ($source in @(
|
|
[PSCustomObject]@{ Name = 'raw'; Url = $rawScriptUrl },
|
|
[PSCustomObject]@{ Name = 'mirror'; Url = $mirrorScriptUrl }
|
|
)) {
|
|
try {
|
|
$response = Invoke-WebRequestCompat -Uri $source.Url
|
|
$content = [string]$response.Content
|
|
|
|
if ($latestFull -eq $defaultLatestFull) {
|
|
$match = [regex]::Match($content, '\[string\]\$latest_full\s*=\s*"([^"]+)"')
|
|
if ($match.Success) {
|
|
$latestFull = $match.Groups[1].Value
|
|
}
|
|
}
|
|
|
|
$bootstrapResults.Add([PSCustomObject]@{
|
|
Name = $source.Name
|
|
Url = $source.Url
|
|
Success = $true
|
|
StatusCode = [int]$response.StatusCode
|
|
Length = $content.Length
|
|
}) | Out-Null
|
|
}
|
|
catch {
|
|
$bootstrapResults.Add([PSCustomObject]@{
|
|
Name = $source.Name
|
|
Url = $source.Url
|
|
Success = $false
|
|
StatusCode = $null
|
|
Length = $null
|
|
Error = $_.Exception.Message
|
|
}) | Out-Null
|
|
}
|
|
}
|
|
|
|
$tempUrl = if ($architecture -eq 'x64') {
|
|
'https://{0}/temporary-download/spotify_installer-{1}-x64.exe' -f $downloadHost, $latestFull
|
|
}
|
|
else {
|
|
$null
|
|
}
|
|
|
|
$directUrl = 'https://{0}/download/spotify_installer-{1}-{2}.exe' -f $downloadHost, $stableFull, $architecture
|
|
|
|
Add-ReportSection -Title 'Environment'
|
|
Add-ReportLine ("Date: {0}" -f (Get-Date).ToString('yyyy-MM-dd HH:mm:ss zzz'))
|
|
Add-ReportLine ("ComputerName: {0}" -f $env:COMPUTERNAME)
|
|
Add-ReportLine ("UserName: {0}" -f $env:USERNAME)
|
|
Add-ReportLine ("PowerShell: {0}" -f $PSVersionTable.PSVersion)
|
|
Add-ReportLine ("Architecture: {0}" -f $architecture)
|
|
Add-ReportLine ("LatestFull: {0}" -f $latestFull)
|
|
Add-ReportLine ("StableFull: {0}" -f $stableFull)
|
|
Add-ReportLine ("TempUrl: {0}" -f $(if ($tempUrl) { $tempUrl } else { 'skipped for non-x64 system' }))
|
|
Add-ReportLine ("DirectUrl: {0}" -f $directUrl)
|
|
|
|
Add-ReportSection -Title 'Bootstrap check'
|
|
foreach ($item in $bootstrapResults) {
|
|
Add-ReportLine ("Source: {0}" -f $item.Name)
|
|
Add-ReportLine ("Url: {0}" -f $item.Url)
|
|
Add-ReportLine ("Success: {0}" -f $item.Success)
|
|
|
|
if ($item.Success) {
|
|
Add-ReportLine ("StatusCode: {0}" -f $item.StatusCode)
|
|
Add-ReportLine ("ContentLength: {0}" -f $item.Length)
|
|
}
|
|
else {
|
|
Add-ReportLine ("Error: {0}" -f $item.Error)
|
|
}
|
|
|
|
Add-ReportLine
|
|
}
|
|
|
|
Invoke-CurlStep -Title 'curl version' -Arguments @('-V')
|
|
|
|
Invoke-PowerShellStep -Title 'DNS' -Action {
|
|
Resolve-DnsName $downloadHost | Format-Table -AutoSize | Out-String -Width 4096
|
|
}
|
|
|
|
if ($tempUrl) {
|
|
Invoke-CurlStep -Title 'HEAD temp' -Arguments @('-I', '-L', '-k', '--ssl-no-revoke', $tempUrl)
|
|
Invoke-CurlStep -Title '1MB temp' -Arguments @('-L', '-k', '--ssl-no-revoke', '--fail-with-body', '--connect-timeout', '15', '-r', '0-1048575', '-o', 'NUL', '-D', '-', '-w', "`nHTTP_STATUS:%{http_code}`n", $tempUrl)
|
|
}
|
|
else {
|
|
Add-ReportSection -Title 'HEAD temp'
|
|
Add-ReportLine 'Skipped because temporary route is only used for x64 latest build'
|
|
|
|
Add-ReportSection -Title '1MB temp'
|
|
Add-ReportLine 'Skipped because temporary route is only used for x64 latest build'
|
|
}
|
|
|
|
Invoke-CurlStep -Title 'HEAD direct stable' -Arguments @('-I', '-L', '-k', '--ssl-no-revoke', $directUrl)
|
|
Invoke-CurlStep -Title '1MB direct stable' -Arguments @('-L', '-k', '--ssl-no-revoke', '--fail-with-body', '--connect-timeout', '15', '-r', '0-1048575', '-o', 'NUL', '-D', '-', '-w', "`nHTTP_STATUS:%{http_code}`n", $directUrl)
|
|
|
|
if ($tempUrl) {
|
|
Invoke-WebClientStep -Title 'WebClient temp' -Uri $tempUrl
|
|
}
|
|
else {
|
|
Add-ReportSection -Title 'WebClient temp'
|
|
Add-ReportLine 'Skipped because temporary route is only used for x64 latest build'
|
|
}
|
|
|
|
Invoke-WebClientStep -Title 'WebClient direct stable' -Uri $directUrl
|
|
|
|
$finalOutputLines = New-Object 'System.Collections.Generic.List[string]'
|
|
$finalOutputLines.Add('----- BEGIN DIAGNOSTICS -----') | Out-Null
|
|
|
|
foreach ($line in $reportLines) {
|
|
$finalOutputLines.Add($line) | Out-Null
|
|
}
|
|
|
|
$finalOutputLines.Add('----- END DIAGNOSTICS -----') | Out-Null
|
|
$finalOutputText = [string]::Join([Environment]::NewLine, $finalOutputLines)
|
|
$clipboardCopied = Copy-TextToClipboard -Text $finalOutputText
|
|
|
|
Write-Host
|
|
if ($clipboardCopied) {
|
|
Write-Host 'Diagnostics copied to clipboard' -ForegroundColor Green
|
|
}
|
|
else {
|
|
Write-Host 'Clipboard copy failed, copy the text below manually' -ForegroundColor Yellow
|
|
}
|
|
|
|
Write-Host '----- BEGIN DIAGNOSTICS -----' -ForegroundColor Cyan
|
|
|
|
foreach ($line in $reportLines) {
|
|
Write-Output $line
|
|
}
|
|
|
|
Write-Host '----- END DIAGNOSTICS -----' -ForegroundColor Cyan
|