├── 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 |
--------------------------------------------------------------------------------