Repository: SpotX-Official/SpotX Branch: main Commit: 691e24fa2ecf Files: 55 Total size: 364.4 KB Directory structure: gitextract_c7vys6wk/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ ├── installer-new-translation.yml │ │ └── itranslation-fix.yml │ └── workflows/ │ ├── auto-closes-false-positive.yml │ ├── check_spotx.yml │ └── update-license.yml ├── Install_New_theme.bat ├── Install_Old_theme.bat ├── LICENSE ├── README.md ├── Uninstall.bat ├── css-helper/ │ └── lyrics-color/ │ ├── colors.css │ └── rules.css ├── js-helper/ │ ├── goofyHistory.js │ └── sectionBlock.js ├── patches/ │ ├── Augmented translation/ │ │ └── ru.json │ └── patches.json ├── res/ │ └── login.spa ├── run.ps1 └── scripts/ ├── Install_Auto.bat ├── Install_Prem.bat └── installer-lang/ ├── be.ps1 ├── bn.ps1 ├── cs.ps1 ├── de.ps1 ├── el.ps1 ├── en.ps1 ├── es.ps1 ├── fa.ps1 ├── fi.ps1 ├── fil.ps1 ├── fr.ps1 ├── hi.ps1 ├── hu.ps1 ├── id.ps1 ├── it.ps1 ├── ja.ps1 ├── ka.ps1 ├── ko.ps1 ├── lv.ps1 ├── pl.ps1 ├── pt.ps1 ├── ro.ps1 ├── ru.ps1 ├── sk.ps1 ├── sr-Latn.ps1 ├── sr.ps1 ├── sv.ps1 ├── ta.ps1 ├── tr.ps1 ├── ua.ps1 ├── vi.ps1 ├── zh-TW.ps1 └── zh.ps1 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: ❌ Bug report description: Report errors or unexpected behavior labels: ❌ bug body: - type: checkboxes attributes: label: 🧐 Have you read the FAQ and viewed similar issues? description: ❗️❗️ Important: Please review the FAQ and check both open and closed issue, as your concern may have already been addressed. However, please be aware that if it's discovered that requested actions weren't completed, the issue may be closed without explanation. options: - label: I have read the FAQ required: true - label: I have checked older issues, open and closed required: true - label: I did a clean install of SpotX as described in the FAQ required: true - type: markdown attributes: value: "###" - type: input id: lang_spoti attributes: label: Сountry of your account description: You can view your country on your account page, in paragraph Country or region. placeholder: "e.g. Ukraine" validations: required: true - type: markdown attributes: value: "###" - type: dropdown id: plan attributes: label: What is your Spotify Plan? description: You can view your plan on your account page, under Your plan. options: - Free - Premium validations: required: true - type: markdown attributes: value: "###" - type: dropdown id: spicetify attributes: label: Do you have Spicetify installed? description: Spicetify is a command line tool to customize Spotify client. options: - "No" - "Yes" validations: required: true - type: markdown attributes: value: "###" - type: textarea attributes: label: ℹ Computer information description: | - Spotify: e.g. 1.1.90.859.gf1bb1e36 (Avoid labeling version as "latest" due to regional update variations in Spotify) - In the open Spotify client in the upper left corner, click •••HelpAbout Spotify - Windows: e.g. Windows 11 21H2 22000.527 - Press Win + Pause/Break to open the page to copy from. - PowerShell: e.g. 5.1.19041.1200 - Press Win + R then type "powershell," and press Enter, type the following command Get-Host | Select-Object Version placeholder: | - Spotify: - Windows: - PowerShell: value: | - Spotify: - Windows: - PowerShell: render: markdown validations: required: true - type: textarea attributes: label: 📝 Description description: List steps to reproduce the error and details on what happens and what you expected to happen. value: | 1. 2. 3. ... validations: required: true - type: textarea attributes: label: 📸 Screenshots description: Place any screenshots of the issue here if needed validations: required: false ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: ✏️ FAQ url: https://telegra.ph/SpotX-FAQ-09-19 about: Read the frequently asked questions, maybe it will help solve your problem right now. ================================================ FILE: .github/ISSUE_TEMPLATE/installer-new-translation.yml ================================================ name: 🌐 Installer translation description: Translate the installer into your language labels: 🌐 New translation body: - type: input id: lang attributes: label: What language do you want to translate into? description: Also note the already added languages placeholder: e.g. English validations: required: true - type: input id: langpowershell attributes: label: Enter language name in PowerShell description: Enter the following command in the PowerShell terminal $PSUICulture, write the answer in the line below. For this to work, your Windows must be in this language. placeholder: e.g. en-US validations: required: true - type: textarea attributes: label: 📝 Translation strings description: | Now you can start translating strings, a few notes before you start: - Translation strings start after an equals sign and are enclosed in quotation marks - There are variables before the equal sign, they do not need to be translated. - Also, if you come across characters {0}, {1}, [Y/N] or file names, then just skip them. Good luck to you. value: | Welcome = " ╔══════════════════════════════╗ ║ Welcome to SpotX for Windows ║ ╚══════════════════════════════╝" Incorrect = "Oops, an incorrect value," Incorrect2 = "enter again through " Download = "Error downloading" Download2 = "Will re-request in 5 seconds..." Download3 = "Error again" Download4 = "Check your network settings and run the installation again" Download5 = "Downloading Spotify" StopScript = "Script is stopped" MsSpoti = "The Microsoft Store version of Spotify has been detected which is not supported" MsSpoti2 = "Uninstall Spotify Microsoft Store edition ? [Y/N]" MsSpoti3 = "Automatically uninstalling Spotify MS..." MsSpoti4 = "Uninstalling Spotify MS..." Prem = "Modification for premium account..." OldV = "Found outdated version of Spotify" OldV2 = "Your Spotify version ({0}) is outdated, the current latest version is — {1}" OldV3 = "Want to update ? [Y/N]" AutoUpd = "Automatic update to the recommended version" DelOrOver = "Remove the current version ({0}) or install over it? Y [Remove] / N [Install Over]" DelOld = "Uninstalling old Spotify..." NewV = "Unsupported version of Spotify found" NewV2 = "Your Spotify version ({0}) has not been tested. The stable version for SpotX is {1}" NewV3 = "Continue with {0} (errors may occur) ? [Y/N]" Recom = "Install the latest version {0} ? [Y/N]" DelNew = "Uninstalling an untested Spotify..." DownSpoti = "Downloading and installing Spotify" DownSpoti2 = "Please wait..." PodcatsOff = "Off Podcasts" PodcastsOn = "On Podcasts" PodcatsSelect = "Hide podcasts, shows, and audiobooks on the homepage ? [Y/N]" DowngradeNote = "It is recommended to block because there is already a newer version of Spotify" UpdBlock = "Spotify updates blocked" UpdUnblock = "Spotify updates are not blocked" UpdSelect = "Block Spotify updates ? [Y/N]" ModSpoti = "Patching Spotify..." Error = "Error" FileLocBroken = "Location of Spotify files is broken, uninstall Spotify client and run the script again" Spicetify = "Spicetify detected, it must be installed after SpotX, open recommended actions in FAQ ? [Y/N]" NoRestore = "SpotX has already been installed, xpui.bak not found. `nPlease uninstall Spotify client and run Install.bat again" InstallComplete = "installation completed" HostInfo = "Unwanted URLs found in hosts file" HostBak = "Backing up hosts.bak..." HostDel = "Trying to remove unwanted URLs from the original hosts file..." HostError = "Something went wrong while editing the hosts file, edit it manually or run the script as administrator" render: txt validations: required: true ================================================ FILE: .github/ISSUE_TEMPLATE/itranslation-fix.yml ================================================ name: "🌐 Localization/Translation issue" description: Report incorrect translations. labels: 🌐 Fix translation body: - type: textarea attributes: label: ❌ Actual phrase(s) placeholder: What is there? Please include a screenshot as that is extremely helpful. validations: required: true - type: textarea attributes: label: ✔️ Expected phrase(s) placeholder: What was expected? validations: required: true - type: textarea attributes: label: ℹ Why is the current translation wrong placeholder: Why do you feel this is incorrect? validations: required: true ================================================ FILE: .github/workflows/auto-closes-false-positive.yml ================================================ name: Auto Close Antivirus False Positive Issues on: issues: types: [opened, reopened, edited] jobs: auto-close: runs-on: ubuntu-latest permissions: issues: write steps: - name: Check for AV-related keywords uses: actions/github-script@v7 with: script: | const issue = context.payload.issue; const title = issue.title.toLowerCase(); const actor = context.payload.sender.login; // Проверяем, кто выполнил действие (не создателя issue, а того кто сейчас действует) const privilegedUsers = ['amd64fox']; if (privilegedUsers.includes(actor)) { console.log('Issue #' + issue.number + ' action performed by @' + actor + ' - will not auto-close'); return; } const keywords = [ 'av', 'virus', 'malware', 'trojan', 'defender', 'smartscreen', 'antivirus', 'anti-virus', 'anti virus', 'malicious', 'agenttesla', 'virustotal', 'eset', 'avast', 'avg', 'kaspersky', 'norton', 'mcafee', 'bitdefender' ]; const containsKeyword = keywords.some(keyword => { const regex = new RegExp('\\b' + keyword + '\\b', 'i'); return regex.test(title); }); if (containsKeyword) { console.log('Issue #' + issue.number + ' contains AV-related keywords in title'); const comment = '# ⚠️ Automatic Closure: Antivirus False Positive\n\n' + '> [!NOTE]\n' + '> This issue has been automatically closed because it appears to be related to an **antivirus false positive detection**.\n\n' + '---\n\n' + '## 🔍 What causes false positives?\n\n' + 'Different antiviruses may respond differently to installing **SpotX**, as SpotX uses a system interpreter **PowerShell** to modify an application **Spotify**.\n\n' + 'Also, most AV use **heuristic analysis**, which looks for threats based on assumptions or by matching its own set of rules. This imperfect method also leads to a high number of false positives.\n\n' + '---\n\n' + '## ✅ What should you do?\n\n' + '### Step 1: Disable Your Antivirus\n' + '> [!IMPORTANT]\n' + '> Before running the SpotX patcher, **temporarily disable or pause your antivirus protection**.\n\n' + '> [!WARNING]\n' + '> **Signs you need to disable your antivirus:**\n' + '> - The terminal window is forcibly closed\n' + '> - Errors flagging the script as malicious\n' + '> - The bat installation file is deleted\n\n' + '### Step 2: Run the Installation\n' + 'Once your antivirus is disabled, proceed with the SpotX installation.\n\n' + '### Step 3: Re-enable Protection\n' + 'After the installation is complete, you can turn your antivirus back on. If your AV then marks some files in the Spotify folder, **add them to the exclusion list** of your antivirus.\n\n' + '> [!TIP]\n' + '> We do not provide instructions on how to disable AV protection or add files/folders to exclusions, as there are many different security systems and the process varies for each. The easiest solution if you don\'t know how to do this is to use [Google](https://www.google.com).\n\n' + '---\n\n' + '
\n\n' + '**🤖 This issue was automatically closed by a bot.**\n\n' + 'If you believe your issue isn\'t related to a false positive, please mention **@​amd64fox**\n\n' + '
'; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, body: comment }); const existingLabels = issue.labels.map(label => label.name); if (existingLabels.length > 0) { for (const label of existingLabels) { await github.rest.issues.removeLabel({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, name: label }); } } await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, labels: ['❎ false positive'] }); await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, state: 'closed', state_reason: 'not_planned' }); console.log('Issue #' + issue.number + ' was automatically closed'); } else { console.log('Issue #' + issue.number + ' does not contain AV-related keywords in title'); } ================================================ FILE: .github/workflows/check_spotx.yml ================================================ name: Check Spotx for the latest Spotify on: workflow_dispatch: jobs: build: runs-on: windows-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Spotx shell: powershell run: | $jsonUrl = "https://raw.githubusercontent.com/amd64fox/LoaderSpot/main/versions.json" $resp = Invoke-RestMethod -Uri $jsonUrl # Get a link to the x86 installer for the first version $firstVersion = $resp.PSObject.Properties.Name | Select-Object -First 1 $x64InstallerUrl = $resp.$firstVersion.links.win.x64 # Regex to find the desired part of the link $regex = [regex]::Match($x64InstallerUrl, "spotify_installer-(.+?)\.exe") $version = $regex.Groups[1].Value # Incoming parameters $parametrs = '-Verbose -new_theme -v $version -sp-over -cache_limit 1000 -block_update_on -lyrics_stat spotify -urlform_goofy "https://docs.google.com/forms/formResponse" -idbox_goofy "9999999" -podcasts_off -adsections_off -lyrics_block' # Run Spotx iex "& { $(iwr -useb 'https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1') } $parametrs" ================================================ FILE: .github/workflows/update-license.yml ================================================ name: Update License Year on: schedule: - cron: '0 0 1 1 *' workflow_dispatch: permissions: contents: write jobs: update-license: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - name: Update year in LICENSE run: | CURRENT_YEAR=$(date +'%Y') sed -i -E "s/Copyright \(c\) ([0-9]{4})(-[0-9]{4})?/Copyright (c) \1-$CURRENT_YEAR/" LICENSE - name: Commit and push changes run: | git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" git add LICENSE if git diff --staged --quiet; then echo "No changes needed" else git commit -m "chore: update license year to $(date +'%Y')" git push fi ================================================ FILE: Install_New_theme.bat ================================================ @echo off :: Line for changing spotx parameters, each parameter should be separated by a space set param=-new_theme set url='https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1' set url2='https://spotx-official.github.io/SpotX/run.ps1' set tls=[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe ^ -Command %tls% $p='%param%'; """ & { $(try { iwr -useb %url% } catch { $p+= ' -m'; iwr -useb %url2% })} $p """" | iex pause exit /b ================================================ FILE: Install_Old_theme.bat ================================================ @echo off :: Line for changing spotx parameters, each parameter should be separated by a space set param=-v 1.2.13.661.ga588f749 -confirm_spoti_recomended_over -block_update_on set url='https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1' set url2='https://spotx-official.github.io/SpotX/run.ps1' set tls=[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; %SYSTEMROOT%\System32\WindowsPowerShell\v1.0\powershell.exe ^ -Command %tls% $p='%param%'; """ & { $(try { iwr -useb %url% } catch { $p+= ' -m'; iwr -useb %url2% })} $p """" | iex pause exit /b ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2021-2026 amd64fox Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================

Patcher for Spotify Desktop Client on Windows

RequirementsFeaturesInstallationUninstallFAQDisclaimer

Requirements

- **OS:** Windows 7-11 - **Spotify:** [Official desktop version](https://loadspot.pages.dev) (Microsoft Store version is not suitable) - **PowerShell:** 5.1 and above

Features

- **Blocks all banner, video, and audio ads** in the client - **Hiding podcasts, episodes, and audiobooks** from the homepage (optional) - **Block Spotify automatic updates** (optional) - **Some native experimental features have been changed** - **Analytics sending has been disabled** - **Advanced installation [parameters](https://github.com/SpotX-Official/SpotX/discussions/60)**

Installation / Update

Choose installation type:

Usual installation (New theme)

#### During installation, you need to confirm some actions, also contains: - New theme activated (new right and left sidebar, some cover change) - All [experimental features](https://github.com/SpotX-Official/SpotX/discussions/50) included

#### Just download and run [Install_New_theme.bat](https://raw.githack.com/amd64fox/SpotX/main/Install_New_theme.bat) or #### Run The following command in PowerShell: ```ps1 iex "& { $(iwr -useb 'https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1') } -new_theme" ``` #### mirror ```ps1 iex "& { $(iwr -useb 'https://spotx-official.github.io/SpotX/run.ps1') } -m -new_theme" ```
Usual installation (Old theme)

#### During installation, you need to confirm some actions, also contains: - Forced installation of version 1.2.13 (since the old theme was removed in subsequent versions) - Old theme activated - Automatic blocking of Spotify updates - All [experimental features](https://github.com/SpotX-Official/SpotX/discussions/50) included

#### Just download and run [Install_Old_theme.bat](https://raw.githack.com/amd64fox/SpotX/main/Install_Old_theme.bat) or #### Run The following command in PowerShell: ```ps1 iex "& { $(iwr -useb 'https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1') } -v 1.2.13.661.ga588f749 -confirm_spoti_recomended_over -block_update_on" ``` #### mirror ```ps1 iex "& { $(iwr -useb 'https://spotx-official.github.io/SpotX/run.ps1') } -m -v 1.2.13.661.ga588f749 -confirm_spoti_recomended_over -block_update_on" ```
Full installation

Full installation without confirmation, what does it do?

- New theme activated (new right and left sidebar, some cover change) - Hiding podcasts/episodes/audiobooks from the homepage - Activated [static theme](https://github.com/SpotX-Official/SpotX/discussions/50#discussioncomment-4096066) spotify for lyrics - Hiding [ad-like sections](https://github.com/SpotX-Official/SpotX/discussions/50#discussioncomment-4478943) - All [experimental features](https://github.com/SpotX-Official/SpotX/discussions/50) included - Removal of Spotify MS if it was found - Installation of the recommended version of Spotify (if another client has already been found, it will be installed over) - Blocking of Spotify updates - After the installation is completed, the client will autorun.

#### Just download and run [Install_Auto.bat](https://raw.githack.com/amd64fox/SpotX/main/scripts/Install_Auto.bat) or #### Run The following command in PowerShell: ```ps1 iex "& { $(iwr -useb 'https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1') } -confirm_uninstall_ms_spoti -confirm_spoti_recomended_over -podcasts_off -block_update_on -start_spoti -new_theme -adsections_off -lyrics_stat spotify" ``` #### mirror ```ps1 iex "& { $(iwr -useb 'https://spotx-official.github.io/SpotX/run.ps1') } -m -confirm_uninstall_ms_spoti -confirm_spoti_recomended_over -podcasts_off -block_update_on -start_spoti -new_theme -adsections_off -lyrics_stat spotify" ```
Other types of installations

Installation for premium

#### Usual installation only without ad blocking, for those who have a premium account, also contains: - New theme activated (new right and left sidebar, some cover change) - Disabled only audio ads in podcasts - All [experimental features](https://github.com/SpotX-Official/SpotX/discussions/50) included

#### Just download and run [Install_Prem.bat](https://raw.githack.com/amd64fox/SpotX/main/scripts/Install_Prem.bat) or #### Run The following command in PowerShell: ```ps1 iex "& { $(iwr -useb 'https://raw.githubusercontent.com/SpotX-Official/SpotX/refs/heads/main/run.ps1') } -premium -new_theme" ``` #### mirror ```ps1 iex "& { $(iwr -useb 'https://spotx-official.github.io/SpotX/run.ps1') } -m -premium -new_theme" ```
Installing with parameters

You can specify various parameters for a more flexible installation, more [details here](https://github.com/SpotX-Official/SpotX/discussions/60)

Uninstall

- Just run [Uninstall.bat](https://raw.githack.com/amd64fox/SpotX/main/Uninstall.bat) or - Reinstall Spotify ([Full uninstall Spotify](https://github.com/amd64fox/Uninstall-Spotify) recommended)

FAQ

Read [FAQ](https://telegra.ph/SpotX-FAQ-09-19)

Disclaimer

SpotX is a tool that modifies the official Spotify client, provided as an evaluation version — use it at your own risk. ================================================ FILE: Uninstall.bat ================================================ @echo off SETLOCAL ENABLEDELAYEDEXPANSION set "SPOTIFY_PATH=%Appdata%\Spotify" if exist "%SPOTIFY_PATH%\chrome_elf.dll.bak" ( del /s /q "%SPOTIFY_PATH%\chrome_elf.dll" > NUL 2>&1 move "%SPOTIFY_PATH%\chrome_elf.dll.bak" "%SPOTIFY_PATH%\chrome_elf.dll" > NUL 2>&1 ) if exist "%SPOTIFY_PATH%\Spotify.dll.bak" ( del /s /q "%SPOTIFY_PATH%\Spotify.dll" > NUL 2>&1 move "%SPOTIFY_PATH%\Spotify.dll.bak" "%SPOTIFY_PATH%\Spotify.dll" > NUL 2>&1 ) if exist "%SPOTIFY_PATH%\Spotify.bak" ( del /s /q "%SPOTIFY_PATH%\Spotify.exe" > NUL 2>&1 move "%SPOTIFY_PATH%\Spotify.bak" "%SPOTIFY_PATH%\Spotify.exe" > NUL 2>&1 ) if exist "%SPOTIFY_PATH%\Apps\xpui.bak" ( del /s /q "%SPOTIFY_PATH%\Apps\xpui.spa" > NUL 2>&1 move "%SPOTIFY_PATH%\Apps\xpui.bak" "%SPOTIFY_PATH%\Apps\xpui.spa" > NUL 2>&1 ) if exist "%temp%\SpotX_Temp*" ( for /d %%i in ("%temp%\SpotX_Temp*") do ( rd /s/q "%%i" > NUL 2>&1 ) ) echo Patch successfully removed pause ================================================ FILE: css-helper/lyrics-color/colors.css ================================================ :root { --past: {{past}}; --current: {{current}}; --next: {{next}}; --hover: {{hover}}; --background: {{background}}; --musixmatch: {{musixmatch}}; } ================================================ FILE: css-helper/lyrics-color/rules.css ================================================ @import url('colors.css'); /* mini lyrics */ .Li269NgzkU2gI4KOP9sM, .I2WIloMMjsBeMaIS8H3v, .McI3hD7aCfpq015LJa6X, .gpDSOimnzH4zTJmE7UR5 { --lyrics-color-active: var(--current) !important; --lyrics-color-inactive: var(--next) !important; --lyrics-color-passed: var(--past) !important; --lyrics-color-background: var(--background) !important; } /* title unsynced */ p[class*="e-"][class*="-text"].encore-text-body-small { color: var(--musixmatch) !important; margin-bottom: 8px !important; } /* fixed color of lyrics button */ [data-testid="lyrics-npv-section"]:not(._OhUGn8Plh3mRw4awIM5):not(.Sb2rC16jDkGc9eweOU8g):not(._YRfjT5prbRuSXcNK9WR):not(.RXRGSIFllAhUYWKYlANd) { background-color: #1f1f1f !important; } /* lyrics description */ .ebHsEf.I4K12o0qDoITOLr2AEs0, .ebHsEf.OYiGFGZJDIZ4FF4ZTDK2, .jKdLzW.LvLs_UgYs7ps5KdoCr0h, .bWzOVV._T5UDP2tItG9WGdwO5Yi, .hzUuLPdH48AzgQun5NYQ [data-encore-id="type"], .hzUuLPdH48AzgQun5NYQ [data-encore-id="text"], body .LomBcMvfM8AEmZGquAdj, body .W_EplVEAbZrZURqfLiQC, body .kGR_hu4tdj9PnUlSPaRL, .GML6YUVCeJvRhGznLnqm, body .iq4cgi0YEKr6DGaTtzUj, body .KDhLFoEqoClhH12bsfrS { color: var(--musixmatch) !important; } /* lyrics not available message */ .C3pBU1DsOUJJOAv89ZFT, .T67LFP0PElpfkkLuegQt, .e7eFLioNSG5PAi1qVFT4 { color: var(--musixmatch) !important; } /* full cinema lyrics */ .FUYNhisXTCmbzt9IDxnT, .tr8V5eHsUaIkOYVw7eSG, .hW9km7ku6_iggdWDR_Lg, .lofIAg8Ixko3mfBrbfej { --lyrics-color-active: var(--current) !important; --lyrics-color-inactive: var(--next) !important; --lyrics-color-passed: var(--past) !important; --lyrics-color-background: var(--background) !important; } /* fix previous lyrics for old versions */ .H2J92dVdr0ykdOX5azL1, .KnFq2ijXFdOtyl4Iebjv { color: var(--past) !important; opacity: 1 !important; } /* hover */ .vapgYYF2HMEeLJuOWGq5:hover, ._LKG3z7SnerR0eigPCoK:hover, .NHVfxGs2HwmI_fly2JC4:hover, .gaHIufRWhoWbiT8S6zuM:hover, .FQYXZaa0aDIrse54YlYO:hover { color: var(--hover) !important; /* remove underline */ text-decoration: none !important; } /* lyrics unsynced */ .HxblHEsl2WX2yhubfVIc, .SruqsAzX8rUtY2isUZDF, .eTLjCqbDo7QehPEPz86a, .AEfhRyqGa3vzQrgfdwWE.Re403AJffPPuZmX7LRJj, .NHVfxGs2HwmI_fly2JC4.E64X_eoy6xsJmDdKKHja, .gaHIufRWhoWbiT8S6zuM.Qo3OkrSis5IWlP9Tchbr, .AEfhRyqGa3vzQrgfdwWE .Re403AJffPPuZmX7LRJj { color: var(--next) !important; } /* full screen lyrics */ .npv-lyrics__text-wrapper--previous .npv-lyrics__text { color: var(--past) !important; } .npv-lyrics__text-wrapper--current .npv-lyrics__text { color: var(--current) !important; } .npv-lyrics__text-wrapper--next .npv-lyrics__text { color: var(--next) !important; } .npv-lyrics__text.npv-lyrics__text--credits, .npv-lyrics__text--unsynced-warning { color: var(--musixmatch) !important; } .npv-lyrics__text--unsynced { color: var(--next) !important; } .npv-background-color { background: var(--background) !important; } .npv-main-container { background: transparent !important; } .npv-lyrics__gradient-background { background: -webkit-gradient(linear, left top, left bottom, from(rgba(18, 18, 18, 0)), color-stop(30%, var(--background)), color-stop(60%, var(--background))) !important; background: -webkit-linear-gradient(top, rgba(18, 18, 18, 0) 0%, var(--background) 30%, var(--background) 60%) !important; background: linear-gradient(to bottom, rgba(18, 18, 18, 0) 0%, var(--background) 30%, var(--background) 60%) !important; } /* read along podcasts */ .l6lFMYQteTVnTcHnLywc, ._nDkCIVgkWayq3tqiIuW, .B_wut2Bw4HwLr3w8rNfM { --transcript-color-background: var(--background) !important; --transcript-color-text: var(--next) !important; --transcript-color-highlightText: var(--current) !important; } ================================================ FILE: js-helper/goofyHistory.js ================================================ // max track buffer for localStorage // when the limit is reached, old tracks will be removed from the beginning, and new ones will be added to the end const MAX_TRACKS = 1000; // max delay between switching tracks (ms) const MAX_DELAY = 1000; function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } const loadTracksFromStorage = () => { try { const savedTracks = localStorage.getItem('sentSpotifyTracks'); return new Set(savedTracks ? JSON.parse(savedTracks) : []); } catch (error) { console.error('Error loading tracks from localStorage:', error); return new Set(); } }; const saveTracksToStorage = (tracks) => { try { let tracksArray = [...tracks]; if (tracksArray.length > MAX_TRACKS) { tracksArray = tracksArray.slice(-MAX_TRACKS); } localStorage.setItem('sentSpotifyTracks', JSON.stringify(tracksArray)); } catch (error) { console.error('Error saving tracks to localStorage:', error); } }; const unique = loadTracksFromStorage(); async function sendToGoogleForm(uri, urlForm, idBox) { try { await fetch(urlForm, { "headers": { "content-type": "application/x-www-form-urlencoded", }, "body": "entry." + idBox + "=" + uri, "method": "POST", "mode": "no-cors", }); saveTracksToStorage(unique); } catch (error) { console.error('Error sending uri to google form:', error); } } const goofyHistory = debounce(async (e, urlForm, idBox) => { const uri = e?.item?.uri; if (uri && uri.includes('spotify:track:') && !unique.has(uri)) { unique.add(uri); await sendToGoogleForm(uri, urlForm, idBox); } }, MAX_DELAY); ================================================ FILE: js-helper/sectionBlock.js ================================================ const API_PATHFINDER = 'api-partner.spotify.com/pathfinder'; const API_RECOMMENDATIONS = 'api.spotify.com/v1/views/personalized-recommendations'; const BLOCKED_SECTIONS_BY_CATEGORY = { 'Party': [ '0JQ5DAnM3wGh0gz1MXnul1' ], 'Chill': [ '0JQ5DAnM3wGh0gz1MXnukV' ], 'Best of the Year': [ '0JQ5IMCbQBLupUQrQFeCzx' ], 'Best of Artists / Tracks': [ '0JQ5DAnM3wGh0gz1MXnu3C' ], 'Best of songwriters': [ '0JQ5DAnM3wGh0gz1MXnu4w' ], 'Biggest Indie Playlists': [ '0JQ5IMCbQBLhSb02SGYpDM' ], 'Charts': [ '0JQ5DAnM3wGh0gz1MXnu5g' ], 'Dinner': [ '0JQ5DAnM3wGh0gz1MXnu3p' ], 'Featured Charts': [ '0JQ5DAob0KOew1FBAMSmBz' ], 'Focus': [ '0JQ5DAob0JCuWaGLU6ntFY', '0JQ5DAnM3wGh0gz1MXnulP' ], 'Fresh new music': [ '0JQ5DAnM3wGh0gz1MXnu3s' ], 'Gaming music': [ '0JQ5DAob0LaV9FOMJ9utY5' ], 'Happy': [ '0JQ5DAnM3wGh0gz1MXnu3q' ], 'ICE PHONK': [ '0JQ5IMCbQBLiqrNCH9VvmA' ], 'Mood': [ '0JQ5DAnM3wGh0gz1MXnucG', '0JQ5DAob0JCuWaGLU6ntFT' ], 'Most Listened 2023': [ '0JQ5IMCbQBLicmNERjnGn5' ], 'Music to game to': [ '0JQ5DAob0Jr9ClCbkV4pZD' ], 'Popular Albums / Artists': [ '0JQ5DAnM3wGh0gz1MXnu3B' ], 'Popular new releases': [ '0JQ5DAnM3wGh0gz1MXnu3D' ], 'Popular radio': [ '0JQ5DAnM3wGh0gz1MXnu4h' ], 'Sad': [ '0JQ5DAnM3wGh0gz1MXnu3u', '0JQ5DAnM3wGh0gz1MXnul2' ], 'Throwback': [ '0JQ5DAnM3wGh0gz1MXnu3w', '0JQ5DAnM3wGh0gz1MXnul4' ], 'Throwback Thursday / Spotify Playlists / Good night ': [ '0JQ5DAuChZYPe9iDhh2mJz' ], 'Today`s biggest hits': [ '0JQ5DAnM3wGh0gz1MXnu3M' ], 'Trending now': [ '0JQ5DAnM3wGh0gz1MXnu3E' ], 'Workout': [ '0JQ5DAnM3wGh0gz1MXnu3x', '0JQ5DAnM3wGh0gz1MXnul6' ], 'Now defrosting': [ '0JQ5IMCbQBLlC31GvtaB6w' ], 'Unknown': [ '0JQ5IMCbQBLqTJyy28YCa9', '0JQ5DAnM3wGh0gz1MXnu7R' ] }; const BLOCKED_SECTIONS = {}; for (const [category, ids] of Object.entries(BLOCKED_SECTIONS_BY_CATEGORY)) { for (const id of ids) { BLOCKED_SECTIONS[id] = category; } } const BLOCKED_CONTENT_TYPES = new Set(['Podcast', 'Audiobook', 'Episode']); const createSectionAdapter = (isPersonalizedRecommendations) => { if (isPersonalizedRecommendations) { return { getId: (item) => { const href = item?.href; if (!href) return null; const parts = href.split('/'); let id = parts[parts.length - 1]; if (id.startsWith('section')) { id = id.substring(7); } return id; }, getTitle: (item) => item?.content?.name || 'Unknown', getRef: (item) => item?.href, getSectionId: (item) => item?.id, getContentItems: (item) => item?.content?.items, getContentData: (contentItem) => contentItem?.content, getContentType: (contentItem) => contentItem?.type, getContentTypeName: (contentItem) => contentItem?.content_type }; } else { return { getId: (item) => { const uri = item?.uri; if (!uri) return null; const parts = uri.split(':'); return parts[parts.length - 1]; }, getTitle: (item) => item?.data?.title?.text || 'Unknown', getRef: (item) => item?.uri, getSectionId: (item) => null, getContentItems: (item) => item?.sectionItems?.items, getContentData: (contentItem) => contentItem?.content?.data, getContentType: (contentItem) => null, getContentTypeName: (contentItem) => null }; } }; const processShortcutsSection = (contentItems, adapter, removed) => { if (!contentItems?.length) return false; for (let j = contentItems.length - 1; j >= 0; j--) { const contentItem = contentItems[j]; const contentType = adapter.getContentTypeName(contentItem); if (contentType !== 'PODCAST_EPISODE' && contentType !== 'AUDIOBOOK') { continue; } removed.push({ type: contentType, name: contentItem?.name || 'Unknown', uri: contentItem?.uri || 'N/A' }); contentItems.splice(j, 1); } return true; }; const isPodcastSection = (contentItems, adapter) => { if (!contentItems?.length) return false; return adapter.getContentType(contentItems[0]) === 'show'; }; const removeBlockedContent = (contentItems, adapter, removed) => { if (!contentItems?.length) return; for (let j = contentItems.length - 1; j >= 0; j--) { const contentData = adapter.getContentData(contentItems[j]); if (!contentData || !BLOCKED_CONTENT_TYPES.has(contentData.__typename)) { continue; } removed.push({ type: contentData.__typename, name: contentData.name || 'Unknown', uri: contentData.uri || 'N/A' }); contentItems.splice(j, 1); } }; function sectionBlock(data, type) { const body = data?.data?.home; const sections = body?.sectionContainer?.sections?.items; const items = data?.content?.items || data?.data?.content?.items; const isPersonalizedRecommendations = !!items && !body; const targetArray = isPersonalizedRecommendations ? items : sections; function removeSections() { if (!targetArray?.length) return; const adapter = createSectionAdapter(isPersonalizedRecommendations); const removed = []; for (let i = targetArray.length - 1; i >= 0; i--) { const item = targetArray[i]; const sectionId = adapter.getId(item); if (!sectionId) continue; if (sectionId in BLOCKED_SECTIONS) { removed.push({ id: sectionId, knownAs: BLOCKED_SECTIONS[sectionId], actualTitle: adapter.getTitle(item), ref: adapter.getRef(item) }); targetArray.splice(i, 1); } } if (removed.length > 0) { console.log(`[SectionBlock] Removed ${removed.length} blocked section(s):`, removed); } } function removePodcasts() { if (!targetArray?.length) return; const adapter = createSectionAdapter(isPersonalizedRecommendations); const removed = []; for (let i = targetArray.length - 1; i >= 0; i--) { const item = targetArray[i]; const contentItems = adapter.getContentItems(item); if (isPersonalizedRecommendations) { const sectionId = adapter.getSectionId(item); if (sectionId === 'shortcuts') { processShortcutsSection(contentItems, adapter, removed); continue; } if (isPodcastSection(contentItems, adapter)) { removed.push({ type: 'PodcastSection', sectionId: sectionId, sectionName: adapter.getTitle(item), itemsCount: contentItems.length }); targetArray.splice(i, 1); continue; } } removeBlockedContent(contentItems, adapter, removed); } if (removed.length > 0) { console.log(`[SectionBlock] Removed ${removed.length} podcast/audiobook item(s):`, removed); } } function removeCanvasSections() { if (!sections?.length) return; const removed = []; for (let i = sections.length - 1; i >= 0; i--) { if (sections[i]?.data?.__typename === 'HomeFeedBaselineSectionData') { removed.push({ uri: sections[i]?.uri || 'N/A', title: sections[i]?.data?.title?.text || 'Canvas Section' }); sections.splice(i, 1); } } if (removed.length > 0) { console.log(`[SectionBlock] Removed ${removed.length} canvas section(s):`, removed); } } if ((body?.greeting && sections) || items) { const actions = { section: removeSections, podcast: removePodcasts, canvas: removeCanvasSections, all: () => { removeSections(); removePodcasts(); if (!isPersonalizedRecommendations) { removeCanvasSections(); } } }; if (Array.isArray(type)) { type.forEach(t => actions[t]?.()); } else { actions[type]?.(); } } } const originalFetch = window.fetch; window.fetch = async function (...args) { const [url] = args; const urlString = typeof url === 'string' ? url : url?.url || ''; const isPathfinderUrl = urlString.includes(API_PATHFINDER); const isPersonalizedRecommendationsUrl = urlString.includes(API_RECOMMENDATIONS); if (!isPathfinderUrl && !isPersonalizedRecommendationsUrl) { return originalFetch.apply(this, args); } const response = await originalFetch.apply(this, args); const clonedResponse = response.clone(); try { const data = await response.json(); const shouldModify = (isPathfinderUrl && data?.data?.home) || (isPersonalizedRecommendationsUrl && data?.content); if (!shouldModify) { return clonedResponse; } sectionBlock(data, ''); return new Response(JSON.stringify(data), { status: response.status, statusText: response.statusText, headers: response.headers }); } catch (error) { console.error('Fetch intercept error:', error); return clonedResponse; } }; ================================================ FILE: patches/Augmented translation/ru.json ================================================ { "lang": { "version": "1.2.80", "language": "Russian" }, "1": { "match": "\"Confirm your age\"", "replace": "\"Подтвердите свой возраст\"" }, "2": { "match": "\"%price%/month after. Terms and conditions apply. One month free not available for users who have already tried Premium.\"", "replace": "\"%price%/месяц спустя. Принять условия. Один месяц бесплатно, недоступно для пользователей, которые уже попробовали Premium.\"" }, "3": { "match": "\"Offline storage location\"", "replace": "\"Хранилище скачанных треков\"" }, "4": { "match": "\"Click to start listening\"", "replace": "\"Нажмите, чтобы начать прослушивание\"" }, "5": { "match": "\"Change speed\"", "replace": "\"Изменение скорости\"" }, "6": { "match": "\"You need to be at least 19 years old to listen to explicit content marked with\"", "replace": "\"Вам должно быть не менее 19 лет, чтобы слушать непристойный контент, помеченный значком\"" }, "7": { "match": "\"Add to another playlist\"", "replace": "\"Добавить в другой плейлист\"" }, "8": { "match": "\"Character counter\"", "replace": "\"Счетчик символов\"" }, "9": { "match": "\"Toggle lightsaber hilt. Current is [{]0[}].\"", "replace": "\"Переключить рукоять светового меча. Текущий {0}.\"" }, "10": { "match": "\"Doors at .0.\"", "replace": "\"Вход в {0}\"" }, "11": { "match": "\"Show at .0.\"", "replace": "\"Показ в {0}\"" }, "12": { "match": "\"Your Location\"", "replace": "\"Ваше местоположение\"" }, "13": { "match": "\"Override certain user attributes to test regionalized content programming. The overrides are only active in this app.\"", "replace": "\"Переопределите определенные атрибуты пользователя, чтобы протестировать региональное программирование контента. Переопределения активны только в этом приложении.\"" }, "14": { "match": " was released this week!", "replace": " был выпущен на этой неделе!" }, "15": { "match": "\"Speed [{]0[}]×\"", "replace": "\"Скорость {0}×\"" }, "16": { "match": "\"Artist\"", "replace": "\"Исполнитель\"" }, "17": { "match": "\"... of ...\"", "replace": "\"{0} из {1}\"" }, "18": { "match": "\"Не удалось изменить\"", "replace": "\"Не удалось обновить\"" }, "19": { "match": "Dynamic loudness control for podcasts - Actively maintain optimal podcast volume level for a clear and balanced sound experience", "replace": "Динамическое управление громкостью для подкастов - активное поддержание оптимального уровня громкости подкаста для четкого и сбалансированного звукового восприятия" }, "20": { "match": "\"Build .0. is available\"", "replace": "\"Сборка {0} доступна\"" }, "21": { "match": "\"Downloading build .0....\"", "replace": "\"Загрузка сборки {0}...\"" }, "22": { "match": "\"Build .0. is ready to install\"", "replace": "\"Сборка {0} готова к установке\"" }, "23": { "match": "\"Remove\"", "replace": "\"Удалить\"" }, "24": { "match": "\"Do not translate\"", "replace": "\"Не переводить\"" }, "25": { "match": "\"Translating to .0.\"", "replace": "\"Перевожу на {0}\"" }, "26": { "match": "\"Translation to .0. not available\"", "replace": "\"Перевод на {0} недоступен\"" }, "27": { "match": "\"Translate lyrics\"", "replace": "\"Перевести текст песни\"" }, "28": { "match": "\"Spotify MiniPlayer\"", "replace": "\"мини-плеер Spotify\"" }, "29": { "match": "\"Venue\"", "replace": "\"Место проведения\"" }, "30": { "match": "\"We couldn't find the venue\"", "replace": "\"Мы не смогли найти место проведения\"" }, "31": { "match": "\"Bring back to main window\"", "replace": "\"Вернуться к основному окну\"" }, "32": { "match": "\"Chapter\"", "replace": "\"Глава\"" }, "33": { "match": "\"Song\"", "replace": "\"Трек\"" }, "34": { "match": "\"Episode\"", "replace": "\"Эпизод\"" }, "35": { "match": "\"The venue you requested couldn.t be found, please try again.\"", "replace": "\"Запрошенное вами место проведения не найдено, попробуйте снова.\"" }, "36": { "match": "\"There was an error fetching the venue.\"", "replace": "\"Произошла ошибка при получении места проведения.\"" }, "37": { "match": "\"Concerts\"", "replace": "\"Концерты\"" }, "38": { "match": "\"all times are local to the venue\"", "replace": "\"все время указано по месту проведения\"" }, "39": { "match": "\"Spotify Spotlight Presale\"", "replace": "\"Предпродажа Spotify Spotlight\"" }, "40": { "match": "\"Get access\"", "replace": "\"Получить доступ\"" }, "41": { "match": "\"Continue playing\"", "replace": "\"Продолжить воспроизведение\"" }, "42": { "match": "\"Fans First\"", "replace": "\"Для фанатов\"" }, "43": { "match": "\"Offer ends in .0.:.1.:.2.\"", "replace": "\"До конца предложения: {0}:{1}:{2}\"" }, "44": { "match": "\"Search in Authors\"", "replace": "\"Искать в Авторах\"" }, "45": { "match": "\"A soundtrack for every part of the day.\"", "replace": "\"Саундтрек для любого времени суток.\"" }, "46": { "match": "\"See your unique playlist\"", "replace": "\"Посмотрите свой уникальный плейлист\"" }, "47": { "match": "\"You.ll still get:\"", "replace": "\"Вы также получите:\"" }, "48": { "match": "\"Trending songs in .country. . Spotify\"", "replace": "\"Популярные песни в %country% | Spotify\"" }, "49": { "match": "\"Featured Charts in .country. . Spotify\"", "replace": "\"Избранные чарты в %country% . Spotify\"" }, "50": { "match": "\"Popular artists in .country. . Spotify\"", "replace": "\"Популярные исполнители в %country% | Spotify\"" }, "51": { "match": "\"Popular albums in .country. . Spotify\"", "replace": "\"Популярные альбомы в %country% | Spotify\"" }, "52": { "match": "\"Discover trending songs in .country. today.\"", "replace": "\"Откройте для себя трендовые песни в %country% сегодня.\"" }, "53": { "match": "\"Discover Featured Charts in .country. today.\"", "replace": "\"Откройте для себя избранные чарты в %country% сегодня.\"" }, "54": { "match": "\"Discover the most popular artists in .country. today.\"", "replace": "\"Откройте для себя самых популярных исполнителей в %country% сегодня.\"" }, "55": { "match": "\"Discover the most popular albums in .country. today.\"", "replace": "\"Откройте для себя самые популярные альбомы в %country% сегодня.\"" }, "56": { "match": "\"Close\"", "replace": "\"Закрыть\"" }, "57": { "match": "\"Ok, we won.t play music from this artist again.\"", "replace": "\"Хорошо, мы больше не будем воспроизводить музыку этого исполнителя\"" }, "58": { "match": "\"Compact list\"", "replace": "\"Компактный список\"" }, "59": { "match": "\"Compact grid\"", "replace": "\"Компактная сетка\"" }, "60": { "match": "\"Lyrics preview\"", "replace": "\"Превью текста\"" }, "61": { "match": "\"Show less\"", "replace": "\"Свернуть\"" }, "62": { "match": "\"Show more\"", "replace": "\"Развернуть\"" }, "63": { "match": "\"Hide lyrics\"", "replace": "\"Скрыть текст\"" }, "64": { "match": "\"Переключиться на видеорежим\"", "replace": "\"К видео\"" }, "65": { "match": "\"Переключиться на аудиорежим\"", "replace": "\"К аудио\"" }, "66": { "match": "\".\".name..\" was released .years. year ago this week!\"", "replace": "\"На этой неделе исполнился %years% год с момента выхода \\\"%name%\\\"!\"" }, "67": { "match": "((?:few|other)..)\".\".name..\" was released .years. years ago this week!\"", "replace": "$1\"На этой неделе исполнилось %years% года с момента выхода \\\"%name%\\\"!\"" }, "68": { "match": "(many..)\".\".name..\" was released .years. years ago this week!\"", "replace": "$1\"На этой неделе исполнилось %years% лет с момента выхода \\\"%name%\\\"!\"" }, "69": { "match": "\".Select Type. \"", "replace": "\"<Выберите тип>\"" }, "70": { "match": "\"Connect\"", "replace": "\"Подключить устройство\"" }, "71": { "match": "\"Синхронизация\"", "replace": "\"Синхронизировать\"" }, "72": { "match": "\"Browse the live events feed to find more concerts.\"", "replace": "\"Листайте нашу афишу, чтобы найти больше концертов.\"" }, "73": { "match": "\"No concerts found\"", "replace": "\"Концертов не найдено\"" }, "74": { "match": "\"Browse the live events feed to find more venues.\"", "replace": "\"Листайте нашу афишу, чтобы найти больше площадок.\"" }, "75": { "match": "\"No venues found\"", "replace": "\"Площадок не найдено\"" }, "76": { "match": "\"Concerts in Your Top Genre\"", "replace": "\"Концерты в вашем любимом жанре\"" }, "77": { "match": "\"Follow your favorite venues to discover and browse events happening near you\"", "replace": "\"Подписывайтесь на любимые площадки, чтобы открывать и просматривать события рядом с вами\"" }, "78": { "match": "\"Search results\"", "replace": "\"Результаты поиска\"" }, "79": { "match": "\"BPM\"", "replace": "\"Темп\"" }, "80": { "match": "\"Folder name is required\"", "replace": "\"Имя папки обязательно\"" }, "81": { "match": "\"Generate\"", "replace": "\"Сгенерировать\"" }, "82": { "match": "\"Retry\"", "replace": "\"Повторить\"" }, "83": { "match": "\"Daily\"", "replace": "\"Ежедневно\"" }, "84": { "match": "\"Tracks update every day by 6am\"", "replace": "\"Треки обновляются каждый день к 6 утра\"" }, "85": { "match": "\"Doesn.t update\"", "replace": "\"Не обновляется\"" }, "86": { "match": "\"Set updates\"", "replace": "\"Настроить обновления\"" }, "87": { "match": "\"Never\"", "replace": "\"Никогда\"" }, "88": { "match": "\"You can still update tracks manually\"", "replace": "\"Вы все еще можете обновлять треки вручную\"" }, "89": { "match": "\"Unknown\"", "replace": "\"Неизвестно\"" }, "90": { "match": "\"Updates daily\"", "replace": "\"Обновляется ежедневно\"" }, "91": { "match": "\"Updates weekly\"", "replace": "\"Обновляется еженедельно\"" }, "92": { "match": "\"Weekly\"", "replace": "\"Еженедельно\"" }, "93": { "match": "\"Every week at 6am every...\"", "replace": "\"Каждую неделю в 6 утра каждый...\"" }, "94": { "match": "\"Updates every .0.\"", "replace": "\"Обновляется каждый {0}\"" }, "95": { "match": "\"Tracks in this playlist will update daily\"", "replace": "\"Треки в этом плейлисте будут обновляться ежедневно\"" }, "96": { "match": "\"Tracks in this playlist won.t update\"", "replace": "\"Треки в этом плейлисте не будут обновляться\"" }, "97": { "match": "\"Tracks in this playlist will update every .0.\"", "replace": "\"Треки в этом плейлисте будут обновляться каждый {0}\"" }, "98": { "match": "\"Got it\"", "replace": "\"Понятно\"" }, "99": { "match": "\"This.ll reset in .0., so come back then.\"", "replace": "\"Сброс через {0}, возвращайтесь позже.\"" }, "100": { "match": "\"Dismiss\"", "replace": "\"Отклонить\"" }, "101": { "match": "\"You.ve reached the limit for prompting playlists\"", "replace": "\"Вы достигли лимита создания плейлистов по запросу\"" }, "102": { "match": "\"Prompt\"", "replace": "\"Запрос\"" }, "103": { "match": "\"Prompted Playlist\"", "replace": "\"Плейлист по запросу\"" }, "104": { "match": "\"Customize prompt\"", "replace": "\"Настроить запрос\"" }, "105": { "match": "\"Friday\"", "replace": "\"Пятница\"" }, "106": { "match": "\"Monday\"", "replace": "\"Понедельник\"" }, "107": { "match": "\"Saturday\"", "replace": "\"Суббота\"" }, "108": { "match": "\"Sunday\"", "replace": "\"Воскресенье\"" }, "109": { "match": "\"Thursday\"", "replace": "\"Четверг\"" }, "110": { "match": "\"Tuesday\"", "replace": "\"Вторник\"" }, "111": { "match": "\"Wednesday\"", "replace": "\"Среда\"" }, "112": { "match": "\"Generate playlist\"", "replace": "\"Создать плейлист\"" }, "113": { "match": "\"See what this prompt would create based on your tastes\"", "replace": "\"Посмотрите, что этот запрос создаст на основе ваших вкусов\"" }, "114": { "match": "\"Beta\"", "replace": "\"Бета\"" }, "115": { "match": "\"Generate a playlist that curates and updates\"", "replace": "\"Создать плейлист, который подбирается и обновляется\"" }, "116": { "match": "\"Update now\"", "replace": "\"Обновить сейчас\"" }, "117": { "match": "\"Something went wrong. Please try again\"", "replace": "\"Что-то пошло не так. Пожалуйста, попробуйте снова\"" }, "118": { "match": "\"Private\"", "replace": "\"Приватный\"" }, "119": { "match": "\"Public\"", "replace": "\"Публичный\"" }, "120": { "match": "\"Set privacy\"", "replace": "\"Настроить приватность\"" }, "121": { "match": "\"Only you and people you invite will be able to view this prompt and playlist\"", "replace": "\"Только вы и приглашенные вами люди смогут видеть этот запрос и плейлист\"" }, "122": { "match": "\"Anyone will be able to view this prompt and playlist\"", "replace": "\"Любой сможет видеть этот запрос и плейлист\"" }, "123": { "match": "\"Learn\"", "replace": "\"Узнать\"" }, "124": { "match": "\"You.re prompting with an AI. Don.t include any sensitive data. .learn. how your data is managed and shared.\"", "replace": "\"Вы делаете запрос к ИИ. Не указывайте конфиденциальные данные. %learn%, как управляются и передаются ваши данные.\"" }, "125": { "match": "\"New prompt\"", "replace": "\"Новый запрос\"" }, "126": { "match": "\"Edit prompt\"", "replace": "\"Изменить запрос\"" }, "127": { "match": "\"Analyzing...\"", "replace": "\"Анализ...\"" }, "128": { "match": "\"Update playlist\"", "replace": "\"Обновить плейлист\"" }, "129": { "match": "\"Updating playlist\"", "replace": "\"Обновление плейлиста\"" }, "130": { "match": "\"Prompt playlist options\"", "replace": "\"Настройки плейлиста по запросу\"" }, "131": { "match": "\"Describe your perfect playlist, in as much detail as you want...\"", "replace": "\"Опишите свой идеальный плейлист, настолько подробно, насколько хотите...\"" }, "132": { "match": "\"Made for\"", "replace": "\"Сделано для\"" }, "133": { "match": "\"Prompted by\"", "replace": "\"По запросу\"" }, "134": { "match": "\"Ideas\"", "replace": "\"Идеи\"" }, "135": { "match": "\"This.ll reset soon.\"", "replace": "\"Скоро сбросится.\"" }, "136": { "match": "\"Hide ideas\"", "replace": "\"Скрыть идеи\"" }, "137": { "match": "\"Show ideas\"", "replace": "\"Показать идеи\"" }, "138": { "match": "\"Name & details\"", "replace": "\"Название и детали\"" }, "139": { "match": "\"Notes\"", "replace": "\"Заметки\"" }, "140": { "match": "\"View prompt\"", "replace": "\"Посмотреть запрос\"" }, "141": { "match": "\"Discard this prompt.\"", "replace": "\"Отменить этот запрос?\"" }, "142": { "match": "\"Cancel\"", "replace": "\"Отмена\"" }, "143": { "match": "\"Discard\"", "replace": "\"Отменить\"" }, "144": { "match": "\"Any changes you.ve made won.t be saved.\"", "replace": "\"Любые внесенные изменения не будут сохранены.\"" }, "145": { "match": "\"What you.ll get\"", "replace": "\"Что вы получите\"" }, "146": { "match": "\"Collapse .0.\"", "replace": "\"Свернуть {0}\"" }, "147": { "match": "\"Collapsed .0.\"", "replace": "\"Свернуто {0}\"" }, "148": { "match": "\"Audiobooks\"", "replace": "\"Аудиокниги\"" }, "149": { "match": "\"Music\"", "replace": "\"Музыка\"" }, "150": { "match": "\"Podcasts\"", "replace": "\"Подкасты\"" }, "151": { "match": "\"Expand .0.\"", "replace": "\"Развернуть {0}\"" }, "152": { "match": "\"Expanded .0.\"", "replace": "\"Развернуто {0}\"" }, "153": { "match": "\"Loading more items\"", "replace": "\"Загрузка дополнительных элементов\"" }, "154": { "match": "\"More items loaded\"", "replace": "\"Загружено больше элементов\"" }, "155": { "match": "\"Played\"", "replace": "\"Прослушано\"" }, "156": { "match": "\"Saved\"", "replace": "\"Сохранено\"" }, "157": { "match": "\"Shared\"", "replace": "\"Поделились\"" }, "158": { "match": "\"Generating...\"", "replace": "\"Генерация...\"" }, "159": { "match": "\"Made for .0.\"", "replace": "\"Сделано для {0}\"" }, "160": { "match": "\"Prompted by .0.\"", "replace": "\"Запрос от {0}\"" } } ================================================ FILE: patches/patches.json ================================================ { "free": { "fullscreen": { "version": { "fr": "1.1.59", "to": "1.1.92" }, "match": "(return|.=.=>)\"free\"===(.+?)(return|.=.=>)\"premium\"===", "replace": "$1\"premium\"===$2$3\"free\"===" }, "audioads": { "version": { "fr": "1.1.59", "to": "1.1.92" }, "match": "(case .:|async enable\\(.\\){)(this.enabled=.+?\\(.{1,3},\"audio\"\\),|return this.enabled=...+?\\(.{1,3},\"audio\"\\))((;case 4:)?this.subscription=this.audioApi).+?this.onAdMessage\\)", "replace": "$1$3.cosmosConnector.increaseStreamTime(-100000000000)" }, "emptyblock": { "version": { "fr": "1.1.59", "to": "" }, "match": "adsEnabled:!0", "replace": "adsEnabled:!1" }, "playlistsponsor": { "version": { "fr": "1.1.59", "to": "" }, "match": "allSponsorships", "replace": "" }, "connectold": { "version": { "fr": "1.1.70", "to": "1.1.92" }, "match": [ " connect-device-list-item--disabled", "connect-picker.unavailable-to-control", "(\"button\",{className:.,disabled:)(..)" ], "replace": [ "", "spotify-connect", "$1false" ] }, "downloadquality": { "version": { "fr": "1.1.70", "to": "1.2.29" }, "match": "(\\(.,..jsxs\\)\\(.{1,3}|(.\\(\\).|..)createElement\\(.{1,4}),{(filterMatchQuery|filter:.,title|(variant:\"viola\",semanticColor:\"textSubdued\"|..:\"span\",variant:.{3,6}mesto,color:.{3,6}),htmlFor:\"desktop.settings.downloadQuality.+?).{1,6}get\\(\"desktop.settings.downloadQuality.title.+?(children:.{1,2}\\(.,.\\).+?,|\\(.,.\\){3,4},|,.\\)}},.\\(.,.\\)\\),)", "replace": "" } }, "others": { "discriptions": { "version": { "fr": "1.1.59", "to": "" }, "match": "((..createElement|children:\\(.{1,7}\\))\\(.{1,7},{source:).{1,7}get\\(\"about.copyright\",.\\),paragraphClassName:(?:\"[\\w]+\"|\\w+)}\\)", "replace": "

More about SpotX


{0} GithubSpotX-Windows
SpotX-Mac/Linux

{1} TelegramSpotX Community
SpotX Channel

{2} FAQWindows
Mac/Linux

DISCLAIMER

SpotX is a modified version of the official Spotify client, provided as an evaluation version, you use it at your own risk.", "svgtg": "", "svggit": "", "svgfaq": "" }, "ForcedExp": { "version": { "fr": "1.1.67", "to": "" }, "match": "((?:{configuration|{resolver|instance):(.).(?:getRemoteConfig|getUrlDispenserServiceClient).+?;)", "replace": "$1const experiments={enable:[],disable:[],custom:[]},setExpValue=(v,e,val)=>v&&(v.set?v.set(e,val):v[e]&&(v[e].value=val)),config=$2.getRemoteConfigResolver?.()||$2.getRemoteConfiguration?.();config&&((values=>{for(const type in experiments)experiments[type].forEach(exp=>setExpValue(values,type==='custom'?exp.name:exp,type==='custom'?exp.value:type==='enable'));(window.Spotx??={}).RemoteExp=values})(config.values||config.activeProperties));" }, "DisableExp": { "InAppMessaging": { "name": "enableInAppMessaging", "description": "Disable pop-up window for premium account purchase", "native_description": "Enables quicksilver in-app messaging modal", "version": { "fr": "1.1.59", "to": "" } }, "ContentInformationMessage": { "name": "enableContentInformationMessage", "description": "Disable covid messages between podcasts", "native_description": "Enable showing content information messages for episodes", "version": { "fr": "1.1.78", "to": "1.2.50" } }, "PickShuffle": { "name": "enablePickAndShuffle", "description": "Removes restrictions on using the queue and listening to tracks in order", "native_description": "Enable pick and shuffle", "version": { "fr": "1.1.85", "to": "1.2.42" } }, "MusicLeavebehinds": { "name": "enableDesktopMusicLeavebehinds", "description": "Disabling ad blocks in playlists", "native_description": "Enable music leavebehinds on eligible playlists for desktop", "version": { "fr": "1.2.10", "to": "" } }, "HptoLocationRefactor": { "name": "enableHptoLocationRefactor", "description": "Disabling in some cases the banner on the main page", "native_description": "Enable new permanent location for HPTO iframe to HptoHtml.js", "version": { "fr": "1.2.1", "to": "1.2.20" } }, "UserFraudSignals": { "name": "enableUserFraudSignals", "description": "presumably protection against bots (reCAPTCHA)", "native_description": "Enable user fraud signals", "version": { "fr": "1.2.10", "to": "1.2.62" } }, "UserFraudVerificationRequest": { "name": "enableUserFraudVerificationRequest", "description": "presumably protection against bots (reCAPTCHA)", "native_description": "Enable the IAV component make api requests", "version": { "fr": "1.2.5", "to": "1.2.62" } }, "UserFraudVerification": { "name": "enableUserFraudVerification", "description": "presumably protection against bots (reCAPTCHA)", "native_description": "Enable user fraud verification", "version": { "fr": "1.2.3", "to": "1.2.62" } }, "UserFraudCspViolation": { "name": "enableUserFraudCspViolation", "description": "Enable CSP violation detection", "native_description": "Enable CSP violation detection", "version": { "fr": "1.2.17", "to": "1.2.62" } }, "EsperantoMigration": { "name": "enableEsperantoMigration", "description": "presumably, the advertising management transition from Cosmos to Esperanto", "native_description": "Enable esperanto Migration for Ad Formats", "version": { "fr": "1.2.6", "to": "1.2.50" } }, "RightSidebarMerchFallback": { "name": "enableRightSidebarMerchFallback", "description": "Allow merch to fallback to artist level merch if track level does not exist", "native_description": "Allow merch to fallback to artist level merch if track level does not exist", "version": { "fr": "1.2.5", "to": "1.2.11" } }, "ReportAudiobooks": { "name": "enableReportAudiobooks", "description": "Enable Report action for audiobooks", "native_description": "Enable Report action for audiobooks", "version": { "fr": "1.1.97", "to": "1.2.50" } }, "ReportPodcastShows": { "name": "enableReportPodcastShows", "description": "Enable Report action for podcast shows", "native_description": "Enable Report action for podcast shows", "version": { "fr": "1.2.12", "to": "" } }, "ReportPodcastEpisodes": { "name": "enableReportPodcastEpisodes", "description": "Enable Report action for podcast episodes", "native_description": "Enable Report action for podcast episodes", "version": { "fr": "1.2.12", "to": "" } }, "ReportAudiobookChapters": { "name": "enableReportAudiobookChapters", "description": "Enable Report action for audiobook chapters", "native_description": "Enable Report action for audiobook chapters", "version": { "fr": "1.2.12", "to": "1.2.50" } }, "Dsa": { "name": "enableDsa", "description": "Enable showing DSA (Digital Services Act) context menu and modal for ads", "native_description": "Enable showing DSA (Digital Services Act) context menu and modal for ads", "version": { "fr": "1.2.12", "to": "1.2.19" } }, "Dsa2": { "name": "enableDsaAds", "description": "Enable showing DSA (Digital Services Act) context menu and modal for ads", "native_description": "Enable showing DSA (Digital Services Act) context menu and modal for ads", "version": { "fr": "1.2.20", "to": "1.2.52" } }, "Dsa3": { "name": "enableDSASetting", "description": "Enable DSA (Digital Service Act) features for desktop and web", "native_description": "Enable DSA (Digital Service Act) features for desktop and web", "version": { "fr": "1.2.20", "to": "" } }, "NewAdsNpv": { "name": "enableNewAdsNpv", "description": "Enable showing new ads NPV", "native_description": "Enable showing new ads NPV", "version": { "fr": "1.2.18", "to": "1.2.50" } }, "NewAdsNpvVideoTakeover": { "name": "enableNewAdsNpvVideoTakeover", "description": "Enable redesigned VideoTakeover for new ads NPV", "native_description": "Enable redesigned VideoTakeover for new ads NPV", "version": { "fr": "1.2.18", "to": "1.2.50" } }, "NewAdsNpvColorExtraction": { "name": "enableNewAdsNpvColorExtraction", "description": "Enable CTA card color extraction for new ads NPV", "native_description": "Enable CTA card color extraction for new ads NPV", "version": { "fr": "1.2.18", "to": "1.2.50" } }, "AudiobookAdExclusivity": { "name": "enableAudiobookAdExclusivity", "description": "Enable Audiobook Ad Formats Exclusivity", "native_description": "Enable Audiobook Ad Formats Exclusivity", "version": { "fr": "1.2.19", "to": "" } }, "NewAdsNpvNewVideoTakeoverSlot": { "name": "enableNewAdsNpvNewVideoTakeoverSlot", "description": "Enable new modal slot to position redesigned new ads NPV VideoTakeover above all areas except RightSidebar and NPB", "native_description": "Enable new modal slot to position redesigned new ads NPV VideoTakeover above all areas except RightSidebar and NPB", "version": { "fr": "1.2.22", "to": "1.2.50" } }, "FraudLoadSignals": { "name": "enableFraudLoadSignals", "description": "Enable user fraud signals emitted on page load", "native_description": "Enable user fraud signals emitted on page load", "version": { "fr": "1.2.22", "to": "1.2.62" } }, "GabitoAdEvent": { "name": "enableGabitoAdEvent", "description": "Use Gabito AdEvent instead of Gabo AdEvent", "native_description": "Use Gabito AdEvent instead of Gabo AdEvent", "version": { "fr": "1.1.93", "to": "1.2.25" } }, "YourListeningUpsell": { "name": "enableYourListeningUpsell", "description": "Enable Your Listening Upsell Banner for free & unauth users", "native_description": "Enable Your Listening Upsell Banner for free & unauth users", "version": { "fr": "1.2.25", "to": "1.2.63" } }, "podcast-ads": { "name": "podcastads-ads_npb", "description": "Enable the podcast ads Now Playing Bar experience, including features like the podcast ad Now Playing Bar hat", "native_description": "Enable the podcast ads Now Playing Bar experience, including features like the podcast ad Now Playing Bar hat", "version": { "fr": "1.1.67", "to": "1.2.50" } }, "podcast-episode": { "name": "podcastaudioplus-episode_entity", "description": "Enable the Sponsored By section on the Episode Page with Call to Action cards", "native_description": "Enable the Sponsored By section on the Episode Page with Call to Action cards", "version": { "fr": "1.1.67", "to": "1.2.50" } }, "podcast-show_page": { "name": "podcastaudioplus-show_page", "description": "Enable show sponsors section on the Show Page", "native_description": "Enable show sponsors section on the Show Page", "version": { "fr": "1.1.77", "to": "1.2.50" } }, "betamaxAutoSeekToAdPosition": { "name": "AutoSeekToAdPosition", "description": "If true, the player will seek to the ad position when an stop position is registered. Usefull for debugging ads.", "native_description": "If true, the player will seek to the ad position when an stop position is registered. Usefull for debugging ads.", "version": { "fr": "1.2.26", "to": "" } }, "PodcastSponsoredContent": { "name": "enablePodcastSponsoredContent", "description": "Enable sponsored content information for podcasts", "native_description": "Enable sponsored content information for podcasts", "version": { "fr": "1.2.26", "to": "1.2.50" } }, "HomeAds": { "name": "enableHomeAds", "description": "Enable Fist Impression Takeover ads on Home Page", "native_description": "Enable Fist Impression Takeover ads on Home Page", "version": { "fr": "1.2.31", "to": "" } }, "LearningHomeCard": { "name": "enableLearningHomeCard", "description": "Enable Learning Home Card", "native_description": "Enable Learning Home Card", "version": { "fr": "1.2.31", "to": "1.2.40" } }, "BillboardEsperantoMigration": { "name": "enableBillboardEsperantoMigration", "description": "Enable esperanto migration for Billboard Ad Format", "native_description": "Enable esperanto migration for Billboard Ad Format", "version": { "fr": "1.2.32", "to": "1.2.52" } }, "LeaderboardEsperantoMigration": { "name": "enableEsperantoMigrationLeaderboard", "description": "Enable esperanto Migration for Leaderboard Ad Format", "native_description": "Enable esperanto Migration for Leaderboard Ad Format", "version": { "fr": "1.2.32", "to": "" } }, "SponsoredPlaylistEsperantoMigration": { "name": "enableSponsoredPlaylistEsperantoMigration", "description": "Enable esperanto Migration for Sponsored Playlist Ad Formats", "native_description": "Enable esperanto Migration for Sponsored Playlist Ad Formats", "version": { "fr": "1.2.32", "to": "1.2.50" } }, "PipImpressionLogging": { "name": "enablePipImpressionLogging", "description": "Enables impression logging for PiP", "native_description": "Enables impression logging for PiP", "version": { "fr": "1.2.32", "to": "1.2.78" } }, "SwitchingBetweenHomeAdsAndHpto": { "name": "allowSwitchingBetweenHomeAdsAndHpto", "description": "Allow switching between the new Home Ads format and legacy HPTO format (as opposed to only showing the legacy HPTO format)", "native_description": "Allow switching between the new Home Ads format and legacy HPTO format (as opposed to only showing the legacy HPTO format)", "version": { "fr": "1.2.34", "to": "" } }, "LyricsUpsell": { "name": "enableLyricsUpsell", "description": "Enable Lyrics Upsell", "native_description": "Enable Lyrics Upsell", "version": { "fr": "1.2.36", "to": "" } }, "ArtistNPVImpressionsLogging": { "name": "enableArtistNPVImpressions", "description": "Enables impression logging for the Artist About section in NPV", "native_description": "Enables impression logging for the Artist About section in NPV", "version": { "fr": "1.2.37", "to": "1.2.50" } }, "SpotlightImpressionLogging": { "name": "enableSpotlightImpressionLogging", "description": "Enables impression logging for Spotlight", "native_description": "Enables impression logging for Spotlight", "version": { "fr": "1.2.37", "to": "1.2.50" } }, "EnhanceLikedSongs": { "name": "enableEnhanceLikedSongs", "description": "disabled on the server side", "native_description": "Enable Enhance Liked Songs UI and functionality", "version": { "fr": "1.1.86", "to": "1.2.25" } }, "EnhancePlaylist": { "name": "enableEnhancePlaylistProd", "description": "disabled on the server side", "native_description": "Enable Enhance Playlist UI and functionality for end-users", "version": { "fr": "1.1.84", "to": "1.2.25" } }, "SurveyAds": { "name": "enableSurveyAds", "description": "Enable Spotify Brand Lift (SBL) Surveys in the NPV", "native_description": "Enable Spotify Brand Lift (SBL) Surveys in the NPV", "version": { "fr": "1.2.43", "to": "1.2.63" } }, "EFlag": { "name": "enableEFlag", "description": "Enables the Employee Flag to send to Sentry", "native_description": "Enables the Employee Flag to send to Sentry", "version": { "fr": "1.2.44", "to": "" } }, "HomeImpressions": { "name": "enableHomeImpressions", "description": "Enable impressions on home for impression economy (some impressions will still be logged with this disabled)", "native_description": "Enable impressions on home for impression economy (some impressions will still be logged with this disabled)", "version": { "fr": "1.2.44", "to": "1.2.63" } }, "SearchImpressions": { "name": "enableSearchImpressions", "description": "Enable impressions on search for impression economy", "native_description": "Enable impressions on search for impression economy", "version": { "fr": "1.2.44", "to": "1.2.63" } }, "StageDesktopDebug": { "name": "enableTheStageDesktopDebug", "description": "Enable debug messages for The Stage", "native_description": "Enable debug messages for The Stage", "version": { "fr": "1.2.51", "to": "" } }, "NewAdsNpvCanvasAds": { "name": "enableNewAdsNpvCanvasAds", "description": "Enable Canvas ads for new ads NPV", "native_description": "Enable Canvas ads for new ads NPV", "version": { "fr": "1.2.28", "to": "1.2.51" } }, "CanvasAds": { "name": "enableCanvasAds", "description": "Enable Canvas for ads", "native_description": "Enable Canvas for ads", "version": { "fr": "1.2.52", "to": "" } }, "ConnectedStateObserver": { "name": "enableConnectedStateObserver", "description": "Enable observer that logs errors related to connected state and ad info", "native_description": "Enable observer that logs errors related to connected state and ad info", "version": { "fr": "1.2.53", "to": "" } }, "EsperantoAdStateReportManager": { "name": "enableEsperantoAdStateReportManager", "description": "Enables esperanto migration of the adStateReportManager", "native_description": "Enables esperanto migration of the adStateReportManager", "version": { "fr": "1.2.55", "to": "1.2.60" } }, "EventsInYourLibrary": { "name": "enableEventsInYourLibrary", "description": "Enable event chips and event items in your library", "native_description": "Enable event chips and event items in your library", "version": { "fr": "1.2.55", "to": "1.2.71" } }, "LeavebehindsMockData": { "name": "enableLeavebehindsMockData", "description": "Use the mock2 endpoint to fetch Leavebehinds from AP4P", "native_description": "Use the mock2 endpoint to fetch Leavebehinds from AP4P", "version": { "fr": "1.2.30", "to": "" } }, "fixColorLyricsBug": { "name": "enableDynamicColors", "description": "if disabled then it fixes the bug with dynamic colors in lyrics", "native_description": "Enable dynamic colors for the app", "version": { "fr": "1.2.47", "to": "1.2.56" } }, "EmbeddedNpvAds": { "name": "enableEmbeddedNpvAds", "description": "Enable embedded display ads on NPV", "native_description": "Enable embedded display ads on NPV", "version": { "fr": "1.2.57", "to": "1.2.77" } }, "EnhancedAdsClientDeconfliction": { "name": "enableEnhancedAdsClientDeconfliction", "description": "Enable refactored version of ads orchestrator middleware", "native_description": "Enable refactored version of ads orchestrator middleware", "version": { "fr": "1.2.57", "to": "1.2.61" } }, "AdCountUi": { "name": "enableAdCountUi", "description": "Enable ad count feedback in NPV", "native_description": "Enable ad count feedback in NPV", "version": { "fr": "1.2.59", "to": "1.2.82" } }, "SaxLeaderboardAds": { "name": "enableSaxLeaderboardAds", "description": "Enable SAX Leaderboard Ad Format", "native_description": "Enable SAX Leaderboard Ad Format", "version": { "fr": "1.2.62", "to": "1.2.82" } }, "EmbeddedAdVisibilityLogging": { "name": "enableEmbeddedAdVisibilityLogging", "description": "When enabled, enhanced visibility logs will be sent for embedded ads", "native_description": "EWhen enabled, enhanced visibility logs will be sent for embedded ads", "version": { "fr": "1.2.63", "to": "1.2.77" } }, "Hpto": { "name": "enableHpto", "description": "Enable Hpto announcements on Home", "native_description": "Enable Hpto announcements on Home", "version": { "fr": "1.2.65", "to": "" } }, "SponsoredPlaylistV2": { "name": "enableSponsoredPlaylistV2", "description": "Enables the new suite of interfaces for Sponsored Playlists V2", "native_description": "Enables the new suite of interfaces for Sponsored Playlists V2", "version": { "fr": "1.2.66", "to": "" } }, "SponsoredPlaylistV2ScrollCard": { "name": "enableSponsoredPlaylistV2ScrollCard", "description": "Enables the music NPV scroll card rendering during a sponsored playlist playback", "native_description": "Enables the music NPV scroll card rendering during a sponsored playlist playback", "version": { "fr": "1.2.66", "to": "" } }, "EmbeddedAdsCarousel": { "name": "enableEmbeddedAdsCarousel", "description": "Enable embedded ads carousel for the NPV", "native_description": "Enable embedded ads carousel for the NPV", "version": { "fr": "1.2.73", "to": "" } }, "EmbeddedAdsFetchingOverCanvas": { "name": "enableEmbeddedAdsFetchingOverCanvas", "description": "Enable embedded ads fetching when canvas track is playing. Defaults to true since this is currently existing behavior", "native_description": "Enable embedded ads fetching when canvas track is playing. Defaults to true since this is currently existing behavior", "version": { "fr": "1.2.72", "to": "1.2.77" } }, "SponsoredPlaylistMockEndpoint": { "name": "enableSponsoredPlaylistMockEndpoint", "description": "Enables the endpoint to fetch mock sponsorships for e2e testing", "native_description": "Enables the endpoint to fetch mock sponsorships for e2e testing", "version": { "fr": "1.2.74", "to": "" } }, "WatchFeedPreviewReporting": { "name": "enableWatchFeedPreviewReporting", "description": "Enable watch feed preview reporting (requires enableOutroPreviewReporting)", "native_description": "Enable watch feed preview reporting (requires enableOutroPreviewReporting)", "version": { "fr": "1.2.70", "to": "" } }, "AgeAssuranceContent": { "name": "enableAgeAssuranceContent", "description": "Enables the age assurance feature for content aspects (e.g., switch to video)", "native_description": "Enables the age assurance feature for content aspects (e.g., switch to video)", "version": { "fr": "1.2.77", "to": "" } }, "AgeAssuranceFriendActivity": { "name": "enableAgeAssuranceFriendActivity", "version": { "fr": "1.2.78", "to": "" } }, "AgeAssuranceComments": { "name": "enableAgeAssuranceComments", "version": { "fr": "1.2.78", "to": "" } }, "AgeAssuranceProfileMenu": { "name": "enableAgeAssuranceProfileMenu", "version": { "fr": "1.2.78", "to": "" } }, "AgeAssuranceSettings": { "name": "enableAgeAssuranceSettings", "version": { "fr": "1.2.78", "to": "" } }, "UnderAgeBlockingModal": { "name": "enableUnderAgeBlockingModal", "native_description": "Enables the underage blocking modal for accounts in blocked/pending disabled state", "version": { "fr": "1.2.78", "to": "" } }, "AdImpressionDoesNotIgnoreVisilibility": { "name": "embeddedAdImpressionDoesNotIgnoreVisilibility", "native_description": "If enabled, we do consider percent visibility when logging the display ad impression", "version": { "fr": "1.2.78", "to": "" } }, "MadeForYou": { "name": "enableMadeForYouEntryPoint", "description": "Show 'Made For You' entry point in the left sidebar", "version": { "fr": "1.1.67", "to": "1.1.95" } }, "bypassApplyUpdateCheck": { "name": "bypassApplyUpdateCheck", "version": { "fr": "1.2.84", "to": "" } } }, "EnableExp": { "HomeViaGraphQLV2": { "name": "enableHomeViaGraphQLV2", "description": "Enable fetching Home via GraphQL", "native_description": "Enable fetching Home via GraphQL", "version": { "fr": "1.1.86", "to": "1.1.92" } }, "BrowseViaPathfinder": { "name": "enableBrowseViaPathfinder", "description": "Fixes a bug on the genres page", "native_description": "Fetch Browse data from Pathfinder", "version": { "fr": "1.1.88", "to": "1.2.24" } }, "IgnoreInRecommendations": { "name": "enableIgnoreInRecommendations", "description": "Exclude playlists from recommendations", "native_description": "Enable Ignore In Recommendations for desktop and web", "version": { "fr": "1.1.87", "to": "1.2.50" } }, "Equalizer": { "name": "enableEqualizer", "description": "Enable audio equalizer for Desktop and Web Player", "native_description": "Enable audio equalizer for Desktop and Web Player", "version": { "fr": "1.1.88", "to": "" } }, "CarouselsOnHome": { "name": "enableCarouselsOnHome", "description": "Directly related to the blocking of podcasts and sections similar to ads on the main page", "native_description": "Use carousels on Home", "version": { "fr": "1.1.93", "to": "1.2.25" } }, "AttackOnTitanEaster": { "name": "enableAttackOnTitanEasterEgg", "description": "Enable Attack on Titan Easter egg turning progress bar red when playing official soundtrack", "native_description": "Enable Attack on Titan Easter egg turning progress bar red when playing official soundtrack", "version": { "fr": "1.2.6", "to": "1.2.50" } }, "AlbumReleaseAnniversaries": { "name": "enableAlbumReleaseAnniversaries", "description": "Enable showing balloons on album release date anniversaries", "native_description": "Enable showing balloons on album release date anniversaries", "version": { "fr": "1.1.89", "to": "" } }, "ClearAllDownloads": { "name": "enableClearAllDownloads", "description": "Enable option in settings to clear all downloads", "native_description": "Enable option in settings to clear all downloads", "version": { "fr": "1.1.92", "to": "1.1.98" } }, "LeftSidebar": { "name": "enableYLXSidebar", "description": "Enable Your Library X view of the left sidebar", "native_description": "Enable Your Library X view of the left sidebar", "version": { "fr": "1.2.0", "to": "1.2.14" } }, "RightSidebar": { "name": "enableRightSidebar", "description": "Enable the view on the right sidebar", "native_description": "Enable the view on the right sidebar", "version": { "fr": "1.1.98", "to": "1.2.23" } }, "AddPlaylistToPlaylist": { "name": "enableAddPlaylistToPlaylist", "description": "Enable support for adding a playlist to another playlist", "native_description": "Enable support for adding a playlist to another playlist", "version": { "fr": "1.1.98", "to": "1.2.3" } }, "RightSidebarLyrics": { "name": "enableRightSidebarLyrics", "description": "Show lyrics in the right sidebar (disables lyrics on bottom bar)", "native_description": "Show lyrics in the right sidebar", "version": { "fr": "1.2.0", "to": "1.2.61" } }, "RightSidebarColors": { "name": "enableRightSidebarExtractedColors", "description": "Extract background color based on artwork image", "native_description": "Extract background color based on artwork image", "version": { "fr": "1.2.1", "to": "1.2.78" } }, "SilenceTrimmer": { "name": "enableSilenceTrimmer", "description": "Enable silence trimming in podcasts", "native_description": "Enable silence trimming in podcasts", "version": { "fr": "1.1.99", "to": "" } }, "PlaybackSpeedIncrements": { "name": "enableSmallPlaybackSpeedIncrements", "description": "playback speed range from 0.5-3.5 with every 0.1 increment (in podcasts)", "native_description": "playback speed range from 0.5-3.5 with every 0.1 increment", "version": { "fr": "1.2.0", "to": "1.2.14" } }, "ShowFollowsSetting": { "name": "enableShowFollowsSetting", "description": "Enable a setting to control if followers and following lists are shown on profile", "native_description": "Enable a setting to control if followers and following lists are shown on profile", "version": { "fr": "1.2.1", "to": "1.2.50" } }, "RightSidebarCredits": { "name": "enableRightSidebarCredits", "description": "Show credits in the right sidebar", "native_description": "Show credits in the right sidebar", "version": { "fr": "1.2.7", "to": "1.2.25" } }, "WhatsNewFeed": { "name": "enableWhatsNewFeed", "description": "Enable the what's new feed panel", "native_description": "Enable the what's new feed panel", "version": { "fr": "1.2.12", "to": "1.2.16" } }, "WhatsNewFeedMainView": { "name": "enableWhatsNewFeedMainView", "description": "Enable Whats new feed in the main view", "native_description": "Enable Whats new feed in the main view", "version": { "fr": "1.2.17", "to": "1.2.45" } }, "RightSidebarArtistEnhanced": { "name": "enableRightSidebarArtistEnhanced", "description": "Enable Artist about V2 section in NPV", "native_description": "Enable Artist about V2 section in NPV", "version": { "fr": "1.2.16", "to": "1.2.50" } }, "NewEntityHeaders": { "name": "enableNewEntityHeaders", "description": "Enable New Entity Headers", "native_description": "Enable New Entity Headers", "version": { "fr": "1.2.15", "to": "1.2.27" } }, "ReadAlongTranscripts": { "name": "enableReadAlongTranscripts", "description": "Enable read along transcripts in the NPV", "native_description": "Enable read along transcripts in the NPV", "version": { "fr": "1.2.17", "to": "1.2.62" } }, "RightSidebarTransitionAnimations": { "name": "enableRightSidebarTransitionAnimations", "description": "Enable the slide-in/out transition on the right sidebar", "native_description": "Enable the slide-in/out transition on the right sidebar", "version": { "fr": "1.2.8", "to": "1.2.33" } }, "YLXEnhancements": { "name": "enableYLXEnhancements", "description": "Enable Your Library X Enhancements", "native_description": "Enable Your Library X Enhancements", "version": { "fr": "1.2.18", "to": "1.2.50" } }, "SmallerLineHeight": { "name": "enableSmallerLineHeight", "description": "Enable line height 1.5 on the ", "native_description": "Enable line height 1.5 on the ", "version": { "fr": "1.2.18", "to": "1.2.23" } }, "ConcertsInterested": { "name": "enableConcertsInterested", "description": "Enable Save and Retrieve feature for concerts", "native_description": "Enable Save and Retrieve feature for concerts", "version": { "fr": "1.2.7", "to": "1.2.62" } }, "ConcertsForThisIsPlaylist": { "name": "enableConcertsForThisIsPlaylist", "description": "Enable Tour Card on This is Playlist", "native_description": "Enable Tour Card on This is Playlist", "version": { "fr": "1.2.11", "to": "1.2.62" } }, "RemoteDownloads": { "name": "enableRemoteDownloads", "description": "Enable remote download support through the offline2 system", "native_description": "Enable remote download support through the offline2 system", "version": { "fr": "1.2.8", "to": "" } }, "AlbumCoverArtModal": { "name": "enableAlbumCoverArtModal", "description": "Enable the cover art modal on the Album page", "native_description": "Enable the cover art modal on the Album page", "version": { "fr": "1.2.13", "to": "1.2.50" } }, "SmartShuffle": { "name": "enableSmartShuffle", "description": "Enable Smart Shuffle in playlists", "native_description": "Enable Smart Shuffle", "version": { "fr": "1.2.22", "to": "" } }, "ConcertsTicketPrice": { "name": "enableConcertsTicketPrice", "description": "Display ticket price on Event page", "native_description": "Display ticket price on Event page", "version": { "fr": "1.2.15", "to": "1.2.62" } }, "DynamicNormalizer": { "name": "enableDynamicNormalizer", "description": "Enable the dynamic normalizer/compressor", "native_description": "Enable the dynamic normalizer/compressor", "version": { "fr": "1.2.14", "to": "1.2.60" } }, "HeBringsNpb": { "name": "enableHeBringsNpb", "description": "he brings you playback progress", "native_description": "he brings you playback progress", "version": { "fr": "1.2.14", "to": "1.2.50" } }, "AlbumPrerelease": { "name": "enableAlbumPrerelease", "description": "Enable album prerelease pages", "native_description": "Enable album prerelease pages", "version": { "fr": "1.2.18", "to": "1.2.50" } }, "ButtonAlwaysVisible": { "name": "saveButtonAlwaysVisible", "description": "Display save button always in whats new feed", "native_description": "Display save button always in whats new feed", "version": { "fr": "1.2.20", "to": "1.2.27" } }, "NpvAboutPodcast": { "name": "enableNpvAboutPodcast", "description": "Enable About Podcast section in NPV", "native_description": "Enable About Podcast section in NPV", "version": { "fr": "1.2.20", "to": "" } }, "QueueOnRightPanel": { "name": "enableQueueOnRightPanel", "description": "Enable Queue on the right panel", "native_description": "Enable Queue on the right panel", "version": { "fr": "1.2.26", "to": "1.2.61" } }, "RecentlyPlayedShortcut": { "name": "enableRecentlyPlayedShortcut", "description": "Show Recently Played shortcut in home view. Also increase max number of shortcuts to 8", "native_description": "Show Recently Played shortcut in home view. Also increase max number of shortcuts to 8", "version": { "fr": "1.2.21", "to": "1.2.25" } }, "EncoreCards": { "name": "enableEncoreCards", "description": "Enables all cards throughout app to be Encore Cards", "native_description": "Enables all cards throughout app to be Encore Cards", "version": { "fr": "1.2.26", "to": "1.2.33" } }, "Plus": { "name": "enableAlignedCuration", "description": "it`s killing the heart icon, you`re able to save and choose the destination for any song, playlist, or podcast", "native_description": "Enable Aligned Curation", "version": { "fr": "1.2.21", "to": "1.2.50" } }, "AlignedCurationSavedIn": { "name": "enableAlignedCurationSavedIn", "description": "Enables the Saved In section in Aligned Curation", "native_description": "Enables the Saved In section in Aligned Curation", "version": { "fr": "1.2.40", "to": "1.2.47" } }, "ViewMode": { "name": "enableViewMode", "description": "Added compact mode to playlist", "native_description": "Enable list / compact mode in entity pages", "version": { "fr": "1.2.24", "to": "" } }, "UserTopContent": { "name": "showUserTopContent", "description": "Show top tracks and top artists on profile page", "native_description": "Show top tracks and top artists on profile page", "version": { "fr": "1.2.22", "to": "" } }, "EightShortcuts": { "name": "enableEightShortcuts", "description": "Increase max number of shortcuts on home to 8", "native_description": "Increase max number of shortcuts on home to 8", "version": { "fr": "1.2.26", "to": "1.2.45" } }, "NPVCredits": { "name": "enableNPVCredits", "description": "Show credits in the right sidebar", "native_description": "Show credits in the right sidebar", "version": { "fr": "1.2.26", "to": "1.2.26" } }, "NPVCredits2": { "name": "enableNPVCreditsWithLinkability", "description": "Show credits in the right sidebar", "native_description": "Show credits in the right sidebar", "version": { "fr": "1.2.27", "to": "1.2.50" } }, "BanArtistAction": { "name": "enableBanArtistAction", "description": "Enable context menu action to ban/unban artists", "native_description": "Enable context menu action to ban/unban artists", "version": { "fr": "1.2.27", "to": "1.2.42" } }, "ArtistBans": { "name": "enableArtistBans", "description": "Enable context menu action to ban/unban artists", "native_description": "Enable context menu action to ban/unban artists", "version": { "fr": "1.2.43", "to": "1.2.50" } }, "LyricsFullAvailabilityCheck": { "name": "enableLyricsFullAvailabilityCheck", "description": "Enable full availability check before showing the mic button for lyrics", "native_description": "Enable full availability check before showing the mic button for lyrics", "version": { "fr": "1.2.43", "to": "" } }, "GlobalNavBar": { "name": "enableGlobalNavBar", "description": "Show global nav bar with home button, search input and user avatar", "native_description": "Show global nav bar with home button, search input and user avatar", "version": { "fr": "1.2.27", "to": "1.2.29" } }, "NotificationCenter": { "name": "enableNotificationCenter", "description": "Enables notification center for desktop & web", "native_description": "Enables notification center for desktop & web", "version": { "fr": "1.2.75", "to": "" } }, "DevicePickerSidePanel": { "name": "enableDevicePickerSidePanel", "description": "Enables Device Picker as a side panel", "native_description": "Enables Device Picker as a side panel", "version": { "fr": "1.2.30", "to": "1.2.45" } }, "Otfn": { "name": "enableOtfn", "description": "Enable On-The-Fly-Normalization", "native_description": "Enable On-The-Fly-Normalization", "version": { "fr": "1.2.31", "to": "" } }, "FullscreenMode": { "name": "enableFullscreenMode", "description": "Enable fullscreen mode", "native_description": "Enable fullscreen mode", "version": { "fr": "1.2.31", "to": "" } }, "NewConnectBar": { "name": "enableNewConnectBar", "description": "New Connect Bar without arrow and clickable elements", "native_description": "New Connect Bar without arrow and clickable elements", "version": { "fr": "1.2.14", "to": "1.2.35" } }, "ResizableTracklistColumns": { "name": "enableResizableTracklistColumns", "description": "Enable resizable tracklist columns", "native_description": "Enable resizable tracklist columns", "version": { "fr": "1.2.28", "to": "1.2.66" } }, "QueueOnRightPanelAnimations": { "name": "enableQueueOnRightPanelAnimations", "description": "Enable animations for Queue on the right panel", "native_description": "Enable animations for Queue on the right panel", "version": { "fr": "1.2.32", "to": "1.2.50" } }, "LikedSongsFilterTags": { "name": "enableLikedSongsFilterTags", "description": "Show filter tags on the Liked Songs entity view", "native_description": "Show filter tags on the Liked Songs entity view", "version": { "fr": "1.2.29", "to": "" } }, "MiniPlayer": { "name": "enablePiPMiniPlayer", "description": "Enable the PiP Mini Player", "native_description": "Enable the PiP Mini Player", "version": { "fr": "1.2.32", "to": "" } }, "MiniPlayerVideo": { "name": "enablePiPMiniPlayerVideo", "description": "Enable playback of video inside the PiP Mini Player", "native_description": "Enable playback of video inside the PiP Mini Player", "version": { "fr": "1.2.32", "to": "" } }, "PremiumUserForMiniPlayer": { "name": "enablePremiumUserForMiniPlayer", "description": "Enables premium user flag for mini player", "native_description": "Enables premium user flag for mini player", "version": { "fr": "1.2.32", "to": "" } }, "HomeSubfeeds": { "name": "enableHomeSubfeeds", "description": "Enable Subfeed filter chips on home", "native_description": "Enable Subfeed filter chips on home", "version": { "fr": "1.2.23", "to": "1.2.44" } }, "SidebarAnimations": { "name": "enableSidebarAnimations", "description": "Enable animations on the left and right on the sidebars and makes the right sidebar collapsible", "native_description": "Enable animations on the left and right on the sidebars and makes the right sidebar collapsible", "version": { "fr": "1.2.34", "to": "1.2.37" } }, "RightSidebarCollapsible": { "name": "enableRightSidebarCollapsible", "description": "Enable the right sidebar to collapse into the right margin", "native_description": "Enable the right sidebar to collapse into the right margin", "version": { "fr": "1.2.34", "to": "1.2.37" } }, "Spotlight": { "name": "enableSpotlight", "description": "adds a new section to the main page 'More like on...'", "native_description": "Enables the Spotlight component on home", "version": { "fr": "1.2.37", "to": "1.2.62" } }, "SharingButtonOnMiniPlayer": { "name": "enableSharingButtonOnMiniPlayer", "description": "Enables sharing button on MiniPlayer (this also moves the (+) icon close to the title)", "native_description": "Enables sharing button on MiniPlayer (this also moves the (+) icon close to the title)", "version": { "fr": "1.2.39", "to": "1.2.43" } }, "PrereleaseRadar": { "name": "enablePrereleaseRadar", "description": "Show a curated list of upcoming albums to a user", "native_description": "Show a curated list of upcoming albums to a user", "version": { "fr": "1.2.39", "to": "1.2.45" } }, "PremiumOnlyModal": { "name": "disablePremiumOnlyModal", "description": "Disable the Premium Only Modal", "native_description": "Disable the Premium Only Modal", "version": { "fr": "1.2.39", "to": "" } }, "CenteredLayout": { "name": "enableCenteredLayout", "description": "Enable centered in 'what's new'", "native_description": "Enable centered layout", "version": { "fr": "1.2.39", "to": "1.2.50" } }, "Lyrics": { "name": "enableLyrics", "description": "Feature flag that controls the availability of the lyrics feature", "native_description": "Feature flag that controls the availability of the lyrics feature", "version": { "fr": "1.2.41", "to": "" } }, "SeekWithArrowKeys": { "name": "enableSeekWithArrowKeys", "description": "Enable seeking 5s backward/forward with left/right arrow keys", "native_description": "Enable seeking 5s backward/forward with left/right arrow keys", "version": { "fr": "1.2.44", "to": "1.2.44" } }, "HomeCarousels": { "name": "enableHomeCarousels", "description": "Enable carousels on home (causes lags in the main menu 1.2.44-1.2.56)", "native_description": "Enable carousels on home", "version": { "fr": "1.2.44", "to": "1.2.62" } }, "canvasHome": { "name": "enableHybridHomeFeedBaseline", "description": "Enables the new Feed Baseline for Hybrid Home", "native_description": "Enables the new Feed Baseline for Hybrid Home", "version": { "fr": "1.2.42", "to": "1.2.62" } }, "canvasHomeAudioPreviews": { "name": "enableFeedBaselineAudioPreviews", "description": "Enable audio previews in the Big Cards", "native_description": "Enable audio previews in the Big Cards", "version": { "fr": "1.2.45", "to": "" } }, "HomePin": { "name": "enableHomePin", "description": "Enable pinning of home shelves", "native_description": "Enable pinning of home shelves", "version": { "fr": "1.2.45", "to": "" } }, "RecentSearchesDropdown": { "name": "enableRecentSearchesDropdown", "description": "Enables recent searches dropdown in GlobalNavBar", "native_description": "Enables recent searches dropdown in GlobalNavBar", "version": { "fr": "1.2.45", "to": "1.2.52" } }, "NearbyJams": { "name": "enableNearbyJams", "description": "Enable the support for Nearby Jams feature in the Device Picker", "native_description": "Enable the support for Nearby Jams feature in the Device Picker", "version": { "fr": "1.2.52", "to": "" } }, "TrackBans": { "name": "enableContextualTrackBans", "description": "Enable ability to ban/hide tracks from eligible contexts", "native_description": "Enable ability to ban/hide tracks from eligible contexts", "version": { "fr": "1.2.52", "to": "1.2.83" } }, "ArtistLikedSongs": { "name": "enableArtistLikedSongs", "description": "Enable Liked Songs section on Artist page", "native_description": "Enable Liked Songs section on Artist page", "version": { "fr": "1.1.67", "to": "1.2.17" } }, "ConcertsNotInterested": { "name": "enableConcertsNotInterested", "description": "Enable user to set not interested on concerts", "native_description": "Enable user to set not interested on concerts", "version": { "fr": "1.2.53", "to": "" } }, "GlobalCreateButton": { "name": "enableGlobalCreateButton", "description": "Enable plus button for creating different types of playlists from global nav bar", "native_description": "Enable plus button for creating different types of playlists from global nav bar", "version": { "fr": "1.2.53", "to": "1.2.56" } }, "ZoomSettingsUIDesktop": { "name": "enableZoomSettingsUIDesktop", "description": "Enable zoom settings from the settings page on Desktop", "native_description": "Enable zoom settings from the settings page on Desktop", "version": { "fr": "1.2.53", "to": "1.2.57" } }, "AmbientModeForMiniplayer": { "name": "enableAmbientModeForMiniplayer", "description": "Enable Ambient Mode for Miniplayer", "native_description": "Enable Ambient Mode for Miniplayer", "version": { "fr": "1.2.55", "to": "1.2.61" } }, "PeekNpv": { "name": "enablePeekNpv", "description": "Collapse right sidebar", "native_description": "Enable the Peek NPV feature", "version": { "fr": "1.2.53", "to": "" } }, "AlignedPanelHeaders": { "name": "enableAlignedPanelHeaders", "description": "Enable aligned panel headers", "native_description": "Enable aligned panel headers", "version": { "fr": "1.2.57", "to": "1.2.62" } }, "UserCommentsForEpisodes": { "name": "enableUserCommentsForEpisodes", "description": "Enable user comments for podcast episodes", "native_description": "Enable user comments for podcast episodes", "version": { "fr": "1.2.56", "to": "" } }, "AdsPrioritizeCanvas": { "name": "embeddedAdsPrioritizeCanvas", "description": "When enabled, tracks eligible for canvas will not be eligible for embedded ads", "native_description": "When enabled, tracks eligible for canvas will not be eligible for embedded ads", "version": { "fr": "1.2.58", "to": "" } }, "NewOverlayScrollbars": { "name": "enableNewOverlayScrollbars", "description": "Enable new overlay scrollbars", "native_description": "Enable new overlay scrollbars", "version": { "fr": "1.2.58", "to": "1.2.81" } }, "FullscreenLibrary": { "name": "enableFullscreenLibrary", "description": "Enable fullscreen library", "native_description": "Enable fullscreen library", "version": { "fr": "1.2.58", "to": "" } }, "ImprovedCinemaMode": { "name": "enableImprovedCinemaMode", "description": "Enable improved cinema mode", "native_description": "Enable improved cinema mode", "version": { "fr": "1.2.50", "to": "1.2.62" } }, "ImprovedCinemaModeCanvas": { "name": "enableImprovedCinemaModeCanvas", "description": "Enable canvas support inside improved cinema mode", "native_description": "Enable canvas support inside improved cinema mode", "version": { "fr": "1.2.56", "to": "" } }, "YlxReverseSorting": { "name": "enableYlxReverseSorting", "description": "Enable reverse sort direction in Your Library", "native_description": "Enable reverse sort direction in Your Library", "version": { "fr": "1.2.60", "to": "" } }, "BackendSearchHistory": { "name": "enableBackendSearchHistory", "description": "Enable backend search history", "native_description": "Enable backend search history", "version": { "fr": "1.2.60", "to": "" } }, "SyncingSearchHistoryToBackend": { "name": "enableSyncingSearchHistoryToBackend", "description": "Enables syncing search history to the backend", "native_description": "Enables syncing search history to the backend", "version": { "fr": "1.2.75", "to": "" } }, "WatchFeedEntityPages": { "name": "enableWatchFeedEntityPages", "description": "enabled track fragments in playlists, artists, albums", "native_description": "Enable Watch Feed feature on entity pages", "version": { "fr": "1.2.56", "to": "" } }, "WatchFeed": { "name": "enableWatchFeed", "description": "Enable Watch Feed feature", "native_description": "Enable Watch Feed feature", "version": { "fr": "1.2.56", "to": "" } }, "LyricsScrollToCurrentLineButton": { "name": "enableLyricsScrollToCurrentLineButton", "description": "Enable scroll to current line button in lyrics", "native_description": "Enable scroll to current line button in lyrics", "version": { "fr": "1.2.65", "to": "1.2.76" } }, "PiPMiniPlayerSettings": { "name": "enablePiPMiniPlayerSettings", "description": "Enable PiP settings", "native_description": "Enable PiP settings", "version": { "fr": "1.2.65", "to": "" } }, "SearchSuggestions": { "name": "enableSearchSuggestions", "description": "Enables the search suggestions dropdown", "native_description": "Enables the search suggestions dropdown", "version": { "fr": "1.2.66", "to": "" } }, "PiPMiniPlayerQueue": { "name": "enablePiPMiniPlayerQueue", "description": "Enables the PiP Mini Player Queue", "native_description": "Enables the PiP Mini Player Queue", "version": { "fr": "1.2.67", "to": "" } }, "SleepTimer": { "name": "enableSleepTimer", "description": "Enable Sleep timer", "native_description": "Enable Sleep timer", "version": { "fr": "1.2.69", "to": "" } }, "TracklistColumnsSorting": { "name": "enableTracklistColumnsSorting", "description": "Enable column reordering functionality in tracklists", "native_description": "Enable column reordering functionality in tracklists", "version": { "fr": "1.2.69", "to": "" } }, "ContextMenuShortcuts": { "name": "enableContextMenuShortcuts", "description": "Enables inline keyboard shortcuts for common context menu items", "native_description": "Enables inline keyboard shortcuts for common context menu items", "version": { "fr": "1.2.69", "to": "" } }, "CommentThreadsReactionsForEpisodes": { "name": "enableCommentThreadsReactionsForEpisodes", "description": "Enable users to react and reply to comments.", "native_description": "Enable users to react and reply to comments.", "version": { "fr": "1.2.74", "to": "1.2.81" } }, "ExcludeTrackFromTasteProfile": { "name": "enableExcludeTrackFromTasteProfile", "description": "Enables option to exclude track from taste profile via context menu", "native_description": "Enables option to exclude track from taste profile via context menu", "version": { "fr": "1.2.73", "to": "" } }, "LauncherAutoStartToggle": { "name": "enableLauncherAutoStartToggle", "description": "Enable launcher auto start toggle", "native_description": "Enable launcher auto start toggle", "version": { "fr": "1.2.71", "to": "" } }, "ProfileVisibilityControls": { "name": "enableProfileVisibilityControls", "description": "Enable profile visibility controls in the settings & profile page", "native_description": "Enable profile visibility controls in the settings & profile page", "version": { "fr": "1.2.74", "to": "" } }, "ShuffleSettings": { "name": "enableShuffleSettings", "description": "Enable shuffle settings section in advanced settings", "native_description": "Enable shuffle settings section in advanced settings", "version": { "fr": "1.2.74", "to": "" } }, "YlxMultiSelect": { "name": "enableYlxMultiSelect", "description": "Enable multi selection in Your Library", "native_description": "Enable multi selection in Your Library", "version": { "fr": "1.2.72", "to": "" } }, "MoreLikeThisPlaylist": { "name": "enableMoreLikeThisPlaylist", "description": "Enables More Like This playlist for playlists the user cannot edit", "native_description": "Enables More Like This playlist for playlists the user cannot edit", "version": { "fr": "1.2.32", "to": "" } }, "Tiltable3DArtwork": { "name": "enableTiltable3DArtwork", "description": "Enable tiltable 3D parallax effect on artwork (Cinema Mode and Cover Art Modal)", "native_description": "Enable tiltable 3D parallax effect on artwork (Cinema Mode and Cover Art Modal)", "version": { "fr": "1.2.76", "to": "" } }, "ExclusiveModeSetting": { "name": "enableExclusiveModeSetting", "native_description": "Display a setting to enable exclusive mode for an output device", "version": { "fr": "1.2.78", "to": "" } }, "ForceVolumeInExclusiveMode": { "name": "enableForceVolumeInExclusiveMode", "native_description": "Enables the force volume toggle when exclusive mode is enabled", "version": { "fr": "1.2.82", "to": "" } }, "ListPrivateByDefaultSetting": { "name": "enableListPrivateByDefaultSetting", "native_description": "Enable List Private By Default setting in Desktop Social Settings", "version": { "fr": "1.2.78", "to": "" } }, "Wrapped2025ListenCount": { "name": "enableWrapped2025ListenCount", "native_description": "Enable displaying listen counts for tracks in Wrapped 2025 Your Top Songs playlists", "version": { "fr": "1.2.80", "to": "" } }, "MagpiePrompting": { "name": "enableMagpiePrompting", "version": { "fr": "1.2.77", "to": "" } }, "MagpieScheduling": { "name": "enableMagpieScheduling", "version": { "fr": "1.2.77", "to": "" } }, "MagpieCuration": { "name": "enableMagpieCuration", "version": { "fr": "1.2.81", "to": "" } }, "Magpie": { "name": "enableMagpie", "version": { "fr": "1.2.77", "to": "" } }, "PlaylistExtenderBpmKey": { "name": "enablePlaylistExtenderBpmKey", "native_description": "Enable BPM and key columns in playlist recommendations", "version": { "fr": "1.2.82", "to": "" } } }, "CustomExp": { "SocialConnectOnDesktop": { "name": "enableSocialConnectOnDesktop", "description": "Enable the Social Connect API that powers group listening sessions for Desktop", "native_description": "Enable the Social Connect API that powers group listening sessions for Desktop", "value": "ENABLED", "version": { "fr": "1.2.21", "to": "1.2.50" } }, "SocialConnectOnDesktopWebPlayer": { "name": "enableSocialConnectOnDesktopWebPlayer", "description": "Enable the Social Connect API that powers group listening sessions for Desktop Web Player", "native_description": "Enable the Social Connect API that powers group listening sessions for Desktop Web Player", "value": "ENABLED", "version": { "fr": "1.2.21", "to": "1.2.50" } }, "MandalorianEaster": { "name": "enableMandalorianEasterEgg", "description": "Enable the Mandalorian easter-egg", "native_description": "Enable the Mandalorian easter-egg", "value": "HELMET_AND_SOUND", "version": { "fr": "1.2.10", "to": "" } }, "NavAlt": { "name": "enableNavAltExperiment", "description": "Enable the new home structure and navigation", "native_description": "Enable the new home structure and navigation", "value": "ENABLED_CENTER", "version": { "fr": "1.1.94", "to": "1.1.96" } }, "NavAlt2": { "name": "enableNavAltExperiment2", "description": "Enable the new home structure and navigation", "native_description": "Enable the new home structure and navigation", "value": "ENABLED_CENTER", "version": { "fr": "1.1.97", "to": "1.2.2" } }, "LyricsUpsell": { "name": "forceLyricsUpsell", "description": "Force Lyrics Upsell for testing", "native_description": "Force Lyrics Upsell for testing", "value": "CONTROL", "version": { "fr": "1.2.19", "to": "" } }, "InternalTrackSearch": { "name": "enableInternalTrackSearch", "description": "Enable internal track search on desktop UI", "native_description": "Enable internal track search on desktop UI", "value": "VARIANT", "version": { "fr": "1.2.19", "to": "1.2.47" } }, "TrackRecommender": { "name": "enableTrackInternalLinkRecommender", "description": "Enable internal link recommender component for track entity pages", "native_description": "Enable internal link recommender component for track entity pages", "value": "canonical-top-position", "version": { "fr": "1.2.19", "to": "1.2.33" } }, "GlobalNavBar": { "name": "enableGlobalNavBar", "description": "Show global nav bar with home button, search input and user avatar", "native_description": "Show global nav bar with home button, search input and user avatar", "value": "home-next-to-search", "version": { "fr": "1.2.30", "to": "1.2.45" } }, "VideosPremiumUpsell": { "name": "musicVideosPremiumUpsell", "description": "Testing Premium upsell for Free users on music videos", "native_description": "Testing Premium upsell for Free users on music videos", "value": "CONTROL", "version": { "fr": "1.2.31", "to": "" } }, "CanvasNpv": { "name": "enableCanvasNpv", "description": "Enables short, looping visuals on tracks", "native_description": "Enables short, looping visuals on tracks", "value": "canvas-play-loop", "version": { "fr": "1.2.33", "to": "1.2.62" } }, "shareButtonPositioning": { "name": "shareButtonPositioning", "description": "Share button positioning in NPV", "native_description": "Share button positioning in NPV", "value": "NPV_ALWAYS_VISIBLE", "version": { "fr": "1.2.39", "to": "1.2.50" } }, "CreateButton": { "name": "enableCreateButton", "description": "Enable create button either in the global navbar or in YLX", "native_description": "Enable create button either in the global navbar or in YLX", "value": "your_library", "version": { "fr": "1.2.57", "to": "1.2.81" } }, "AdsDismissTimeInterval": { "name": "embeddedAdsDismissTimeInterval", "description": "How long to wait (in seconds) before autodismissing embedded ad", "native_description": "How long to wait (in seconds) before autodismissing embedded ad", "value": "0", "version": { "fr": "1.2.59", "to": "" } }, "AdsRefreshTimeInterval": { "name": "embeddedAdsRefreshTimeInterval", "description": "How long to wait (in seconds) between each successful render of embedded ad", "native_description": "How long to wait (in seconds) between each successful render of embedded ad", "value": "99999", "version": { "fr": "1.2.59", "to": "" } }, "SpotifyOnboardingChecklist": { "name": "enableSpotifyOnboardingChecklist", "description": "Enable checklist-based onboarding", "native_description": "Enable checklist-based onboarding", "value": "control", "version": { "fr": "1.2.60", "to": "" } }, "LyricsVariationsInNPV": { "name": "enableLyricsVariationsInNPV", "description": "Enable Lyrics in NPV and reducing accidental view", "native_description": "Enable Lyrics in NPV and reducing accidental view", "value": "REDUCED_VISIBILITY", "version": { "fr": "1.2.66", "to": "" } }, "SkipNextTooltip": { "name": "enableSkipNextTooltip", "description": "Enable tooltip that shows a preview of the next item in queue", "native_description": "Enable tooltip that shows a preview of the next item in queue", "value": "expanded", "version": { "fr": "1.2.65", "to": "" } }, "OutroPreviewReporting": { "name": "enableOutroPreviewReporting", "description": "Enable outro reporter for audio previews", "native_description": "Enable outro reporter for audio previews", "value": "DISABLED", "version": { "fr": "1.2.70", "to": "" } }, "LibraryImportSettings": { "name": "enableLibraryImportSettings", "description": "Enable library import on settings page", "native_description": "Enable library import on settings page", "value": "enabled", "version": { "fr": "1.2.73", "to": "" } }, "SearchResultsAsList": { "name": "enableSearchResultsAsList", "description": "Enables the search results as a list of top results", "native_description": "Enables the search results as a list of top results", "value": "LIST_WITH_TOP_RESULT", "version": { "fr": "1.2.73", "to": "" } } }, "binary": { "block_update": { "version": { "fr": "1.1.59", "to": "" }, "add": "(?<=desktop-update\/.)7(\/update)", "match": "(?<=desktop-update\/.)2(\/update)", "replace": "7/update" }, "block_slots": { "version": { "fr": "1.1.59", "to": "" }, "add": "slot}(?=.{3,8}(override_url|queued_ads))", "match": "slots(?=.{3,8}(override_url|queued_ads))", "replace": "slot}" }, "block_slots_2": { "version": { "fr": "1.2.55", "to": "" }, "add": "slot}(?=.{25,35}state)", "match": "slots(?=.{25,35}state)", "replace": "slot}" }, "block_slots_3": { "version": { "fr": "1.2.55", "to": "" }, "add": "}(?=payload=)", "match": "\\?(?=payload=)", "replace": "}" }, "block_gabo": { "version": { "fr": "1.1.59", "to": "1.2.73" }, "add": "dodo(?=.receiver-service)", "match": "gabo(?=.receiver-service)", "replace": "dodo" }, "block_gabo2": { "version": { "fr": "1.2.74", "to": "" }, "add": "dodo(?=.receiver-service(?:/public|[^/]))", "match": "gabo(?=.receiver-service(?:/public|[^/]))", "replace": "dodo" } }, "themelyrics": { "theme": { "default": { "pasttext": "#575757", "current": "#C8C8C8", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "red": { "pasttext": "#575757", "current": "#FF3737", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "orange": { "pasttext": "#575757", "current": "#F68D00", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "yellow": { "pasttext": "#575757", "current": "#ECE224", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "spotify": { "pasttext": "#575757", "current": "#1ED760", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "spotify#2": { "pasttext": "#9b9b9b", "current": "#1ed760", "next": "#666666", "hover": "#f2f2f2", "background": "#242424", "maxmatch": "#C2C2C2" }, "blue": { "pasttext": "#575757", "current": "#00DFEA", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "purple": { "pasttext": "#575757", "current": "#9E6BE3", "next": "#575757", "hover": "#C8C8C8", "background": "#121212", "maxmatch": "#969696" }, "strawberry": { "pasttext": "#F17F7F", "current": "#E43A47", "next": "#595959", "hover": "#F2F2F2", "background": "#1C1C1E", "maxmatch": "#595959" }, "pumpkin": { "pasttext": "#FDAC69", "current": "#E88500", "next": "#595959", "hover": "#F2F2F2", "background": "#1C1C1E", "maxmatch": "#595959" }, "sandbar": { "pasttext": "#FFDB7A", "current": "#F5BA18", "next": "#595959", "hover": "#F2F2F2", "background": "#1C1C1E", "maxmatch": "#595959" }, "radium": { "pasttext": "#AAFFA3", "current": "#17D344", "next": "#595959", "hover": "#F2F2F2", "background": "#1C1C1E", "maxmatch": "#595959" }, "oceano": { "pasttext": "#70DBF0", "current": "#13A1BD", "next": "#595959", "hover": "#F2F2F2", "background": "#1C1C1E", "maxmatch": "#595959" }, "royal": { "pasttext": "#B8A3EB", "current": "#8461DD", "next": "#595959", "hover": "#F2F2F2", "background": "#1C1C1E", "maxmatch": "#595959" }, "github": { "pasttext": "#AD82F8", "current": "#7EE787", "next": "#47566D", "hover": "#70B3FF", "background": "#161B22", "maxmatch": "#408BD0" }, "discord": { "pasttext": "#616774", "current": "#7A8FDC", "next": "#616774", "hover": "#FFFFFF", "background": "#23272A", "maxmatch": "#616774" }, "drot": { "pasttext": "#505050", "current": "#F37171", "next": "#505050", "hover": "#A13131", "background": "#191414", "maxmatch": "#787878" }, "forest": { "pasttext": "#505050", "current": "#AEF97B", "next": "#505050", "hover": "#418022", "background": "#141914", "maxmatch": "#787878" }, "fresh": { "pasttext": "#505050", "current": "#50DCF0", "next": "#505050", "hover": "#0B7383", "background": "#14191E", "maxmatch": "#787878" }, "zing": { "pasttext": "#4E596F", "current": "#F67064", "next": "#4E596F", "hover": "#FFFFFF", "background": "#202430", "maxmatch": "#9EA8BC" }, "pinkle": { "pasttext": "#9579E3", "current": "#CD3B99", "next": "#5E547C", "hover": "#FFFFFF", "background": "#1C1925", "maxmatch": "#5E547C" }, "krux": { "pasttext": "#5C89D2", "current": "#01C38D", "next": "#696E79", "hover": "#FFFFFF", "background": "#191E29", "maxmatch": "#696E79" }, "blueberry": { "pasttext": "#1CAAC6", "current": "#90E0F0", "next": "#516377", "hover": "#A0D1FA", "background": "#232937", "maxmatch": "#516377" }, "postlight": { "pasttext": "#C9A8FE", "current": "#9D65C7", "next": "#534D6F", "hover": "#D1D1D1", "background": "#13101C", "maxmatch": "#534D6F" }, "relish": { "pasttext": "#9D2117", "current": "#C8C8C8", "next": "#C8A032", "hover": "#E5CB8B", "background": "#121212", "maxmatch": "#787878" }, "turquoise": { "pasttext": "#00656aa0", "current": "#01dfea", "next": "#575757", "hover": "#a97aff", "background": "#121212", "maxmatch": "#00656a" }, "lavender": { "pasttext": "#B8A2EA", "current": "#8462DD", "next": "#575757", "hover": "#F2F2F2", "background": "#121212", "maxmatch": "#C2C2C2" } } }, "collaboration": { "version": { "fr": "1.1.74", "to": "1.2.81" }, "match": "[\\w$]+\\??\\.canAdministratePermissions(\\W.*?\"permissions\\.invite-collaborators\")", "replace": "false$1" }, "byspotx": { "add": "// Patched by SpotX" }, "disablesentry": { "version": { "fr": "1.1.70", "to": "" }, "match": [ "@\\w+\/gabo[^\"]+" ], "replace": [ "" ] }, "cssmin": { "version": { "fr": "1.1.70", "to": "" }, "match": [ "[/]\\*([^*]|[\r\n]|(\\*([^/]|[\r\n])))*\\*[/]", "[/][/]#\\s.*", "\r?\n(?!\\(1|\\d)" ], "replace": [ "", "", "" ] }, "htmlmin": { "version": { "fr": "1.1.70", "to": "" }, "match": [ "
  • zlib<\/a><\/li>\n(.|\n)*<\/p>(<\/div>)", "\t", " ", "(?m)(^\\s*\r?\n)", "\r?\n(?!\\(1|\\d)" ], "replace": [ "$2", "", "", "", "" ] }, "blankmin": { "version": { "fr": "1.1.70", "to": "" }, "match": [ " ", "(?m)(^\\s*\r?\n)", "\r?\n(?!\\(1|\\d)" ], "replace": [ "", "", "" ] }, "minjs": { "version": { "fr": "1.1.70", "to": "" }, "match": [ "[/][/][#] sourceMappingURL=.*[.]map", "\r?\n(?!\\(1|\\d)" ], "replace": [ "", "" ] }, "minjson": { "version": { "fr": "1.1.70", "to": "" }, "match": [ " ", " ", "\": ", "\r?\n(?!\\(1|\\d)" ], "replace": [ "", "", "\":", "" ] }, "block_subfeeds": { "version": { "fr": "1.2.36", "to": "" }, "add": " .LVMjmN2CaPruPAo62RAY, .Wn4fEKCJ6jYPfG2Y0ABn, .Y3Kd8WKnE3ZW9lzgA8nN, .C_T1Uoz7Fws_AFDl { display: none !important }" }, "downloadquality": { "version": { "fr": "1.2.30", "to": "" }, "add": " :is(.weV_qxFz4gF5sPotO10y, .BMtRRwqaJD_95vJFMFD0, .eguwzH_QWTBXry7hiNj3, .qV_CxbowaNkMarye, .qV_CxbowaNkMarye):has([for=\"desktop.settings.downloadQuality\"]) {display: none}" }, "downloadicon": { "version": { "fr": "1.1.74", "to": "" }, "add": " .BKsbV2Xl786X9a09XROH, .GWCBhKJqeZal3n5tCQwl, .pX3IkLhEry0wVfiU {display:none}" }, "submenudownload": { "version": { "fr": "1.1.74", "to": "" }, "add": " .pzkhLqffqF_4hucrVVQA, .egE6UQjF_UUoCzvMxREj, .Y98_oiegQgSpY_o7hoKG, .tT_JypfxNakuY1jHgyBN, .zVA1h9TUy8QQBogj {display:none}" }, "veryhighstream": { "version": { "fr": "1.1.74", "to": "1.2.29" }, "add": " #desktop\\.settings\\.streamingQuality>option:nth-child(5) {display:none}" }, "fix-scrollbar": { "version": { "fr": "1.1.59", "to": "" }, "add": " .main-view-container__scroll-node > .os-scrollbar-vertical {top:0px !important}" }, "fix-old-theme": { "version": { "fr": "1.1.74", "to": "1.2.13" }, "match": "((LKgm9fCDTO7wqig_5U1q|eFQFeXVbY8rrcTagg0oH){--left-sidebar-item-height.+?margin-top)(:\\d+px)", "replace": "$1:0px" }, "searchFixes": { "version": { "fr": "1.1.85", "to": "1.2.57" }, "match": "(typeName\\])", "replace": "$1 || []" } }, "VariousJs": { "product_state": { "version": { "fr": "1.1.93", "to": "" }, "match": "(this\\.(?:productStateApi|_product_state)(?:|_service)=(.))(?=}|(?:,this\\.cache=.)?,this\\.productStateApi|,this\\._events)", "replace": "$1,$2?.putOverridesValues?.({0})", "add": "ads:'0',catalogue:'premium',product:'premium',type:'premium',name:'Spotify','unrestricted':'1'", "add2": "'storage-size-config':'{0},90,500,3'" }, "dev-tools": { "version": { "fr": "1.1.98", "to": "1.2.37" }, "match": [ "(onSelected:).{3,14}({..\"app.enable.developer.mode.,).\\)(,.\\(!0\\))?}", "{(.{5,20}?|)(this\\.(?:productStateApi|_product_state)(?:|_service)=(.))(?=}|,this\\.productStateApi|,this\\._events|,.\\?\\.putOverridesValues)", "(..\\(.\\)|.)(\\?(..createElement|\\(.{1,7}jsxs\\))\\(.{1,3},{filterMatchQuery:.{2,15}\\(\"settings.employee\"\\))" ], "replace": [ "$1(check)=>$2check);localStorage.setItem('dev-tools',check);Spotx.ProductState.putOverridesValues({pairs:{'com.spotify.madprops.use.ucs.product.state':check?'1':'0','employee':check?'1':'0'}});setTimeout(()=>{window.sendCosmosRequest({ request:JSON.stringify({method:'POST',uri:'sp://desktop/v1/restart'})})},700);}", "{window.Spotx={ProductState:$3};if(localStorage.getItem('dev-tools')==='true'){$3.putOverridesValues({pairs:{'com.spotify.madprops.use.ucs.product.state':'1','employee':'1'}})};$1$2", "true$2" ] }, "banner_home": { "version": { "fr": "1.1.70", "to": "" }, "match": "(ADS_PREMIUM,isPremium:).(.{20,60}?isHptoHidden:).", "replace": "$1true$2true" }, "sentry": { "version": { "fr": "1.1.70", "to": "" }, "match": "(864e5)<30", "replace": "$1<0" }, "disablelog": { "version": { "fr": "1.1.70", "to": "" }, "match": "sp://logging/v3/\\w+", "replace": "" }, "hidemerchsidebar": { "version": { "fr": "1.2.0", "to": "1.2.44" }, "match": "((return|merch:.}\\)\\=\\>)( .&&\\(\\!.\\|\\|..length\\<1\\)&&..length\\>0&&\\(.=.\\),)?\\!.\\|\\|..length\\<1)", "replace": "$2 true " }, "offrujs": { "version": { "fr": "1.1.92", "to": "" }, "match": "\\[(.\\..{1,2}\\.)en,.\\..{1,2}\\.a(.+?\\])", "replace": "[$1en,$1ru]" }, "goofyhistory": { "version": { "fr": "1.1.70", "to": "" }, "match": "({data:.}\\)=>{|.\\.data;)((?:this|.)\\._state=(.))", "replace": "$1goofyHistory($3, {0}, {1});$2" }, "similarplaylist": { "version": { "fr": "1.1.85", "to": "1.2.24" }, "match": "(Fragment,(?:null,|{children:\\[))(?:[^}]{1,100}?)contextmenu\\.create-similar-playlist\"\\)}\\),", "replace": "$1" }, "sidebar_fix": { "version": { "fr": "1.1.99", "to": "1.2.6" }, "match": "min:320,max:420", "replace": "min:250,max:400" }, "filtertags_locale_fix": { "version": { "fr": "1.2.29", "to": "" }, "match": "(withPath\\(\"\/liked-songs\"\\))", "replace": "$1.withLocale(\"en\")" }, "lyrics-old-on": { "version": { "fr": "1.1.70", "to": "1.2.35" }, "match": "((?:\\(?await )?.\\.build.{20,60}encodeURIComponent.{20,140}\"\/track\/{trackId}.+?)(.send)", "replace": "$1.withHeaders([{key:'spotify-app-version',value:'1.2.43'}])$2" }, "lyrics-block": { "version": { "fr": "1.1.70", "to": "1.2.40" }, "match": "(?:\\(?await )?.\\.build.{20,60}encodeURIComponent.{20,140}\"\\/track\\/{trackId}.+?send\\(\\)(?:\\)\\.body)?", "replace": "{}" }, "fixTitlebarHeight": { "version": { "fr": "1.2.45", "to": "1.2.45" }, "match": "(\"update_titlebar\",height:).", "replace": "$1'40'" }, "mock": { "version": { "fr": "1.2.55", "to": "" }, "match": "\/(?:leavebehinds\/mock|leavebehinds|mock|sponsoredplaylist)\/", "replace": "/localhost/" }, "upgradeButton": { "version": { "fr": "1.2.55", "to": "" }, "match": "\"\\(min-width: 1024px\\)\"", "replace": "\"(min-width: 9999990px)\"" }, "upgradeMenu": { "version": { "fr": "1.2.55", "to": "" }, "match": "=>\\{((?:(?!=>\\{).)*?(?=\"loggedin_upgrade_menu))", "replace": "=>{ return null; $1" }, "hideEmptyYourEpisodes": { "version": { "fr": "1.2.78", "to": "" }, "match": "(includeYourEpisodes\\?\\{inclusionMode:[^,}]+\\.)INCLUDE_ALWAYS", "replace": "$1INCLUDE_IF_NOT_EMPTY" }, "GenreHubHashFix": { "version": { "fr": "1.1.85", "to": "1.1.99" }, "match": "((sha256Hash: *await|return [^,]+,) *)([^ (]+[.]k[.]hash)[(]([^.()]+)[.]body[)]", "replace": "$1(\"browsePage\"===$4.name?\"d8346883162a16a62a5b69e73e70c66a68c27b14265091cd9e1517f48334bbb3\":$3($4.body))" } } } ================================================ FILE: run.ps1 ================================================ [CmdletBinding()] param ( [Parameter(HelpMessage = 'Latest recommended Spotify version for Windows 10+.')] [string]$latest_full = "1.2.85.519.g549a528b", [Parameter(HelpMessage = 'Latest supported Spotify version for Windows 7-8.1')] [string]$last_win7_full = "1.2.5.1006.g22820f93", [Parameter(HelpMessage = 'Latest supported Spotify version for x86')] [string]$last_x86_full = "1.2.53.440.g7b2f582a", [Parameter(HelpMessage = 'Force a specific download method. Default is automatic selection.')] [Alias('dm')] [ValidateSet('curl', 'webclient')] [string]$download_method, [Parameter(HelpMessage = "Change recommended Spotify version. Example: 1.2.85.519.g549a528b.")] [Alias("v")] [string]$version, [Parameter(HelpMessage = 'Custom path to Spotify installation directory. Default is %APPDATA%\Spotify.')] [string]$SpotifyPath, [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' # Использовать кастомный путь если указан параметр -SpotifyPath if ($SpotifyPath) { $spotifyDirectory = $SpotifyPath } $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" function Get-SystemArchitecture { $archNames = @($env:PROCESSOR_ARCHITEW6432, $env:PROCESSOR_ARCHITECTURE) | Where-Object { $_ } foreach ($archName in $archNames) { switch ($archName.ToUpperInvariant()) { 'ARM64' { return 'arm64' } 'AMD64' { return 'x64' } 'X86' { return 'x86' } } } return 'x64' } function Get-SpotifyVersionNumber { param( [Parameter(Mandatory = $true)] [string]$SpotifyVersion ) return [Version]($SpotifyVersion -replace '\.g[0-9a-f]{8}$', '') } function Get-SpotifyInstallerArchitecture { param( [Parameter(Mandatory = $true)] [string]$SystemArchitecture, [Parameter(Mandatory = $true)] [version]$SpotifyVersion, [Parameter(Mandatory = $true)] [version]$LastX86SupportedVersion ) switch ($SystemArchitecture) { 'arm64' { return 'arm64' } 'x64' { if ($SpotifyVersion -le $LastX86SupportedVersion) { return 'x86' } return 'x64' } 'x86' { if ($SpotifyVersion -le $LastX86SupportedVersion) { return 'x86' } throw "Version $SpotifyVersion is not supported on x86 systems" } default { return 'x64' } } } $spotifyDownloadBaseUrl = "https://broad-pine-bbc0.amd64fox1.workers.dev/download" $systemArchitecture = Get-SystemArchitecture $match_v = "^(?\d+\.\d+\.\d+\.\d+\.g[0-9a-f]{8})(?:-\d+)?$" $versionIsSupported = $false if ($version) { if ($version -match $match_v) { $onlineFull = $Matches.version $versionIsSupported = $true } else { Write-Warning "Invalid $($version) format. Example: 1.2.13.661.ga588f749 (legacy -4064 suffix is optional)" Write-Host } } $old_os = $win7 -or $win8 -or $win8_1 $last_win7 = Get-SpotifyVersionNumber -SpotifyVersion $last_win7_full $last_x86 = Get-SpotifyVersionNumber -SpotifyVersion $last_x86_full if (-not $versionIsSupported) { if ($old_os) { $onlineFull = $last_win7_full } elseif ($systemArchitecture -eq 'x86') { $onlineFull = $last_x86_full } else { # latest tested version for Win 10-12 $onlineFull = $latest_full } } else { $requestedOnlineVersion = Get-SpotifyVersionNumber -SpotifyVersion $onlineFull if ($old_os) { if ($requestedOnlineVersion -gt $last_win7) { Write-Warning ("Version {0} is only supported on Windows 10 and above" -f $requestedOnlineVersion) 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 $requestedOnlineVersion = $last_win7 } } if ($systemArchitecture -eq 'x86' -and $requestedOnlineVersion -gt $last_x86) { Write-Warning ("Version {0} is not supported on 32-bit (x86) Windows systems" -f $requestedOnlineVersion) Write-Warning ("The recommended version has been automatically changed to {0}, the latest supported version for x86 systems" -f $last_x86) Write-Host $onlineFull = $last_x86_full $requestedOnlineVersion = $last_x86 } } $online = (Get-SpotifyVersionNumber -SpotifyVersion $onlineFull).ToString() 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 Test-CurlAvailability { try { if (curl.exe -V) { return $true } } catch { } return $false } function Resolve-SpotifyDownloadMethod { param( [string]$ForcedMethod ) if ($ForcedMethod) { switch ($ForcedMethod) { 'curl' { if (Test-CurlAvailability) { return 'curl' } throw "Forced download method 'curl' is not available on this system" } 'webclient' { return 'webclient' } } } if (Test-CurlAvailability) { return 'curl' } return 'webclient' } function Format-DownloadSizeMb { param( [long]$Bytes ) return ('{0:N2} MB' -f ($Bytes / 1MB)) } function Invoke-WebClientDownloadWithProgress { param( [Parameter(Mandatory = $true)] [System.Net.WebClient]$WebClient, [Parameter(Mandatory = $true)] [string]$Url, [Parameter(Mandatory = $true)] [string]$DestinationPath ) $fileName = Split-Path -Path $DestinationPath -Leaf $previousProgressPreference = $ProgressPreference $responseStream = $null $fileStream = $null $stopwatch = $null try { $ProgressPreference = 'Continue' $responseStream = $WebClient.OpenRead($Url) if ($null -eq $responseStream) { throw "Failed to open response stream for $Url" } $totalBytes = 0L $contentLength = $WebClient.ResponseHeaders['Content-Length'] if ($contentLength) { $null = [long]::TryParse($contentLength, [ref]$totalBytes) } $fileStream = [System.IO.File]::Open($DestinationPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::None) $buffer = New-Object byte[] 262144 $bytesReceived = 0L $progressUpdateIntervalMs = 200 $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $lastProgressUpdateMs = - $progressUpdateIntervalMs while (($bytesRead = $responseStream.Read($buffer, 0, $buffer.Length)) -gt 0) { $fileStream.Write($buffer, 0, $bytesRead) $bytesReceived += $bytesRead if (($stopwatch.ElapsedMilliseconds - $lastProgressUpdateMs) -ge $progressUpdateIntervalMs) { if ($totalBytes -gt 0) { $percentComplete = [Math]::Min([int][Math]::Floor(($bytesReceived / $totalBytes) * 100), 100) $status = "{0} / {1} ({2}%)" -f (Format-DownloadSizeMb -Bytes $bytesReceived), (Format-DownloadSizeMb -Bytes $totalBytes), $percentComplete Write-Progress -Activity "Downloading $fileName" -Status $status -PercentComplete $percentComplete } else { $status = "{0} downloaded" -f (Format-DownloadSizeMb -Bytes $bytesReceived) Write-Progress -Activity "Downloading $fileName" -Status $status -PercentComplete 0 } $lastProgressUpdateMs = $stopwatch.ElapsedMilliseconds } } if ($totalBytes -gt 0) { $completedStatus = "{0} / {1} (100%)" -f (Format-DownloadSizeMb -Bytes $bytesReceived), (Format-DownloadSizeMb -Bytes $totalBytes) Write-Progress -Activity "Downloading $fileName" -Status $completedStatus -PercentComplete 100 } Write-Progress -Activity "Downloading $fileName" -Completed } finally { if ($null -ne $stopwatch) { $stopwatch.Stop() } if ($null -ne $fileStream) { $fileStream.Dispose() } if ($null -ne $responseStream) { $responseStream.Dispose() } Write-Progress -Activity "Downloading $fileName" -Completed $ProgressPreference = $previousProgressPreference } } function Invoke-SpotifyDownloadAttempt { param( [Parameter(Mandatory = $true)] [string]$Url, [Parameter(Mandatory = $true)] [string]$DestinationPath, [Parameter(Mandatory = $true)] [System.Net.WebClient]$WebClient, [Parameter(Mandatory = $true)] [ValidateSet('curl', 'webclient')] [string]$DownloadMethod ) switch ($DownloadMethod) { 'curl' { $stcode = curl.exe -Is -w "%{http_code}" -o NUL -k $Url --retry 2 --ssl-no-revoke if ($stcode.trim() -ne "200") { throw "Unexpected HTTP status: $($stcode.Trim())" } curl.exe -q -k $Url -o $DestinationPath --progress-bar --retry 3 --ssl-no-revoke return } 'webclient' { Invoke-WebClientDownloadWithProgress -WebClient $WebClient -Url $Url -DestinationPath $DestinationPath return } } } function downloadSp([string]$DownloadFolder) { $webClient = New-Object -TypeName System.Net.WebClient $spotifyVersion = Get-SpotifyVersionNumber -SpotifyVersion $onlineFull $arch = Get-SpotifyInstallerArchitecture ` -SystemArchitecture $systemArchitecture ` -SpotifyVersion $spotifyVersion ` -LastX86SupportedVersion $last_x86 $web_Url = "$spotifyDownloadBaseUrl/spotify_installer-$onlineFull-$arch.exe" $local_Url = Join-Path $DownloadFolder 'SpotifySetup.exe' $web_name_file = "SpotifySetup.exe" try { $selectedDownloadMethod = Resolve-SpotifyDownloadMethod -ForcedMethod $download_method } catch { Write-Warning $_.Exception.Message Stop-Script } for ($attempt = 1; $attempt -le 2; $attempt++) { try { Invoke-SpotifyDownloadAttempt ` -Url $web_Url ` -DestinationPath $local_Url ` -WebClient $webClient ` -DownloadMethod $selectedDownloadMethod return } catch { Write-Host if ($attempt -eq 1) { Write-Host ($lang).Download $web_name_file -ForegroundColor RED $_.Exception Write-Host Write-Host ($lang).Download2`n Start-Sleep -Milliseconds 5000 continue } Write-Host ($lang).Download3 -ForegroundColor RED $_.Exception Write-Host Write-Host ($lang).Download4`n if ($DownloadFolder -and (Test-Path $DownloadFolder)) { Start-Sleep -Milliseconds 200 Remove-Item -Recurse -LiteralPath $DownloadFolder -ErrorAction SilentlyContinue } Stop-Script } } } function Remove-TempDirectory { param( [string]$Directory, [int]$DelayMs = 200 ) if ($Directory -and (Test-Path $Directory)) { Start-Sleep -Milliseconds $DelayMs Remove-Item -Recurse -LiteralPath $Directory -ErrorAction SilentlyContinue -Force } } 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') { $previousProgressPreference = $ProgressPreference try { $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 } finally { $ProgressPreference = $previousProgressPreference } } if ($ch -eq 'n') { Stop-Script } } } if ($premium) { Write-Host ($lang).Prem`n } $spotifyInstalled = (Test-Path -LiteralPath $spotifyExecutable) if ($SpotifyPath -and -not $spotifyInstalled) { Write-Warning "Spotify not found in custom path: $spotifyDirectory" Stop-Script } 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 (skip if custom path is used) if ($oldversion -and -not $SpotifyPath) { 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 (skip if custom path is used) if ($testversion -and -not $SpotifyPath) { if ($confirm_spoti_recomended_over -or $confirm_spoti_recomended_uninstall) { Write-Host ($lang).NewV`n } if (!($confirm_spoti_recomended_over) -and !($confirm_spoti_recomended_uninstall)) { do { Write-Host (($lang).NewV2 -f $offline, $online) $ch = Read-Host -Prompt (($lang).NewV3 -f $offline) 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 = 'n' } if ($ch -eq 'y') { $upgrade_client = $false } if ($ch -eq 'n') { if (!($confirm_spoti_recomended_over) -and !($confirm_spoti_recomended_uninstall)) { do { $ch = Read-Host -Prompt (($lang).Recom -f $online) 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 $downgrading = $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).DelNew`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') { Remove-TempDirectory -Directory $tempDirectory Stop-Script } } } } # If there is no client or it is outdated, then install (skip if custom path is used) if (-not $SpotifyPath -and (-not $spotifyInstalled -or $upgrade_client)) { Write-Host ($lang).DownSpoti"" -NoNewline Write-Host $online -ForegroundColor Green Write-Host ($lang).DownSpoti2`n # Delete old version files of Spotify before installing, leave only profile files $ErrorActionPreference = 'SilentlyContinue' Kill-Spotify Start-Sleep -Milliseconds 600 $null = Unlock-Folder Start-Sleep -Milliseconds 200 Get-ChildItem $spotifyDirectory -Exclude 'Users', 'prefs' | Remove-Item -Recurse -Force Start-Sleep -Milliseconds 200 $tempDirName = "SpotX_Temp-$(Get-Date -UFormat '%Y-%m-%d_%H-%M-%S')" $tempDirectory = Join-Path ([System.IO.Path]::GetTempPath()) $tempDirName if (-not (Test-Path -LiteralPath $tempDirectory)) { New-Item -ItemType Directory -Path $tempDirectory | Out-Null } # Client download downloadSp -DownloadFolder $tempDirectory Write-Host Start-Sleep -Milliseconds 200 # Client installation $setupExe = Join-Path $tempDirectory 'SpotifySetup.exe' Start-Process -FilePath explorer.exe -ArgumentList $setupExe while (-not (get-process | Where-Object { $_.ProcessName -eq 'SpotifySetup' })) {} wait-process -name SpotifySetup Kill-Spotify # Upgrade check version Spotify offline $offline = (Get-Item $spotifyExecutable).VersionInfo.FileVersion # Upgrade check version Spotify.bak $offline_bak = (Get-Item $exe_bak).VersionInfo.FileVersion } # Delete Spotify shortcut if it is on desktop if ($no_shortcut) { $ErrorActionPreference = 'SilentlyContinue' $desktop_folder = DesktopFolder Start-Sleep -Milliseconds 1000 remove-item "$desktop_folder\Spotify.lnk" -Recurse -Force } $ch = $null # updated Russian translation if ($langCode -eq 'ru' -and [version]$offline -ge [version]"1.1.92.644") { $webjsonru = Get -Url (Get-Link -e "/patches/Augmented%20translation/ru.json") if ($webjsonru -ne $null) { $ru = $true } } if ($podcasts_off) { Write-Host ($lang).PodcatsOff`n $ch = 'y' } if ($podcasts_on) { Write-Host ($lang).PodcastsOn`n $ch = 'n' } if (!($podcasts_off) -and !($podcasts_on)) { do { $ch = Read-Host -Prompt ($lang).PodcatsSelect Write-Host if (!($ch -eq 'n' -or $ch -eq 'y')) { incorrectValue } } while ($ch -notmatch '^y$|^n$') } if ($ch -eq 'y') { $podcast_off = $true } $ch = $null if ($downgrading) { $upd = "`n" + [string]($lang).DowngradeNote } else { $upd = "" } if ($block_update_on) { Write-Host ($lang).UpdBlock`n $ch = 'y' } if ($block_update_off) { Write-Host ($lang).UpdUnblock`n $ch = 'n' } if (!($block_update_on) -and !($block_update_off)) { do { $text_upd = [string]($lang).UpdSelect + $upd $ch = Read-Host -Prompt $text_upd Write-Host if (!($ch -eq 'n' -or $ch -eq 'y')) { incorrectValue } } while ($ch -notmatch '^y$|^n$') } if ($ch -eq 'y') { $not_block_update = $false } if (!($new_theme) -and [version]$offline -ge [version]"1.2.14.1141") { Write-Warning "This version does not support the old theme, use version 1.2.13.661 or below" Write-Host } if ($ch -eq 'n') { $not_block_update = $true $ErrorActionPreference = 'SilentlyContinue' if ((Test-Path -LiteralPath $exe_bak) -and $offline -eq $offline_bak) { Remove-Item $spotifyExecutable -Recurse -Force Rename-Item $exe_bak $spotifyExecutable } } $ch = $null $webjson = Get -Url (Get-Link -e "/patches/patches.json") -RetrySeconds 5 if ($webjson -eq $null) { Write-Host Write-Host "Failed to get patches.json" -ForegroundColor Red Remove-TempDirectory -Directory $tempDirectory Stop-Script } function Helper($paramname) { function Remove-Json { param ( [Parameter(Mandatory = $true)] [Alias("j")] [PSObject]$Json, [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [Alias("p")] [string[]]$Properties ) foreach ($Property in $Properties) { $Json.psobject.properties.Remove($Property) } } function Move-Json { param ( [Parameter(Mandatory = $true)] [Alias("t")] [PSObject]$to, [Parameter(Mandatory = $true)] [Alias("n")] [string[]]$name, [Parameter(Mandatory = $true)] [Alias("f")] [PSObject]$from ) foreach ($propertyName in $name) { $from | Add-Member -MemberType NoteProperty -Name $propertyName -Value $to.$propertyName Remove-Json -j $to -p $propertyName } } switch ( $paramname ) { "HtmlLicMin" { # licenses.html minification $name = "patches.json.others." $n = "licenses.html" $contents = "htmlmin" $json = $webjson.others } "HtmlBlank" { # htmlBlank minification $name = "patches.json.others." $n = "blank.html" $contents = "blank.html" $json = $webjson.others } "MinJs" { # Minification of all *.js $contents = "minjs" $json = $webjson.others } "MinJson" { # Minification of all *.json $contents = "minjson" $json = $webjson.others } "FixCss" { # Remove indent for old theme xpui.css $name = "patches.json.others." $n = "xpui.css" $json = $webjson.others } "Fixjs" { $n = $name $contents = "searchFixes" $name = "patches.json.others." $json = $webjson.others } "Cssmin" { # Minification of all *.css $contents = "cssmin" $json = $webjson.others } "DisableSentry" { $name = "patches.json.others." $n = $fileName $contents = "disablesentry" $json = $webjson.others } "Discriptions" { # Add discriptions (xpui-desktop-modals.js) $svg_tg = $webjson.others.discriptions.svgtg $svg_git = $webjson.others.discriptions.svggit $svg_faq = $webjson.others.discriptions.svgfaq $replace = $webjson.others.discriptions.replace $replacedText = $replace -f $svg_git, $svg_tg, $svg_faq $webjson.others.discriptions.replace = '$1"' + $replacedText + '"})' $name = "patches.json.others." $n = "xpui-desktop-modals.js" $contents = "discriptions" $json = $webjson.others } "OffadsonFullscreen" { # Full screen mode activation and removing "Upgrade to premium" menu, upgrade button, disabling a playlist sponsor $name = "patches.json.free." $n = "xpui.js" $contents = $webjson.free.psobject.properties.name $json = $webjson.free } "ForcedExp" { # Forced disable some exp (xpui.js) $offline_patch = $offline -replace '(\d+\.\d+\.\d+)(.\d+)', '$1' $Enable = $webjson.others.EnableExp $Disable = $webjson.others.DisableExp $Custom = $webjson.others.CustomExp # causes lags in the main menu 1.2.44-1.2.56 if ([version]$offline -le [version]'1.2.56.502') { Move-Json -n 'HomeCarousels' -t $Enable -f $Disable } # disable search suggestions Move-Json -n 'SearchSuggestions' -t $Enable -f $Disable # disable new scrollbar Move-Json -n 'NewOverlayScrollbars' -t $Enable -f $Disable # temporarily disable collapsing right sidebar Move-Json -n 'PeekNpv' -t $Enable -f $Disable if ($podcast_off) { Move-Json -n 'HomePin' -t $Enable -f $Disable } # disabled broken panel from 1.2.37 to 1.2.38 if ([version]$offline -eq [version]'1.2.37.701' -or [version]$offline -eq [version]'1.2.38.720' ) { Move-Json -n 'DevicePickerSidePanel' -t $Enable -f $Disable } if ([version]$offline -ge [version]'1.2.41.434' -and $lyrics_block) { Move-Json -n 'Lyrics' -t $Enable -f $Disable } if ([version]$offline -eq [version]'1.2.30.1135') { Move-Json -n 'QueueOnRightPanel' -t $Enable -f $Disable } if ([version]$offline -le [version]'1.2.50.335') { if (!($plus)) { Move-Json -n "Plus", "AlignedCurationSavedIn" -t $Enable -f $Disable } } if (!$topsearchbar) { Move-Json -n "GlobalNavBar" -t $Enable -f $Disable $Custom.GlobalNavBar.value = "control" if ([version]$offline -le [version]"1.2.45.454") { Move-Json -n "RecentSearchesDropdown" -t $Enable -f $Disable } } if ([version]$offline -le [version]'1.2.50.335') { if (!($funnyprogressbar)) { Move-Json -n 'HeBringsNpb' -t $Enable -f $Disable } } if ([version]$offline -le [version]'1.2.62.580') { if (!$newFullscreenMode) { Move-Json -n "ImprovedCinemaMode", "ImprovedCinemaModeCanvas" -t $Enable -f $Disable } } # disable subfeed filter chips on home if ($homesub_off) { Move-Json -n "HomeSubfeeds" -t $Enable -f $Disable } # Old theme if (!($new_theme) -and [version]$offline -le [version]"1.2.13.661") { Move-Json -n 'RightSidebar', 'LeftSidebar' -t $Enable -f $Disable Remove-Json -j $Custom -p "NavAlt", 'NavAlt2' Remove-Json -j $Enable -p 'RightSidebarLyrics', 'RightSidebarCredits', 'RightSidebar', 'LeftSidebar', 'RightSidebarColors' } # New theme else { if ($rightsidebar_off -and [version]$offline -lt [version]"1.2.24.756") { Move-Json -n 'RightSidebar' -t $Enable -from $Disable } else { if (!($rightsidebarcolor)) { Remove-Json -j $Enable -p 'RightSidebarColors' } if ($old_lyrics) { Remove-Json -j $Enable -p 'RightSidebarLyrics' $Custom.LyricsVariationsInNPV.value = "CONTROL" } } } if (!$premium) { Remove-Json -j $Enable -p 'RemoteDownloads', 'Magpie', 'MagpiePrompting', 'MagpieScheduling', 'MagpieCuration' } # Disable unimportant exp if ($exp_spotify) { $objects = @( @{ Object = $webjson.others.CustomExp.psobject.properties PropertiesToKeep = @('LyricsUpsell') }, @{ Object = $webjson.others.EnableExp.psobject.properties PropertiesToKeep = @('BrowseViaPathfinder', 'HomeViaGraphQLV2') } ) foreach ($obj in $objects) { $propertiesToRemove = $obj.Object.Name | Where-Object { $_ -notin $obj.PropertiesToKeep } $propertiesToRemove | foreach { $obj.Object.Remove($_) } } } $Exp = ($Enable, $Disable, $Custom) foreach ($item in $Exp) { $itemProperties = $item | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name foreach ($key in $itemProperties) { $vers = $item.$key.version if (!($vers.to -eq "" -or [version]$vers.to -ge [version]$offline_patch -and [version]$vers.fr -le [version]$offline_patch)) { if ($item.PSObject.Properties.Name -contains $key) { $item.PSObject.Properties.Remove($key) } } } } $Enable = $webjson.others.EnableExp $Disable = $webjson.others.DisableExp $Custom = $webjson.others.CustomExp $enableNames = foreach ($item in $Enable.PSObject.Properties.Name) { $webjson.others.EnableExp.$item.name } $disableNames = foreach ($item in $Disable.PSObject.Properties.Name) { $webjson.others.DisableExp.$item.name } $customNames = foreach ($item in $Custom.PSObject.Properties.Name) { $custname = $webjson.others.CustomExp.$item.name $custvalue = $webjson.others.CustomExp.$item.value # Create a string with the desired format $objectString = "{name:'$custname',value:'$custvalue'}" $objectString } # Convert the strings of objects into a single text string if ([string]::IsNullOrEmpty($customNames)) { $customTextVariable = '[]' } else { $customTextVariable = "[" + ($customNames -join ',') + "]" } if ([string]::IsNullOrEmpty($enableNames)) { $enableTextVariable = '[]' } else { $enableTextVariable = "['" + ($enableNames -join "','") + "']" } if ([string]::IsNullOrEmpty($disableNames)) { $disableTextVariable = '[]' } else { $disableTextVariable = "['" + ($disableNames -join "','") + "']" } $replacements = @( @("enable:[]", "enable:$enableTextVariable"), @("disable:[]", "disable:$disableTextVariable"), @("custom:[]", "custom:$customTextVariable") ) foreach ($replacement in $replacements) { $webjson.others.ForcedExp.replace = $webjson.others.ForcedExp.replace.Replace($replacement[0], $replacement[1]) } $name = "patches.json.others." $n = "xpui.js" $contents = "ForcedExp" $json = $webjson.others } "RuTranslate" { # Additional translation of some words for the Russian language $n = "ru.json" $contents = $webjsonru.psobject.properties.name $json = $webjsonru } "Binary" { $binary = $webjson.others.binary if ($not_block_update) { Remove-Json -j $binary -p 'block_update' } if ($premium) { Remove-Json -j $binary -p 'block_slots_2', 'block_slots_3' } $name = "patches.json.others.binary." $n = "Spotify.exe" $contents = $webjson.others.binary.psobject.properties.name $json = $webjson.others.binary } "Collaborators" { # Hide Collaborators icon $name = "patches.json.others." $n = "xpui-routes-playlist.js" $contents = "collaboration" $json = $webjson.others } "Dev" { $name = "patches.json.others." $n = "xpui-routes-desktop-settings.js" $contents = "dev-tools" $json = $webjson.others } "VariousofXpui-js" { $VarJs = $webjson.VariousJs if ($premium) { Remove-Json -j $VarJs -p 'mock', 'upgradeButton', 'upgradeMenu' } if ($topsearchbar -or ([version]$offline -ne [version]"1.2.45.451" -and [version]$offline -ne [version]"1.2.45.454")) { Remove-Json -j $VarJs -p "fixTitlebarHeight" } if (!($lyrics_block)) { Remove-Json -j $VarJs -p "lyrics-block" } else { Remove-Json -j $VarJs -p "lyrics-old-on" } if (!($devtools)) { Remove-Json -j $VarJs -p "dev-tools" } else { if ([version]$offline -ge [version]"1.2.35.663") { # Create a copy of 'dev-tools' $newDevTools = $webjson.VariousJs.'dev-tools'.PSObject.Copy() # Delete the first item and change the version $newDevTools.match = $newDevTools.match[0], $newDevTools.match[2] $newDevTools.replace = $newDevTools.replace[0], $newDevTools.replace[2] $newDevTools.version.fr = '1.2.35' # Assign a copy of 'devtools' to the 'devtools' property in $web json.others $webjson.others | Add-Member -Name 'dev-tools' -Value $newDevTools -MemberType NoteProperty # leave only first item in $web json.Various Js.'devtools' match & replace $webjson.VariousJs.'dev-tools'.match = $webjson.VariousJs.'dev-tools'.match[1] $webjson.VariousJs.'dev-tools'.replace = $webjson.VariousJs.'dev-tools'.replace[1] } } if ($urlform_goofy -and $idbox_goofy) { $webjson.VariousJs.goofyhistory.replace = $webjson.VariousJs.goofyhistory.replace -f "`"$urlform_goofy`"", "`"$idbox_goofy`"" } else { Remove-Json -j $VarJs -p "goofyhistory" } if (!($ru)) { Remove-Json -j $VarJs -p "offrujs" } if (!($premium) -or ($cache_limit)) { if (!($premium)) { $adds += $webjson.VariousJs.product_state.add } if ($cache_limit) { if ($cache_limit -lt 500) { $cache_limit = 500 } if ($cache_limit -gt 20000) { $cache_limit = 20000 } $adds2 = $webjson.VariousJs.product_state.add2 if (!([string]::IsNullOrEmpty($adds))) { $adds2 = ',' + $adds2 } $adds += $adds2 -f $cache_limit } $repl = $webjson.VariousJs.product_state.replace $webjson.VariousJs.product_state.replace = $repl -f "{pairs:{$adds}}" } else { Remove-Json -j $VarJs -p 'product_state' } $name = "patches.json.VariousJs." $n = "xpui.js" $contents = $webjson.VariousJs.psobject.properties.name $json = $webjson.VariousJs } } $paramdata = $xpui $novariable = "Didn't find variable " $offline_patch = $offline -replace '(\d+\.\d+\.\d+)(.\d+)', '$1' $contents | foreach { if ( $json.$PSItem.version.to ) { $to = [version]$json.$PSItem.version.to -ge [version]$offline_patch } else { $to = $true } if ( $json.$PSItem.version.fr ) { $fr = [version]$json.$PSItem.version.fr -le [version]$offline_patch } else { $fr = $false } $checkVer = $fr -and $to; $translate = $paramname -eq "RuTranslate" if ($checkVer -or $translate) { if ($json.$PSItem.match.Count -gt 1) { $count = $json.$PSItem.match.Count - 1 $numbers = 0 While ($numbers -le $count) { if ($paramdata -match $json.$PSItem.match[$numbers]) { $paramdata = $paramdata -replace $json.$PSItem.match[$numbers], $json.$PSItem.replace[$numbers] } else { $notlog = "MinJs", "MinJson", "Cssmin" if ($paramname -notin $notlog) { Write-Host $novariable -ForegroundColor red -NoNewline Write-Host "$name$PSItem $numbers"'in'$n } } $numbers++ } } if ($json.$PSItem.match.Count -eq 1) { if ($paramdata -match $json.$PSItem.match) { $paramdata = $paramdata -replace $json.$PSItem.match, $json.$PSItem.replace } else { if (!($translate) -or $err_ru) { Write-Host $novariable -ForegroundColor red -NoNewline Write-Host "$name$PSItem"'in'$n } } } } } $paramdata } function extract ($counts, $method, $name, $helper, $add, $patch) { switch ( $counts ) { "one" { if ($method -eq "zip") { Add-Type -Assembly 'System.IO.Compression.FileSystem' $xpui_spa_patch = Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui.spa' $zip = [System.IO.Compression.ZipFile]::Open($xpui_spa_patch, 'update') $file = $zip.GetEntry($name) $reader = New-Object System.IO.StreamReader($file.Open()) } if ($method -eq "nonezip") { $file = Get-Item (Join-Path (Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui') $name) $reader = New-Object -TypeName System.IO.StreamReader -ArgumentList $file } $xpui = $reader.ReadToEnd() $reader.Close() if ($helper) { $xpui = Helper -paramname $helper } if ($method -eq "zip") { $writer = New-Object System.IO.StreamWriter($file.Open()) } if ($method -eq "nonezip") { $writer = New-Object System.IO.StreamWriter -ArgumentList $file } $writer.BaseStream.SetLength(0) $writer.Write($xpui) if ($add) { $add | foreach { $writer.Write([System.Environment]::NewLine + $PSItem ) } } $writer.Close() if ($method -eq "zip") { $zip.Dispose() } } "more" { Add-Type -Assembly 'System.IO.Compression.FileSystem' $xpui_spa_patch = Join-Path (Join-Path $spotifyDirectory 'Apps') 'xpui.spa' $zip = [System.IO.Compression.ZipFile]::Open($xpui_spa_patch, 'update') $zip.Entries | Where-Object { $_.FullName -like $name -and $_.FullName.Split('/') -notcontains 'spotx-helper' } | foreach { $reader = New-Object System.IO.StreamReader($_.Open()) $xpui = $reader.ReadToEnd() $reader.Close() $xpui = Helper -paramname $helper $writer = New-Object System.IO.StreamWriter($_.Open()) $writer.BaseStream.SetLength(0) $writer.Write($xpui) $writer.Close() } $zip.Dispose() } "exe" { $ANSI = [Text.Encoding]::GetEncoding(1251) $xpui = [IO.File]::ReadAllText($spotify_binary, $ANSI) $xpui = Helper -paramname $helper [IO.File]::WriteAllText($spotify_binary, $xpui, $ANSI) } } } function injection { param( [Alias("p")] [string]$ArchivePath, [Alias("f")] [string]$FolderInArchive, [Alias("n")] [string[]]$FileNames, [Alias("c")] [string[]]$FileContents, [Alias("i")] [string[]]$FilesToInject # force only specific file/files to connect index.html otherwise all will be connected ) $folderPathInArchive = "$($FolderInArchive)/" Add-Type -AssemblyName System.IO.Compression.FileSystem $archive = [System.IO.Compression.ZipFile]::Open($ArchivePath, 'Update') try { for ($i = 0; $i -lt $FileNames.Length; $i++) { $fileName = $FileNames[$i] $fileContent = $FileContents[$i] $entry = $archive.GetEntry($folderPathInArchive + $fileName) if ($entry -eq $null) { $stream = $archive.CreateEntry($folderPathInArchive + $fileName).Open() } else { $stream = $entry.Open() } $writer = [System.IO.StreamWriter]::new($stream) $writer.Write($fileContent) $writer.Dispose() $stream.Dispose() } $indexEntry = $archive.Entries | Where-Object { $_.FullName -eq "index.html" } if ($indexEntry -ne $null) { $indexStream = $indexEntry.Open() $reader = [System.IO.StreamReader]::new($indexStream) $indexContent = $reader.ReadToEnd() $reader.Dispose() $indexStream.Dispose() $headTagIndex = $indexContent.IndexOf("") $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 "