uBlockOrigin / uAssets

Resources for uBlock Origin, uMatrix: static filter lists, ready-to-use rulesets, etc.
GNU General Public License v3.0
3.99k stars 753 forks source link

open.spotify.com: ads #22231

Closed gettysburg closed 7 months ago

gettysburg commented 7 months ago

Prerequisites

URL(s) where the issue occurs.

https://open.spotify.com

Description

All banners are removed, and audio ads are silenced.

However, the audio ads still break playback, and make it stop for a few seconds completely.

Is there a way to skip the audio ads altogether on the web player?

Having to wait a variable amount of time once an ad plays is not optimal.

There is a working AdBlocker for the Windows version of Spotify, which applies various patches through pattern scans to essentially modify JavaScript files before the browser engine executes them, here are some relevant ones:

if (file_name == L"home-hpto.css") {
        const auto hpto = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L".WiPggcPDzbwGxoxwLWFf{-webkit-box-pack:center;-ms-flex-pack:center;display:-webkit-box;display:-ms-flexbox;display:flex;");
        if (hpto.is_found()) {
            if (hpto.write<const char*>(".WiPggcPDzbwGxoxwLWFf{-webkit-box-pack:center;-ms-flex-pack:center;display:-webkit-box;display:-ms-flexbox;display:none;")) {
                Logger::Log(L"hptocss patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"hptocss patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"hptocss - failed not found!", Logger::LogLevel::Error);
        }
    }

This one disables the ads I believe:

        const auto skipads = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"adsEnabled:!0");
        if (skipads.is_found()) {
            if (skipads.offset(12).write<const char>('1')) {
                Logger::Log(L"adsEnabled patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"adsEnabled - patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"adsEnabled - failed not found!", Logger::LogLevel::Error);
        }

Here are the other needed modifications:

        if (sponsorship.is_found()) {
            if (sponsorship.offset(5).write<const char*>(std::string(15, ' ').append("\"").c_str())) {
                Logger::Log(L"sponsorship patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"sponsorship patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"sponsorship - failed not found!", Logger::LogLevel::Error);
        }

        const auto skipsentry = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"sentry.io");
        if (skipsentry.is_found()) {
            if (skipsentry.write<const char*>("localhost")) {
                Logger::Log(L"sentry.io -> localhost patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"sentry.io -> localhost - patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"sentry.io -> localhost - failed not found!", Logger::LogLevel::Error);
        }

        const auto ishptoenable = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"hptoEnabled:!0");
        if (ishptoenable.is_found())
        {
            if (ishptoenable.offset(13).write<const char>('1')) {
                Logger::Log(L"hptoEnabled patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"hptoEnabled - patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"hptoEnabled - failed not found!", Logger::LogLevel::Error);
        }

        const auto ishptohidden = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"isHptoHidden:!0");
        if (ishptohidden.is_found()) {
            if (ishptohidden.offset(14).write<const char>('1')) {
                Logger::Log(L"isHptoHidden patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"isHptoHidden - patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"isHptoHidden - failed not found!", Logger::LogLevel::Error);
        }

        const auto sp_localhost = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"sp://ads/v1/ads/");
        if (sp_localhost.is_found()) {
            if (sp_localhost.write<const char*>("sp://localhost//")) {
                Logger::Log(L"sp://ads/v1/ads/ patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"sp://ads/v1/ads/ - patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"sp://ads/v1/ads/ - failed not found!", Logger::LogLevel::Error);
        }

        const auto premium_free = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"\"free\"===e.session?.productState?.catalogue?.toLowerCase(),r=e=>\"premium\"===e.session?.productState?.catalogue?.toLowerCase()");
        if (premium_free.is_found()) {
            //Print(L"{}", premium_free.read<const char*>());
            if (premium_free.write<const char*>("\"premium\"===e.session?.productState?.catalogue?.toLowerCase(),r=e=>\"free\"===e.session?.productState?.catalogue?.toLowerCase()")) {
                Logger::Log(L"premium patched!", Logger::LogLevel::Info);
            }
            else {
                Logger::Log(L"premium - patch failed!", Logger::LogLevel::Error);
            }
        }
        else {
            Logger::Log(L"premium - failed not found!", Logger::LogLevel::Error);
        }

Code taken from: https://github.com/mrpond/BlockTheSpot/blob/master/src/Modify.cpp

Now, I know this is for the Windows version and C(++) code, but the patches they apply here could also be applied or injected through uBO, what they do instead is hook the web browsers file parser, and change content in the JavaScript files as they are parsed and executed.

I am very certain that uBlock Origin could also integrate this.

Since the Spotify Windows Client is essentially just a browser engine that loads the web player, with some extra app features, applying the same patches from above through filters and injection should work the same.

That way, Spotify Web would believe you already own premium, and ads are disabled through that one variable in xpui.js, and as a result, never even presenting you with audio ads again.

Note that the desktop version also blocks access to some URL's, I can compile a list if wanted.

It would be nice to have fully functioning filters, without the ads getting stuck or taking a long time to recover to play the next song.

I am a long time C/C++ developer, so if you need help interpreting the code, just ask.

For example, for the variable adsEnabled:

const auto skipads = PatternScanner::ScanFirst(reinterpret_cast<std::size_t>(buffer), bufferSize, L"adsEnabled:!0");

they perform a pattern scan, and then modify the 0 to a 1 (since it is already negated by the exclamation mark):

skipads.offset(12).write<const char>('1')

Essentially uBO would have to parse files, search for strings and modify them before the web browser actually runs the JS code.

I believe this is, at least, on Firefox, possible.

As said before, I know C/C++ very well, but am not very prolific with the filter engines and syntax of uBO at all, I have essentially never read up on how it all works.

Please let me know your thoughts on this.. going through the source files for BlockTheSpot probably also won't hurt.

Thank you.

Other extensions used

Old Reddit Redirect Tampermonkey Remove DNT (DoNotTrack) Header from Requests Search by Image Return YouTube Dislike

Screenshot(s)

No screenshots.

Configuration

Details ```uBlock Origin: 1.55.0 Firefox: 115 filterset (summary): network: 320910 cosmetic: 250576 scriptlet: 49548 html: 2259 listset (total-discarded, last-updated): added: adguard-generic: 81884-7055, 16h.40m adguard-mobile: 9098-20, 16h.40m adguard-spyware-url: 1424-103, 16h.40m adguard-spyware: 89713-26976, 16h.40m block-lan: 56-0, 16h.40m curben-phishing: 97767-28, 16h.40m adguard-social: 22473-878, 16h.40m adguard-cookies: 28464-63, 16h.40m ublock-cookies-adguard: 911-0, 16h.40m adguard-popup-overlays: 26878-1682, 16h.40m adguard-mobile-app-banners: 4920-1074, 16h.40m adguard-other-annoyances: 14138-334, 16h.40m [12 lists not shown]: [too many] default: user-filters: 0-0, never ublock-filters: 37434-440, 2h.30m Δ ublock-badware: 7800-10, 2h.30m Δ ublock-privacy: 988-0, 2h.30m Δ ublock-unbreak: 2244-1, 2h.30m Δ ublock-quick-fixes: 136-0, 2h.30m Δ easylist: 79953-1006, 2h.30m Δ easyprivacy: 43568-1109, 2h.30m Δ urlhaus-1: 8535-0, 16h.39m plowe-0: 3786-84, 16h.39m filterset (user): [empty] userSettings: advancedUserEnabled: true cloudStorageEnabled: true hiddenSettings: autoUpdateAssetFetchPeriod: 5 autoUpdateDelayAfterLaunch: 15 supportStats: allReadyAfter: 574 ms (selfie) maxAssetCacheWait: 445 ms cacheBackend: indexedDB popupPanel: blocked: 18 network: spotify.com: 2 cookielaw.org: 1 doubleclick.net: 3 googleoptimize.com: 1 scdn.co: 1 sentry.io: 10 extended: ##span[dir="auto"][draggable="true"]:style(user-select: auto!imp… ##+js(set-constant, rwt, noopFunc) ##+js(trusted-set-cookie, SOCS, CAESHAgBEhJnd3NfMjAyMzA2MTItMF9S… ##+js(remove-attr, ping, div[id="search"] a[data-ved][ping]) ```
ItsProfessional commented 7 months ago
  • [X] I verified that this issue is not a duplicate. (Search here to find out.)

You didn't. #22198

gettysburg commented 7 months ago

Yes, I admit I lied on that one and did not check, but it seems you don't understand the purpose of the thread, the approach the developers of the desktop adblock tool took might work for uBO as well.

Why bother with so many rules when setting one variable is enough?

Should I re-post the same idea in that thread, linking back to this one?