├── README.md ├── Uninstall-Spotify.bat └── core.ps1 /README.md: -------------------------------------------------------------------------------- 1 | # Uninstall Spotify for Windows 2 | 3 | Completely removes Spotify Desktop and Spotify Microsoft Store 4 | 5 | ## Usage 6 | 7 | 8 | > [!CAUTION] 9 | The script completely removes all local data for the Spotify and Spicetify applications. By running it, you acknowledge that you fully understand what you are doing 10 | 11 | 12 | Download and run [Uninstall-Spotify.bat](https://raw.githack.com/amd64fox/Uninstall-Spotify/main/Uninstall-Spotify.bat) 13 | 14 | or 15 | 16 | Offline launch: 17 | download and unpack the [archive](https://github.com/amd64fox/Uninstall-Spotify/archive/refs/heads/main.zip), run Uninstall-Spotify.bat file inside Uninstall-Spotify-main folder 18 | 19 | 20 | ## Removes: 21 | - Spotify (Desktop & Microsoft store) 22 | - [Spicetify](https://spicetify.app/) 23 | - Files remaining from the official Spotify installer 24 | - Registry keys and values remaining from the official Spotify installer 25 | -------------------------------------------------------------------------------- /Uninstall-Spotify.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set PWSH="%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -ExecutionPolicy Bypass -Command 3 | set RawGit='https://raw.githubusercontent.com/amd64fox/Uninstall-Spotify/refs/heads/main/core.ps1' 4 | set JsDelivr='https://cdn.jsdelivr.net/gh/amd64fox/Uninstall-Spotify@refs/heads/main/core.ps1' 5 | set TLS=[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; 6 | 7 | if "%1"=="e" goto :run 8 | 9 | %PWSH% "([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)" | findstr "True" >nul 10 | 11 | if %errorLevel% == 0 ( 12 | goto :run 13 | ) else ( 14 | %PWSH% "try { start wt 'cmd /c \"\"%~dpnx0\" e\"' -v RunAs } catch { start cmd '/c \"\"%~dpnx0\" e\"' -v RunAs }" 15 | exit 16 | ) 17 | 18 | :run 19 | if exist "%~dp0core.ps1" ( 20 | %PWSH% "& { $(Get-Content '%~dp0core.ps1' -Raw) } | iex; exit $LASTEXITCODE" 21 | ) else ( 22 | %PWSH% %TLS% "& { $(try { iwr -useb %RawGit% } catch { iwr -useb %JsDelivr% }) } | iex; exit $LASTEXITCODE" 23 | ) 24 | 25 | exit -------------------------------------------------------------------------------- /core.ps1: -------------------------------------------------------------------------------- 1 | $paths = @{ 2 | RoamingFolder = Join-Path $env:APPDATA "Spotify" 3 | LocalFolder = Join-Path $env:LOCALAPPDATA "Spotify" 4 | RoamingFolderSpice = Join-Path $env:APPDATA "spicetify" 5 | LocalFolderSpice = Join-Path $env:LOCALAPPDATA "spicetify" 6 | UninstallExe = Join-Path ([System.IO.Path]::GetTempPath()) "SpotifyUninstall.exe" 7 | TempSearch = Join-Path ([System.IO.Path]::GetTempPath()) "SpotX_Temp*" 8 | DesktopShortcut = Join-Path $env:USERPROFILE "Desktop\Spotify.lnk" 9 | StartMenuShortcut = Join-Path $env:APPDATA "Microsoft\Windows\Start Menu\Programs\Spotify.lnk" 10 | } 11 | 12 | $registryKeys = @( 13 | "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\Spotify" 14 | "HKCU:\Software\Spotify" 15 | "HKCU:\Software\Classes\spotify" 16 | "HKCU:\Software\Microsoft\Internet Explorer\Low Rights\ElevationPolicy\{5C0D11B8-C5F6-4be3-AD2C-2B1A3EB94AB6}" 17 | "HKCU:\Software\Microsoft\Internet Explorer\Low Rights\DragDrop\{5C0D11B8-C5F6-4be3-AD2C-2B1A3EB94AB6}" 18 | ) 19 | 20 | $registryValue = @( 21 | @{ 22 | Path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" 23 | Name = "Spotify Web Helper" 24 | } 25 | @{ 26 | Path = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" 27 | Name = "Spotify" 28 | } 29 | ) 30 | 31 | $removedItems = 0 32 | $errorMessages = @() 33 | $ieCachePath = Join-Path $env:LOCALAPPDATA "Microsoft\Windows\INetCache\IE" 34 | 35 | function Find-ItemsToRemove { 36 | $foundItems = @{ 37 | FilesFolders = @() 38 | RegistryKeys = @() 39 | RegistryValues = @() 40 | StoreApp = $null 41 | TempSearchFiles = @() 42 | IeCacheFiles = @() 43 | } 44 | 45 | $sortedItems = $paths.GetEnumerator() | Where-Object { $_.Key -ne "TempSearch" } | ForEach-Object { 46 | if (Test-Path $_.Value) { 47 | Get-Item $_.Value 48 | } 49 | } | Sort-Object @{Expression = { -not $_.PSIsContainer } }, @{Expression = { $_.Extension -ne ".lnk" } } 50 | 51 | $foundItems.FilesFolders = $sortedItems.FullName 52 | 53 | $foundItems.TempSearchFiles = @(Get-ChildItem -Path $paths.TempSearch -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName) 54 | 55 | $foundItems.IeCacheFiles = @(Get-ChildItem -Path $ieCachePath -Force -Recurse -Filter "SpotifyFullSetup*" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName) 56 | 57 | $registryKeys | ForEach-Object { 58 | if (Test-Path $_) { $foundItems.RegistryKeys += $_ } 59 | } 60 | 61 | $registryValue | ForEach-Object { 62 | if (Get-ItemProperty -Path $_.Path -Name $_.Name -ErrorAction SilentlyContinue) { 63 | $foundItems.RegistryValues += $_ 64 | } 65 | } 66 | 67 | $foundItems.StoreApp = Get-AppxPackage -Name "SpotifyAB.SpotifyMusic" -ErrorAction SilentlyContinue 68 | 69 | return $foundItems 70 | } 71 | 72 | function Remove-ItemSafely { 73 | param([string]$Path) 74 | 75 | if (Test-Path -LiteralPath $Path) { 76 | try { 77 | Remove-Item -LiteralPath $Path -Recurse -Force -ErrorAction Stop 78 | Write-Host "Removed: $Path" 79 | return 1 80 | } 81 | catch { 82 | $script:errorMessages += "Failed to remove $Path : $($_.Exception.Message)" 83 | return 0 84 | } 85 | } 86 | return 0 87 | } 88 | 89 | function Remove-FoundItems { 90 | param($foundItems) 91 | 92 | $count = 0 93 | 94 | if ($foundItems.StoreApp) { 95 | try { 96 | $ProgressPreference = 'SilentlyContinue' 97 | Remove-AppxPackage -Package $foundItems.StoreApp.PackageFullName -ErrorAction Stop 98 | Write-Host "Removed: Spotify Store version" 99 | $count++ 100 | } 101 | catch { 102 | $script:errorMessages += "Failed to remove Store app: $_" 103 | } 104 | } 105 | 106 | # Remove files/folders 107 | $foundItems.FilesFolders + $foundItems.TempSearchFiles + $foundItems.IeCacheFiles | ForEach-Object { 108 | $count += Remove-ItemSafely -Path $_ 109 | } 110 | 111 | # Remove registry keys 112 | $foundItems.RegistryKeys | ForEach-Object { 113 | try { 114 | Remove-Item -Path $_ -Recurse -Force -ErrorAction Stop 115 | Write-Host "Removed: $_" 116 | $count++ 117 | } 118 | catch { 119 | $script:errorMessages += "Failed to remove registry key $_ : $_" 120 | } 121 | } 122 | 123 | # Remove registry values 124 | $foundItems.RegistryValues | ForEach-Object { 125 | try { 126 | Remove-ItemProperty -Path $_.Path -Name $_.Name -Force -ErrorAction Stop 127 | Write-Host "Removed: $($_.Path)\value=$($_.Name)" 128 | $count++ 129 | } 130 | catch { 131 | $script:errorMessages += "Failed to remove registry value $($_.Name) : $_" 132 | } 133 | } 134 | 135 | return $count 136 | } 137 | 138 | function Get-RemovalSummary { 139 | if ($errorMessages.Count -gt 0 -or $removedItems -gt 0) { 140 | if ($removedItems -gt 0) { 141 | Write-Host "`nCleanup completed: removed $removedItems items" 142 | } 143 | 144 | if ($errorMessages.Count -gt 0) { 145 | Write-Host "`nErrors encountered ($($errorMessages.Count)):" 146 | $errorMessages | ForEach-Object { Write-Warning $_ } 147 | Write-Host "`nSome items may require manual removal." 148 | } 149 | } 150 | else { 151 | Write-Host "No traces of Spotify were detected" 152 | } 153 | 154 | if ($Host.Name -eq "ConsoleHost") { 155 | Write-Host "`nPress any key to exit..." 156 | $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") 157 | } 158 | } 159 | 160 | function Stop-SpotifyProcesses { 161 | param( 162 | [int]$maxAttempts = 5, 163 | [int]$retryDelay = 1000 164 | ) 165 | 166 | for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) { 167 | $processes = Get-Process -Name "Spotify","SpotifyLauncher","SpotifyUninstall" -ErrorAction SilentlyContinue 168 | if (-not $processes) { break } 169 | 170 | $processes | ForEach-Object { 171 | try { 172 | Stop-Process -Id $_.Id -Force -ErrorAction Stop 173 | } 174 | catch { 175 | $script:errorMessages += "Failed to stop process $($_.Name) (PID: $($_.Id)): $_" 176 | } 177 | } 178 | Start-Sleep -Milliseconds $retryDelay 179 | } 180 | } 181 | 182 | function Reset-TargetACLs { 183 | param( 184 | [Parameter(Mandatory)] 185 | $foundItems 186 | ) 187 | 188 | $aclPaths = $foundItems.FilesFolders + $foundItems.TempSearchFiles + $foundItems.IeCacheFiles 189 | 190 | foreach ($path in $aclPaths) { 191 | if (Test-Path -Path $path) { 192 | try { 193 | Write-Verbose "Resetting ACLs for: $path" 194 | $result = icacls $path /reset /T /Q 195 | if ($LASTEXITCODE -ne 0) { 196 | throw "icacls failed with exit code $LASTEXITCODE" 197 | } 198 | } 199 | catch { 200 | Write-Warning "Failed to reset ACLs for $path : $_" 201 | } 202 | } 203 | } 204 | } 205 | 206 | try { 207 | # Stop all running Spotify processes before cleanup 208 | Stop-SpotifyProcesses -retryDelay 500 209 | 210 | if ($PSVersionTable.PSVersion.Major -ge 7) { 211 | Import-Module Appx -UseWindowsPowerShell -WarningAction SilentlyContinue 212 | } 213 | 214 | Write-Host "Search items..." 215 | Write-Host 216 | $itemsToRemove = Find-ItemsToRemove 217 | $maxAttempts = 5 218 | $attempt = 0 219 | 220 | do { 221 | $attempt++ 222 | 223 | # Reset access control lists (ACLs) to ensure proper file/folder access 224 | Reset-TargetACLs -foundItems $itemsToRemove 225 | 226 | # Remove all identified Spotify-related items (files, registry entries, etc.) 227 | $removedItems += Remove-FoundItems -foundItems $itemsToRemove 228 | 229 | # Scan again for any remaining items that need cleanup 230 | $itemsToRemove = Find-ItemsToRemove 231 | 232 | # Exit loop if maximum attempts reached to prevent infinite loops 233 | if ($attempt -ge $maxAttempts) { 234 | Write-Host "The maximum number of attempts $($maxAttempts) has been reached. Terminating the loop" 235 | break 236 | } 237 | 238 | if ($attempt -ge 1) { Start-Sleep -Milliseconds 1500 } 239 | 240 | } while (($itemsToRemove.FilesFolders.Count + 241 | $itemsToRemove.RegistryKeys.Count + 242 | $itemsToRemove.RegistryValues.Count + 243 | $itemsToRemove.TempSearchFiles.Count + 244 | $itemsToRemove.IeCacheFiles.Count) -gt 0 -or 245 | $itemsToRemove.StoreApp) 246 | 247 | # Display final cleanup summary and any encountered errors 248 | Get-RemovalSummary 249 | } 250 | catch { 251 | Write-Host "Critical error:" -ForegroundColor Red -NoNewline 252 | Write-Host " $($_.Exception.Message)" 253 | Write-Host "$($_.ScriptStackTrace)" 254 | exit 1 255 | } 256 | --------------------------------------------------------------------------------