[CmdletBinding()] param ( [Parameter(HelpMessage = "Change recommended version of Spotify.")] [Alias("v")] [string]$version, [Parameter(HelpMessage = "Use github.io mirror instead of raw.githubusercontent.")] [Alias("m")] [switch]$mirror, [Parameter(HelpMessage = "Developer mode activation.")] [Alias("dev")] [switch]$devtools, [Parameter(HelpMessage = 'Disable podcasts/episodes/audiobooks from homepage.')] [switch]$podcasts_off, [Parameter(HelpMessage = 'Disable Ad-like sections from homepage')] [switch]$adsections_off, [Parameter(HelpMessage = 'Disable canvas from homepage')] [switch]$canvashome_off, [Parameter(HelpMessage = 'Do not disable podcasts/episodes/audiobooks from homepage.')] [switch]$podcasts_on, [Parameter(HelpMessage = 'Block Spotify automatic updates.')] [switch]$block_update_on, [Parameter(HelpMessage = 'Do not block Spotify automatic updates.')] [switch]$block_update_off, [Parameter(HelpMessage = 'Change limit for clearing audio cache.')] [Alias('cl')] [int]$cache_limit, [Parameter(HelpMessage = 'Automatic uninstallation of Spotify MS if it was found.')] [switch]$confirm_uninstall_ms_spoti, [Parameter(HelpMessage = 'Overwrite outdated or unsupported version of Spotify with the recommended version.')] [Alias('sp-over')] [switch]$confirm_spoti_recomended_over, [Parameter(HelpMessage = 'Uninstall outdated or unsupported version of Spotify and install the recommended version.')] [Alias('sp-uninstall')] [switch]$confirm_spoti_recomended_uninstall, [Parameter(HelpMessage = 'Installation without ad blocking for premium accounts.')] [switch]$premium, [Parameter(HelpMessage = 'Disable Spotify autostart on Windows boot.')] [switch]$DisableStartup, [Parameter(HelpMessage = 'Automatic launch of Spotify after installation is complete.')] [switch]$start_spoti, [Parameter(HelpMessage = 'Experimental features operated by Spotify.')] [switch]$exp_spotify, [Parameter(HelpMessage = 'Enable top search bar.')] [switch]$topsearchbar, [Parameter(HelpMessage = 'Enable new fullscreen mode (Experimental)')] [switch]$newFullscreenMode, [Parameter(HelpMessage = 'disable subfeed filter chips on home.')] [switch]$homesub_off, [Parameter(HelpMessage = 'Do not hide the icon of collaborations in playlists.')] [switch]$hide_col_icon_off, [Parameter(HelpMessage = 'Disable new right sidebar.')] [switch]$rightsidebar_off, [Parameter(HelpMessage = 'it`s killing the heart icon, you`re able to save and choose the destination for any song, playlist, or podcast')] [switch]$plus, [Parameter(HelpMessage = 'Enable funny progress bar.')] [switch]$funnyprogressBar, [Parameter(HelpMessage = 'New theme activated (new right and left sidebar, some cover change)')] [switch]$new_theme, [Parameter(HelpMessage = 'Enable right sidebar coloring to match cover color)')] [switch]$rightsidebarcolor, [Parameter(HelpMessage = 'Returns old lyrics')] [switch]$old_lyrics, [Parameter(HelpMessage = 'Disable native lyrics')] [switch]$lyrics_block, [Parameter(HelpMessage = 'Do not create desktop shortcut.')] [switch]$no_shortcut, [Parameter(HelpMessage = 'Static color for lyrics.')] [ArgumentCompleter({ param($cmd, $param, $wordToComplete) [array] $validValues = @('blue', 'blueberry', 'discord', 'drot', 'default', 'forest', 'fresh', 'github', 'lavender', 'orange', 'postlight', 'pumpkin', 'purple', 'radium', 'relish', 'red', 'sandbar', 'spotify', 'spotify#2', 'strawberry', 'turquoise', 'yellow', 'zing', 'pinkle', 'krux', 'royal', 'oceano') $validValues -like "*$wordToComplete*" })] [string]$lyrics_stat, [Parameter(HelpMessage = 'Accumulation of track listening history with Goofy.')] [string]$urlform_goofy = $null, [Parameter(HelpMessage = 'Accumulation of track listening history with Goofy.')] [string]$idbox_goofy = $null, [Parameter(HelpMessage = 'Error log ru string.')] [switch]$err_ru, [Parameter(HelpMessage = 'Select the desired language to use for installation. Default is the detected system language.')] [Alias('l')] [string]$language ) # Ignore errors from `Stop-Process` $PSDefaultParameterValues['Stop-Process:ErrorAction'] = [System.Management.Automation.ActionPreference]::SilentlyContinue function Format-LanguageCode { # Normalizes and confirms support of the selected language. [CmdletBinding()] [OutputType([string])] param ( [string]$LanguageCode ) $supportLanguages = @( 'be', 'bn', 'cs', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fil', 'fr', 'hi', 'hu', 'id', 'it', 'ja', 'ka', 'ko', 'lv', 'pl', 'pt', 'ro', 'ru', 'sk', 'sr', 'sr-Latn', 'sv', 'ta', 'tr', 'ua', 'vi', 'zh', 'zh-TW' ) # Trim the language code down to two letter code. switch -Regex ($LanguageCode) { '^be' { $returnCode = 'be' break } '^bn' { $returnCode = 'bn' break } '^cs' { $returnCode = 'cs' break } '^de' { $returnCode = 'de' break } '^el' { $returnCode = 'el' break } '^en' { $returnCode = 'en' break } '^es' { $returnCode = 'es' break } '^fa' { $returnCode = 'fa' break } '^fi$' { $returnCode = 'fi' break } '^fil' { $returnCode = 'fil' break } '^fr' { $returnCode = 'fr' break } '^hi' { $returnCode = 'hi' break } '^hu' { $returnCode = 'hu' break } '^id' { $returnCode = 'id' break } '^it' { $returnCode = 'it' break } '^ja' { $returnCode = 'ja' break } '^ka' { $returnCode = 'ka' break } '^ko' { $returnCode = 'ko' break } '^lv' { $returnCode = 'lv' break } '^pl' { $returnCode = 'pl' break } '^pt' { $returnCode = 'pt' break } '^ro' { $returnCode = 'ro' break } '^(ru|py)' { $returnCode = 'ru' break } '^sk' { $returnCode = 'sk' break } '^(sr|sr-Cyrl)$' { $returnCode = 'sr' break } '^sr-Latn' { $returnCode = 'sr-Latn' break } '^sv' { $returnCode = 'sv' break } '^ta' { $returnCode = 'ta' break } '^tr' { $returnCode = 'tr' break } '^ua' { $returnCode = 'ua' break } '^vi' { $returnCode = 'vi' break } '^(zh|zh-CN)$' { $returnCode = 'zh' break } '^zh-TW' { $returnCode = 'zh-TW' break } Default { $returnCode = $PSUICulture $long_code = $true break } } # Checking the long language code if ($long_code -and $returnCode -NotIn $supportLanguages) { if ($returnCode -match '-') { $intermediateCode = $returnCode.Substring(0, $returnCode.LastIndexOf('-')) if ($intermediateCode -in $supportLanguages) { $returnCode = $intermediateCode } else { $returnCode = $returnCode -split "-" | Select-Object -First 1 } } } if ($returnCode -NotIn $supportLanguages) { $returnCode = 'en' } return $returnCode } $spotifyDirectory = Join-Path $env:APPDATA 'Spotify' $spotifyDirectory2 = Join-Path $env:LOCALAPPDATA 'Spotify' $spotifyExecutable = Join-Path $spotifyDirectory 'Spotify.exe' $spotifyDll = Join-Path $spotifyDirectory 'Spotify.dll' $chrome_elf = Join-Path $spotifyDirectory 'chrome_elf.dll' $exe_bak = Join-Path $spotifyDirectory 'Spotify.bak' $dll_bak = Join-Path $spotifyDirectory 'Spotify.dll.bak' $chrome_elf_bak = Join-Path $spotifyDirectory 'chrome_elf.dll.bak' $spotifyUninstall = Join-Path ([System.IO.Path]::GetTempPath()) 'SpotifyUninstall.exe' $start_menu = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs\Spotify.lnk' $upgrade_client = $false # Check version Powershell $psv = $PSVersionTable.PSVersion.major if ($psv -ge 7) { Import-Module Appx -UseWindowsPowerShell -WarningAction:SilentlyContinue } # add Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; function Stop-Script { param( [string]$Message = ($lang).StopScript ) Write-Host $Message switch ($Host.Name) { "Windows PowerShell ISE Host" { pause break } default { Write-Host ($lang).PressAnyKey [void][System.Console]::ReadKey($true) break } } Exit } function Get-Link { param ( [Alias("e")] [string]$endlink ) switch ($mirror) { $true { return "https://spotx-official.github.io/SpotX" + $endlink } default { return "https://raw.githubusercontent.com/SpotX-Official/SpotX/main" + $endlink } } } function CallLang($clg) { $ProgressPreference = 'SilentlyContinue' try { $response = (iwr -Uri (Get-Link -e "/scripts/installer-lang/$clg.ps1") -UseBasicParsing).Content if ($mirror) { $response = [System.Text.Encoding]::UTF8.GetString($response) } Invoke-Expression $response } catch { Write-Host "Error loading $clg language" Pause Exit } } # Set language code for script. $langCode = Format-LanguageCode -LanguageCode $Language $lang = CallLang -clg $langCode Write-Host ($lang).Welcome Write-Host # Check version Windows $os = Get-CimInstance -ClassName "Win32_OperatingSystem" -ErrorAction SilentlyContinue if ($os) { $osCaption = $os.Caption } else { $osCaption = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName } $pattern = "\bWindows (7|8(\.1)?|10|11|12)\b" $reg = [regex]::Matches($osCaption, $pattern) $win_os = $reg.Value $win12 = $win_os -match "\windows 12\b" $win11 = $win_os -match "\windows 11\b" $win10 = $win_os -match "\windows 10\b" $win8_1 = $win_os -match "\windows 8.1\b" $win8 = $win_os -match "\windows 8\b" $win7 = $win_os -match "\windows 7\b" $match_v = "^\d+\.\d+\.\d+\.\d+\.g[0-9a-f]{8}-\d+$" if ($version) { if ($version -match $match_v) { $onlineFull = $version } else { Write-Warning "Invalid $($version) format. Example: 1.2.13.661.ga588f749-4064" Write-Host } } $old_os = $win7 -or $win8 -or $win8_1 # latest tested version for Win 7-8.1 $last_win7_full = "1.2.5.1006.g22820f93-1078" if (!($version -and $version -match $match_v)) { if ($old_os) { $onlineFull = $last_win7_full } else { # latest tested version for Win 10-12 $onlineFull = "1.2.80.349.g2efc88b5-968" } } else { if ($old_os) { $last_win7 = "1.2.5.1006" if ([version]($onlineFull -split ".g")[0] -gt [version]$last_win7) { Write-Warning ("Version {0} is only supported on Windows 10 and above" -f ($onlineFull -split ".g")[0]) Write-Warning ("The recommended version has been automatically changed to {0}, the latest supported version for Windows 7-8.1" -f $last_win7) Write-Host $onlineFull = $last_win7_full } } } $online = ($onlineFull -split ".g")[0] function Get { param ( [Parameter(Mandatory = $true)] [string]$Url, [int]$MaxRetries = 3, [int]$RetrySeconds = 3, [string]$OutputPath ) $params = @{ Uri = $Url TimeoutSec = 15 } if ($OutputPath) { $params['OutFile'] = $OutputPath } for ($i = 0; $i -lt $MaxRetries; $i++) { try { $response = Invoke-RestMethod @params return $response } catch { Write-Warning "Attempt $($i+1) of $MaxRetries failed: $_" if ($i -lt $MaxRetries - 1) { Start-Sleep -Seconds $RetrySeconds } } } Write-Host Write-Host "ERROR: " -ForegroundColor Red -NoNewline; Write-Host "Failed to retrieve data from $Url" -ForegroundColor White Write-Host return $null } function incorrectValue { Write-Host ($lang).Incorrect"" -ForegroundColor Red -NoNewline Write-Host ($lang).Incorrect2"" -NoNewline Start-Sleep -Milliseconds 1000 Write-Host "3" -NoNewline Start-Sleep -Milliseconds 1000 Write-Host " 2" -NoNewline Start-Sleep -Milliseconds 1000 Write-Host " 1" Start-Sleep -Milliseconds 1000 Clear-Host } function Unlock-Folder { $blockFileUpdate = Join-Path $env:LOCALAPPDATA 'Spotify\Update' if (Test-Path $blockFileUpdate -PathType Container) { $folderUpdateAccess = Get-Acl $blockFileUpdate $hasDenyAccessRule = $false foreach ($accessRule in $folderUpdateAccess.Access) { if ($accessRule.AccessControlType -eq 'Deny') { $hasDenyAccessRule = $true $folderUpdateAccess.RemoveAccessRule($accessRule) } } if ($hasDenyAccessRule) { Set-Acl $blockFileUpdate $folderUpdateAccess } } } function Mod-F { param( [string] $template, [object[]] $arguments ) $result = $template for ($i = 0; $i -lt $arguments.Length; $i++) { $placeholder = "{${i}}" $value = $arguments[$i] $result = $result -replace [regex]::Escape($placeholder), $value } return $result } function downloadSp() { $webClient = New-Object -TypeName System.Net.WebClient Import-Module BitsTransfer $max_x86 = [Version]"1.2.53" $versionParts = $onlineFull -split '\.' $short = [Version]"$($versionParts[0]).$($versionParts[1]).$($versionParts[2])" $arch = if ($short -le $max_x86) { "win32-x86" } else { "win32-x86_64" } $web_Url = "https://download.scdn.co/upgrade/client/$arch/spotify_installer-$onlineFull.exe" $local_Url = "$PWD\SpotifySetup.exe" $web_name_file = "SpotifySetup.exe" try { if (curl.exe -V) { $curl_check = $true } } catch { $curl_check = $false } try { if ($curl_check) { $stcode = curl.exe -Is -w "%{http_code} \n" -o /dev/null -k $web_Url --retry 2 --ssl-no-revoke if ($stcode.trim() -ne "200") { Write-Host "Curl error code: $stcode"; throw } curl.exe -q -k $web_Url -o $local_Url --progress-bar --retry 3 --ssl-no-revoke return } if (!($curl_check ) -and $null -ne (Get-Module -Name BitsTransfer -ListAvailable)) { $ProgressPreference = 'Continue' Start-BitsTransfer -Source $web_Url -Destination $local_Url -DisplayName ($lang).Download5 -Description "$online " return } if (!($curl_check ) -and $null -eq (Get-Module -Name BitsTransfer -ListAvailable)) { $webClient.DownloadFile($web_Url, $local_Url) return } } catch { Write-Host Write-Host ($lang).Download $web_name_file -ForegroundColor RED $Error[0].Exception Write-Host Write-Host ($lang).Download2`n Start-Sleep -Milliseconds 5000 try { if ($curl_check) { $stcode = curl.exe -Is -w "%{http_code} \n" -o /dev/null -k $web_Url --retry 2 --ssl-no-revoke if ($stcode.trim() -ne "200") { Write-Host "Curl error code: $stcode"; throw } curl.exe -q -k $web_Url -o $local_Url --progress-bar --retry 3 --ssl-no-revoke return } if (!($curl_check ) -and $null -ne (Get-Module -Name BitsTransfer -ListAvailable) -and !($curl_check )) { Start-BitsTransfer -Source $web_Url -Destination $local_Url -DisplayName ($lang).Download5 -Description "$online " return } if (!($curl_check ) -and $null -eq (Get-Module -Name BitsTransfer -ListAvailable) -and !($curl_check )) { $webClient.DownloadFile($web_Url, $local_Url) return } } catch { Write-Host ($lang).Download3 -ForegroundColor RED $Error[0].Exception Write-Host Write-Host ($lang).Download4`n $tempDirectory = $PWD Pop-Location Start-Sleep -Milliseconds 200 Remove-Item -Recurse -LiteralPath $tempDirectory Stop-Script } } } function DesktopFolder { # If the default Dekstop folder does not exist, then try to find it through the registry. $ErrorActionPreference = 'SilentlyContinue' if (Test-Path "$env:USERPROFILE\Desktop") { $desktop_folder = "$env:USERPROFILE\Desktop" } $regedit_desktop_folder = Get-ItemProperty -Path "Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\" $regedit_desktop = $regedit_desktop_folder.'{754AC886-DF64-4CBA-86B5-F7FBF4FBCEF5}' if (!(Test-Path "$env:USERPROFILE\Desktop")) { $desktop_folder = $regedit_desktop } return $desktop_folder } function Kill-Spotify { param ( [int]$maxAttempts = 5 ) for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { $allProcesses = Get-Process -ErrorAction SilentlyContinue $spotifyProcesses = $allProcesses | Where-Object { $_.ProcessName -like "*spotify*" } if ($spotifyProcesses) { foreach ($process in $spotifyProcesses) { try { Stop-Process -Id $process.Id -Force } catch { # Ignore NoSuchProcess exception } } Start-Sleep -Seconds 1 } else { break } } if ($attempt -gt $maxAttempts) { Write-Host "The maximum number of attempts to terminate a process has been reached." } } Kill-Spotify # Remove Spotify Windows Store If Any if ($win10 -or $win11 -or $win8_1 -or $win8 -or $win12) { if (Get-AppxPackage -Name SpotifyAB.SpotifyMusic) { Write-Host ($lang).MsSpoti`n if (!($confirm_uninstall_ms_spoti)) { do { $ch = Read-Host -Prompt ($lang).MsSpoti2 Write-Host if (!($ch -eq 'n' -or $ch -eq 'y')) { incorrectValue } } while ($ch -notmatch '^y$|^n$') } if ($confirm_uninstall_ms_spoti) { $ch = 'y' } if ($ch -eq 'y') { $ProgressPreference = 'SilentlyContinue' # Hiding Progress Bars if ($confirm_uninstall_ms_spoti) { Write-Host ($lang).MsSpoti3`n } if (!($confirm_uninstall_ms_spoti)) { Write-Host ($lang).MsSpoti4`n } Get-AppxPackage -Name SpotifyAB.SpotifyMusic | Remove-AppxPackage } if ($ch -eq 'n') { Stop-Script } } } # Attempt to fix the hosts file $hostsFilePath = Join-Path $Env:windir 'System32\Drivers\Etc\hosts' $hostsBackupFilePath = Join-Path $Env:windir 'System32\Drivers\Etc\hosts.bak' if (Test-Path -Path $hostsFilePath) { $hosts = [System.IO.File]::ReadAllLines($hostsFilePath) $regex = "^(?!#|\|)((?:.*?(?:download|upgrade)\.scdn\.co|.*?spotify).*)" if ($hosts -match $regex) { Write-Host ($lang).HostInfo`n Write-Host ($lang).HostBak`n Copy-Item -Path $hostsFilePath -Destination $hostsBackupFilePath -ErrorAction SilentlyContinue if ($?) { Write-Host ($lang).HostDel try { $hosts = $hosts | Where-Object { $_ -notmatch $regex } [System.IO.File]::WriteAllLines($hostsFilePath, $hosts) } catch { Write-Host ($lang).HostError`n -ForegroundColor Red $copyError = $Error[0] Write-Host "Error: $($copyError.Exception.Message)`n" -ForegroundColor Red } } else { Write-Host ($lang).HostError`n -ForegroundColor Red $copyError = $Error[0] Write-Host "Error: $($copyError.Exception.Message)`n" -ForegroundColor Red } } } # Unique directory name based on time Push-Location -LiteralPath ([System.IO.Path]::GetTempPath()) New-Item -Type Directory -Name "SpotX_Temp-$(Get-Date -UFormat '%Y-%m-%d_%H-%M-%S')" | Convert-Path | Set-Location if ($premium) { Write-Host ($lang).Prem`n } $spotifyInstalled = (Test-Path -LiteralPath $spotifyExecutable) if ($spotifyInstalled) { # Check version Spotify offline $offline = (Get-Item $spotifyExecutable).VersionInfo.FileVersion # Version comparison # converting strings to arrays of numbers using the -split operator and a foreach loop $arr1 = $online -split '\.' | foreach { [int]$_ } $arr2 = $offline -split '\.' | foreach { [int]$_ } # compare each element of the array in order from most significant to least significant. for ($i = 0; $i -lt $arr1.Length; $i++) { if ($arr1[$i] -gt $arr2[$i]) { $oldversion = $true break } elseif ($arr1[$i] -lt $arr2[$i]) { $testversion = $true break } } # Old version Spotify if ($oldversion) { if ($confirm_spoti_recomended_over -or $confirm_spoti_recomended_uninstall) { Write-Host ($lang).OldV`n } if (!($confirm_spoti_recomended_over) -and !($confirm_spoti_recomended_uninstall)) { do { Write-Host (($lang).OldV2 -f $offline, $online) $ch = Read-Host -Prompt ($lang).OldV3 Write-Host if (!($ch -eq 'n' -or $ch -eq 'y')) { incorrectValue } } while ($ch -notmatch '^y$|^n$') } if ($confirm_spoti_recomended_over -or $confirm_spoti_recomended_uninstall) { $ch = 'y' Write-Host ($lang).AutoUpd`n } if ($ch -eq 'y') { $upgrade_client = $true if (!($confirm_spoti_recomended_over) -and !($confirm_spoti_recomended_uninstall)) { do { $ch = Read-Host -Prompt (($lang).DelOrOver -f $offline) Write-Host if (!($ch -eq 'n' -or $ch -eq 'y')) { incorrectValue } } while ($ch -notmatch '^y$|^n$') } if ($confirm_spoti_recomended_uninstall) { $ch = 'y' } if ($confirm_spoti_recomended_over) { $ch = 'n' } if ($ch -eq 'y') { Write-Host ($lang).DelOld`n $null = Unlock-Folder cmd /c $spotifyExecutable /UNINSTALL /SILENT wait-process -name SpotifyUninstall Start-Sleep -Milliseconds 200 if (Test-Path $spotifyDirectory) { Remove-Item -Recurse -Force -LiteralPath $spotifyDirectory } if (Test-Path $spotifyDirectory2) { Remove-Item -Recurse -Force -LiteralPath $spotifyDirectory2 } if (Test-Path $spotifyUninstall ) { Remove-Item -Recurse -Force -LiteralPath $spotifyUninstall } } if ($ch -eq 'n') { $ch = $null } } if ($ch -eq 'n') { $downgrading = $true } } # Unsupported version Spotify if ($testversion) { # Submit unsupported version of Spotify to google form for further processing $binary = if (Test-Path $spotifyDll) { $spotifyDll } else { $spotifyExecutable } Start-Job -ScriptBlock { param($binary, $win_os, $psv, $online, $offline) try { $country = [System.Globalization.RegionInfo]::CurrentRegion.EnglishName $txt = [IO.File]::ReadAllText($binary) $regex = "(?") $scriptTagIndex = $indexContent.IndexOf("") $indexContent = $modifiedIndexContent } elseif ($fileName.EndsWith(".css")) { $modifiedIndexContent = $indexContent.Insert($headTagIndex, "") $indexContent = $modifiedIndexContent } } $indexEntry.Delete() $newIndexEntry = $archive.CreateEntry("index.html").Open() $indexWriter = [System.IO.StreamWriter]::new($newIndexEntry) $indexWriter.Write($indexContent) $indexWriter.Dispose() $newIndexEntry.Dispose() } else { Write-Warning "