Skip to content

Instantly share code, notes, and snippets.

@simonwep
Last active April 11, 2024 12:27
Show Gist options
  • Save simonwep/24f8cdcd6d32d86e929004013bd660ae to your computer and use it in GitHub Desktop.
Save simonwep/24f8cdcd6d32d86e929004013bd660ae to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Spotify ad skipper
// @version 1.0
// @namespace http://tampermonkey.net/
// @description Detects and skips ads on spotify
// @match https://*.spotify.com/*
// @grant none
// @run-at document-start
// @downloadURL https://gist.githubusercontent.com/Simonwep/24f8cdcd6d32d86e929004013bd660ae/raw
// @updateURL https://gist.githubusercontent.com/Simonwep/24f8cdcd6d32d86e929004013bd660ae/raw
// ==/UserScript==
!async function () {
async function queryAsync(query) {
return new Promise(resolve => {
const interval = setInterval(() => {
const element = document.querySelector(query);
if (element) {
clearInterval(interval);
return resolve(element);
}
}, 250);
});
}
/**
* Inject a middleware function in a object or instance
* @param ctx Object or instance
* @param fn Function name
* @param middleware Middleware function
* @param transform Transform function result
*/
function inject({ctx, fn, middleware, transform}) {
const original = ctx[fn];
ctx[fn] = function () {
if (!middleware || middleware.call(this, ...arguments) !== false) {
const result = original.call(this, ...arguments);
return transform ? transform.call(this, result, ...arguments) : result;
}
};
}
const nowPlayingBar = await queryAsync('.now-playing-bar');
const playButton = await queryAsync('button[title=Play], button[title=Pause]');
let audio;
inject({
ctx: document,
fn: 'createElement',
transform(result, type) {
if (type === 'audio') {
audio = result;
}
return result;
}
});
let playInterval;
new MutationObserver(() => {
const link = document.querySelector('.now-playing > a');
if (link) {
if (!audio) {
return console.error('Audio-element not found!');
}
if (!playButton) {
return console.error('Play-button not found!');
}
// console.log('Ad found', audio, playButton, nowPlayingBar);
audio.src = '';
playButton.click();
if (!playInterval) {
playInterval = setInterval(() => {
if (!document.querySelector('.now-playing > a') && playButton.title === 'Pause') {
clearInterval(playInterval);
playInterval = null;
} else {
playButton.click();
}
}, 500);
}
}
}).observe(nowPlayingBar, {
characterData: true,
childList: true,
attributes: true,
subtree: true
});
// Hide upgrade-button and captcha-errors, we don't what to see that.
const style = document.createElement('style');
style.innerHTML = `
[aria-label="Upgrade to Premium"],
body > div:not(#main) {
display: none !important;
}
`;
document.body.appendChild(style);
}();
@hannsen
Copy link

hannsen commented Sep 5, 2019

can you add these 2 lines for automatic updates

// @downloadURL  https://gist.githubusercontent.com/Simonwep/24f8cdcd6d32d86e929004013bd660ae/raw/b08df5ba372fbeab156d93337e36f0070991f2b0/tm-spotify-ad-skipper.js
// @updateURL    https://gist.githubusercontent.com/Simonwep/24f8cdcd6d32d86e929004013bd660ae/raw/b08df5ba372fbeab156d93337e36f0070991f2b0/tm-spotify-ad-skipper.js

@simonwep
Copy link
Author

simonwep commented Sep 5, 2019

@hannsen Oh, that's nice!

@hannsen
Copy link

hannsen commented Sep 5, 2019

Arg, my bad, this doesnt work with gist, as they change the url after you change them.

This url works though: https://gist.githubusercontent.com/Simonwep/24f8cdcd6d32d86e929004013bd660ae/raw

@developerfromjokela
Copy link

Where should I put this script?

@simonwep
Copy link
Author

simonwep commented Feb 8, 2020

@RonanFelipe
Copy link

I'm getting a error. When there is an ad about to start, the music keep in play pause the whole ad duration. The only thing I could do to stop was refresh the page.

@Weridox
Copy link

Weridox commented Apr 18, 2020

It stopped working after few weeks :(

@rikarikrika
Copy link

it's stop working after few days, how to resolve this? ty

@marcbelmont
Copy link

@rikapinka @Weridox

I've updated the script. It now detects ads and mutes until the ad is finished. It's not as clean as previous technique but it does the job for me.
https://gist.github.com/marcbelmont/1ea63270867a4e8786dd5f172d8d4489

@dayoshiguy
Copy link

doesnt work anymore

@dayoshiguy
Copy link

@marcbelmont does it skip ads?

@marcbelmont
Copy link

marcbelmont commented May 9, 2020

@marcbelmont does it skip ads?

@dayoshiguy It mutes the ads. Good enough for me.

@Weridox
Copy link

Weridox commented May 9, 2020

@marcbelmont
Thank you for your work. I use updated script. Works fine for me.

@doconghaph
Copy link

tysm !!

@sinhpn92
Copy link

How to use this script? I did run in the console, but it isn't still working.

@MohamedElashri
Copy link

How to use this script? I did run in the console, but it isn't still working.

You should use something like violentmonkey to install this script.

@mindplay-dk
Copy link

This handy inject function, is that part of a library somewhere?

@Ju2t-us
Copy link

Ju2t-us commented Oct 10, 2022

Works perfectly! I don't have much BAT but here's a tip @simonwep

@prawndumplings
Copy link

prawndumplings commented Jun 10, 2023

Great work, thankyou.

@codenyte
Copy link

Why don't you publish this on Greasy Fork?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment