Created
November 26, 2025 00:00
-
-
Save Ascor8522/14da1671cf8b9b60d426ddbe3b366488 to your computer and use it in GitHub Desktop.
Script to download, patch and install ReVanced APKs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # revanced.ps1 | |
| # | |
| # DESCRIPTION: | |
| # ============ | |
| # Script to download, patch and install ReVanced APKs. | |
| # | |
| # COPYRIGHT AND LICENSE: | |
| # ====================== | |
| # Copyright (c) 2025 Ascor8522 | |
| # Licensed under the MIT License | |
| # | |
| # REQUIREMENTS: | |
| # ============= | |
| # - PowerShell 7+ | |
| # - Out-ConsoleGridView module | |
| # - Java installed and in PATH | |
| # - ADB installed and in PATH | |
| Function New-TemporaryFolder { | |
| $File = New-TemporaryFile | |
| Remove-Item $File -Force | |
| New-Item -Itemtype Directory -Path "$($ENV:Temp)\$($File.Name)" | |
| } | |
| $tmpDir = New-TemporaryFolder | |
| $cliPath = "$tmpDir\revanced-cli.jar" | |
| $patchesPath = "$tmpDir\patches.rvp" | |
| $apps = @( | |
| @{ | |
| package = "com.google.android.apps.youtube.music" | |
| revancedPackage = "app.revanced.android.apps.youtube.music" | |
| apkPath = "$tmpDir\YouTubeMusic.apk" | |
| patchedApkPath = "$tmpDir\YouTubeMusic-patched.apk" | |
| apkMirrorDeveloper = "google-inc" | |
| apkMirrorAppName = "youtube-music" | |
| apkMirrorApkName = "youtube-music-VERSION-release" | |
| }, | |
| @{ | |
| package = "com.google.android.youtube" | |
| revancedPackage = "app.revanced.android.youtube" | |
| apkPath = "$tmpDir\YouTube.apk" | |
| patchedApkPath = "$tmpDir\YouTube-patched.apk" | |
| apkMirrorDeveloper = "google-inc" | |
| apkMirrorAppName = "youtube" | |
| apkMirrorApkName = "youtube-VERSION-release" | |
| } | |
| ) | |
| $arch = @("armeabi-v7a", "universal") | |
| $dpi = "nodpi" | |
| Function Download-RevancedCli { | |
| $latestReleaseUrl = "https://api.github.com/repos/ReVanced/revanced-cli/releases/latest" | |
| $jarUrl = Invoke-RestMethod -Uri $latestReleaseUrl | |
| | % { $_.assets } | |
| | Where-Object { $_.name -like "*.jar" } | |
| | Select-Object -ExpandProperty browser_download_url | |
| Invoke-WebRequest -Uri $jarUrl -OutFile $cliPath | |
| Write-Host "Downloaded revanced-cli to $cliPath" | |
| } | |
| Function Download-Patches { | |
| $latestReleaseUrl = "https://api.github.com/repos/ReVanced/revanced-patches/releases/latest" | |
| $rvpUrl = Invoke-RestMethod -Uri $latestReleaseUrl | |
| | % { $_.assets } | |
| | Where-Object { $_.name -like "*.rvp" } | |
| | Select-Object -ExpandProperty browser_download_url | |
| Invoke-WebRequest -Uri $rvpUrl -OutFile $patchesPath | |
| Write-Host "Downloaded revanced-patches to $patchesPath" | |
| } | |
| Function Get-Apk-Versions($app) { | |
| $exp = "(?<version>\d+\.\d+\.\d+) \((?<patches>\d+) patches\)" | |
| $versions = & java -jar $cliPath list-versions -f $app.package $patchesPath | |
| | Out-String | |
| | Select-String $exp -AllMatches | |
| | % { $_.matches } | |
| | % { [PSCustomObject]@{ | |
| Version = $_.Groups['version'].Value; | |
| Patches = [int]$_.Groups['patches'].Value; | |
| } } | |
| | Sort-Object -Property Version -Descending | |
| Write-Host "Found $($versions.Count) versions" | |
| return $versions | |
| } | |
| Function Select-Apk-Version($versions, $app) { | |
| $version = $versions | |
| | Out-ConsoleGridView -OutputMode Single -Title "Select version for $($app.package)" -UseNetDriver | |
| | Select-Object -ExpandProperty Version | |
| Write-Host "Selected version $version" | |
| return $version | |
| } | |
| Function Get-Apk-Version-Arch-Dpi-Url($app, $version) { | |
| $versionDashes = $version -replace "\.","-" | |
| $apkName = $app.apkMirrorApkName -replace "VERSION",$versionDashes | |
| $apkPageUrl = "https://www.apkmirror.com/apk/$($app.apkMirrorDeveloper)/$($app.apkMirrorAppName)/$($apkName)/" | |
| $exp = '(?<=<div class="table-cell rowheight addseparator expand pad dowrap">)((?:.|\n)*?)(?=<\/div>)' | |
| $response = Invoke-WebRequest -Uri $apkPageUrl | |
| | % { $_.Content } | |
| | % { $_ -replace '<head>(.|\n)*?</head>', '' } | |
| | % { $_ -replace '<svg(>| )(.|\n)*?</svg>', '' } | |
| | % { $_ -replace '<input(>| )(.|\n)*?>', '' } | |
| | % { $_ -replace '<img(>| )(.|\n)*?>', '' } | |
| | % { $_ -replace '<script(>| )(.|\n)*?</script>', '' } | |
| | % { $_ -replace '<style(>| )(.|\n)*?</style>', '' } | |
| | % { $_ -replace '<br[^>]*/?>', ' ' } | |
| | % { $_ -replace '(?<=>[^<]+)&(?=[^<]+<)', '&' } # escape & in text nodes | |
| | % { $_ -replace 'href="mailto:.*?"', '' } # remove mailto links | |
| | % { $_ -replace '(?<=<.+) [\w-]+=[\w-]+(?=.+>)', '' } # remove attributes without quotes | |
| | Select-String $exp -AllMatches | |
| | % { $_.Matches } | |
| | % { $_.Groups[1].Value.Trim() } | |
| | % { $_ -replace '<a .*?href="(.*?)"(?:.|\n)*', '$1'} | |
| if($response.Count % 4 -ne 0) { | |
| Write-Error "Unexpected number of download links: $($response.Count)" | |
| } | |
| $versionsCount = $response.Count / 4 | |
| Write-Host "Found $($versionsCount) download links" | |
| $versions = @() | |
| for($i = 0; $i -lt $versionsCount; $i++) { | |
| $versions += [PSCustomObject]@{ | |
| Arch = $response[$i * 4 + 0] | |
| Android = $response[$i * 4 + 1] | |
| Dpi = $response[$i * 4 + 2] | |
| Link = $response[$i * 4 + 3] | |
| } | |
| } | |
| $filteredVersions = $versions | |
| | Where-Object { $_.Arch -in $arch } | |
| | Where-Object { $_.Dpi -eq $dpi } | |
| if($filteredVersions.Count -eq 0) { | |
| Write-Error "No matching APK found for arch=$arch and dpi=$dpi" | |
| } | |
| return $filteredVersions[0].Link | |
| } | |
| Function Get-Apk-Final-Download-Url($apkDownloadPageUrl) { | |
| $url = "https://www.apkmirror.com$apkDownloadPageUrl" | |
| $exp = '(?<=<a rel="nofollow" class="accent_bg btn btn-flat downloadButton ..." href=")(.+?)(?=">)' | |
| return Invoke-WebRequest -Uri $url | |
| | % { $_.Content } | |
| | Select-String -Pattern $exp -AllMatches | |
| | % { $_.Matches } | |
| | % { $_.Groups[1].Value.Trim() } | |
| } | |
| Function Download-Apk($app, $version) { | |
| $apkDownloadPageUrl = Get-Apk-Version-Arch-Dpi-Url $app $version | |
| $finalUrl = "https://www.apkmirror.com$(Get-Apk-Final-Download-Url $apkDownloadPageUrl)" | |
| $exp = '/wp-content/themes/APKMirror/download.php\?id=.+&key=.+&forcebaseapk=true' | |
| $url = Invoke-WebRequest -Uri $finalUrl | |
| | % { $_.Content } | |
| | Select-String -Pattern $exp -AllMatches | |
| | % { $_.Matches } | |
| | % { $_.Groups[0].Value.Trim() } | |
| $finalDownloadUrl = "https://www.apkmirror.com$url" | |
| Invoke-WebRequest -Uri $finalDownloadUrl -OutFile $app.apkPath | |
| Write-Host "Downloaded APK to $($app.apkPath)" | |
| } | |
| Function Patch-Apk($app) { | |
| $Host.UI.RawUI.ForegroundColor = 'Gray' | |
| & java -jar $cliPath patch -p $patchesPath -o $app.patchedApkPath $app.apkPath | |
| [Console]::ResetColor() | |
| Write-Host "Patched APK to $($app.patchedApkPath)" | |
| } | |
| Function Upgarde-App($app) { | |
| Write-Host "Uninstalling old version" | |
| & adb uninstall $app.revancedPackage # TODO: requires fixing, sometimes | |
| Write-Host "Installing patched APK" | |
| & adb install $app.patchedApkPath # TODO: requires fixing, sometimes | |
| Write-Host "Upgraded" | |
| } | |
| Function Main() { | |
| Write-Host "Using temporary directory $tmpDir" | |
| Download-RevancedCli | |
| Download-Patches | |
| ForEach ($app in $apps) { | |
| Write-Host "==============================" | |
| Write-Host "Processing app $($app.package)" | |
| Write-Host "==============================" | |
| $versions = Get-Apk-Versions $app | |
| $version = Select-Apk-Version $versions $app | |
| if(-not $version) { | |
| Write-Host "No version selected, skipping" | |
| continue | |
| } | |
| Download-Apk $app $version | |
| Patch-Apk $app | |
| Upgarde-App $app | |
| } | |
| Remove-Item $tmpDir -Recurse -Force | |
| } | |
| $ErrorActionPreference = "Stop" | |
| $PSNativeCommandUseErrorActionPreference = $true | |
| Main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment