Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@jotafeldmann
Last active February 3, 2023 18:35
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jotafeldmann/963f485805543184b643761c3c3fbdac to your computer and use it in GitHub Desktop.
Save jotafeldmann/963f485805543184b643761c3c3fbdac to your computer and use it in GitHub Desktop.
Music Player Notification: add desktop tracks transitions notifications for Spotify and Youtube Music web player.
/*
# Music Player Notification
Notifications for tracks transitions
- Author: Jorge Feldmann (https://github.com/jotafeldmann)
- Last update: see the gist update date
- Feel free to manipulate and add more music players notifications
## Instructions:
1. Go to the sources URL (check the code)
2. Then paste the following code in the console
3. Finally, play any playlist or song and watch notifications about the music
## Development
- https://gist.github.com/jotafeldmann/963f485805543184b643761c3c3fbdac
- Using `let` so you can change the code and just paste it again
- No plans for any browser plugin: lots of trouble, for this dummy stuff
- Use and change this code by your own
- No guarantees
*/
let MusicSourceFactory = ({
url,
domNodeClassNameToObserve,
getTrackName,
getArtistName,
getAlbumCoverUrl
} = {}) => ({
[url]: {
domNodeClassNameToObserve,
getTrackName,
getArtistName,
getAlbumCoverUrl,
}
})
let musicSourcesConfig = {
... MusicSourceFactory({
url: 'music.youtube.com',
domNodeClassNameToObserve: '.title.style-scope.ytmusic-player-bar',
getTrackName: () => document.querySelector('.title.style-scope.ytmusic-player-bar').textContent,
getArtistName: () => document.querySelector('.yt-simple-endpoint.style-scope.yt-formatted-string').textContent,
getAlbumCoverUrl: () => document.querySelector('.image.style-scope.ytmusic-player-bar').src
}),
... MusicSourceFactory({
url: 'open.spotify.com',
domNodeClassNameToObserve: 'div[data-testid="now-playing-widget"]',
getTrackName: () => document.querySelector('div[data-testid="now-playing-widget"]').innerText.split('\n')[0],
getArtistName: () => document.querySelector('div[data-testid="now-playing-widget"]').innerText.split('\n')[1],
getAlbumCoverUrl: () => document.querySelector('div[data-testid="now-playing-widget"]').children[0].querySelector('img').src,
})
}
let MusicPlayerNotification = (() => {
const observerNodeConfig = { attributes: true, childList: true, subtree: true, characterData: true }
let domNodeClassNameToObserve, getTrackName, getArtistName, getAlbumCoverUrl, observer
const bounceError = fn => {
try {
return fn()
} catch (err) {
console.error(err)
return ''
}
}
const emitTrackChanges = () => (new Notification(bounceError(getTrackName), {
body: bounceError(getArtistName),
icon: bounceError(getAlbumCoverUrl),
silent: true
})).onclick = () => window.focus()
const stopObserver = () => observer.disconnect()
const testNotifications = async () => Notification
.requestPermission()
.then(() => new Notification('Testing notification, if you can see this, its ready'))
.catch(err => { console.error (err); throw new Error(err) })
const initMusicNotifications = () => {
const domNodeToObserve = document.querySelector(domNodeClassNameToObserve)
observer = new MutationObserver(emitTrackChanges)
stopObserver()
observer.observe(domNodeToObserve, observerNodeConfig)
}
const start = async (config) => {
await testNotifications()
domNodeClassNameToObserve = config.domNodeClassNameToObserve
getTrackName = config.getTrackName
getArtistName = config.getArtistName
getAlbumCoverUrl = config.getAlbumCoverUrl
initMusicNotifications()
}
return {
start,
stop: () => stopObserver()
}
})()
let init = () => {
let extraTimeForDOMLoad = 2000
let getMusicSource = (currentUrl = '', urlSources = ['']) => urlSources.filter(s => currentUrl.indexOf(s) > -1)[0]
let source = getMusicSource(document.URL, Object.keys(musicSourcesConfig))
let config = musicSourcesConfig[source]
if (!config) { let msg = 'Cant detect music source'; alert(msg); throw new Error(msg)}
let checkAndPlay = () => (document.readyState === 'complete' ? setTimeout(() => MusicPlayerNotification.start(config), extraTimeForDOMLoad) : null)
checkAndPlay() || document.addEventListener('readystatechange', checkAndPlay)
}
init()
@danidr7
Copy link

danidr7 commented Jul 13, 2021

Nice trick, dude!

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