HTML5 notifications on Spotify Web Player

Adds silent song notifications with title, artist and cover art

As of 2015-11-14. See the latest version.

  1. // ==UserScript==
  2. // @name HTML5 notifications on Spotify Web Player
  3. // @description Adds silent song notifications with title, artist and cover art
  4. // @namespace https://greatest.deepsurf.us/users/4813-swyter
  5. // @match https://play.spotify.com/*
  6. // @version 2015.11.14.03
  7. // @grant none
  8. // @noframes
  9. // ==/UserScript==
  10.  
  11. /* run this just on the parent page, not in sub-frames */
  12. if (window.parent !== window)
  13. throw "stop execution";
  14.  
  15. function when_external_loaded()
  16. {
  17. /* request permission to show notifications, if needed */
  18. if (Notification.permission !== 'granted')
  19. Notification.requestPermission();
  20.  
  21. /* create a listener mechanism for title changes using mutation observers,
  22. let's be good citizens (http://stackoverflow.com/a/29540461) */
  23. document.aptEventListener = document.addEventListener;
  24. document.addEventListener = function(what, callback)
  25. {
  26. if (what !== 'title')
  27. return document.aptEventListener.apply(this, arguments);
  28.  
  29. console.log('setting title event listener =>', arguments);
  30.  
  31. new MutationObserver(function(mutations)
  32. {
  33. console.log('mutation observer triggered =>', mutations[0].target); setTimeout(callback, 1500);
  34. }).observe(document.querySelector('title'), {subtree: true, childList: true, characterData: true});
  35. };
  36.  
  37. /* trigger a new notification every time the page title changes */
  38. document.addEventListener('title', function()
  39. {
  40. /* trigger it only if we are actually playing songs and we haven't
  41. shown a notification for this song already */
  42. if (!document.title.match('▶') || document.prevtitle === document.title)
  43. {
  44. console.log('returning without showing it up =>', document.title);
  45. return;
  46. }
  47.  
  48. /* save the current title to avoid needless repetition */
  49. document.prevtitle = document.title;
  50.  
  51. /* some debug printing to help out development, seems work work pretty nicely */
  52. console.log('this seems to be a new song, showing it up =>', document.title);
  53.  
  54. /* feel free to customize the formatting and layout to your liking */
  55. var track_name = document.querySelector("iframe#app-player").contentWindow.document.querySelector("#track-name > a").textContent;
  56. var track_artist = document.querySelector("iframe#app-player").contentWindow.document.querySelector("#track-artist > a").textContent;
  57. var track_coverart = document.querySelector("iframe#app-player").contentWindow.document.querySelector(".sp-image-img").style.backgroundImage.replace(/"/g,'').split("(")[1].split(")")[0];
  58.  
  59. /* show it! */
  60. new Notification(track_name, {body: track_artist, icon: track_coverart, silent: true});
  61. });
  62. }
  63.  
  64. /* inject this cleaning function right in the page to avoid silly sandbox-related greasemonkey limitations */
  65. window.document.head.appendChild(
  66. inject_fn = document.createElement("script")
  67. );
  68.  
  69. inject_fn.innerHTML = '(' + when_external_loaded.toString() + ')()';