Greasy Fork is available in English.

Swyter Tweaks for Pandora

Provides non-stop music and puts the current song and artist on the title bar. \

  1. // ==UserScript==
  2. // @name Swyter Tweaks for Pandora
  3. // @description Provides non-stop music and puts the current song and artist on the title bar. \
  4. // Additionally removes advertising, unblocks lyrics, provides a persistent skin selector, enforces the Pandora One layout and places a convenient download button. Nothing more, nothing less.
  5. // @include http*://www.pandora.com/*
  6. // @version 2013.06.20
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @namespace https://greasyfork.org/users/4813
  10. // ==/UserScript==
  11.  
  12. function thingie()
  13. {
  14. /* find if the I'm listening button exists and click it if does */
  15. listen=document.getElementsByClassName("still_listening");
  16. if (listen.length > 0)
  17. {
  18. listen[0].click();
  19. }
  20. /* unlock lyrics */
  21. lyrics=document.querySelector(".lyricsText.unselectable");
  22. if (lyrics != null)
  23. {
  24. for (var i=lyrics.attributes.length; i-->0;)
  25. {
  26. lyrics.removeAttributeNode(lyrics.attributes[i]);
  27. }
  28. lyrics.classList.add("lyricsText");
  29. }
  30. /* direct the download link to the current song, redundant -- but should fix fixefox's strange behavior on mouse down (?) */
  31. download=document.getElementById("swydwnld");
  32. download.href=($.jPlayer.prototype.instances.jp_0.data('jPlayer').status.src);
  33. download.download=mysong.text+"-"+artist.text+"."+($.jPlayer.prototype.instances.jp_0.data('jPlayer').status.formatType || "m4a");
  34. /* calls itself every 6s */
  35. setTimeout(thingie,6*1000);
  36. }
  37.  
  38. /* set the Pandora One centered layout */
  39. document.documentElement.classList.add("width-p1-noAds");
  40.  
  41. /* get rid of the in-page advertising container */
  42. if(ads=document.getElementById("ad_container"))
  43. {
  44. ads.parentNode.removeChild(ads);
  45. }
  46.  
  47. /* let's hook the AJAX requests and filter out the audio ads and user tracking */
  48. XMLHttpRequest.prototype.upon = XMLHttpRequest.prototype.open;
  49. XMLHttpRequest.prototype.open = function(method,url)
  50. {
  51. if(url.match(/proxyAdRequest|mediaserverPublicRedirect|brokenAd|stats|andomedia|tritondigital|doubleclick|imrworldwide|scorecardresearch|analytics/) != null)
  52. {
  53. console.info("Audio ad intercepted!",arguments);
  54. this.abort();
  55. }
  56. else
  57. {
  58. this.upon.apply(this,arguments);
  59. }
  60. }
  61.  
  62. /* pick the song and artist fields */
  63. mysong=document.getElementsByClassName("playerBarSong")[0];
  64. artist=document.getElementsByClassName("playerBarArtist")[0];
  65.  
  66. /* add a download button for the current song */
  67. document.getElementsByClassName("buttons")[1].innerHTML+="<a id='swydwnld' \
  68. target='_blank' \
  69. class='button btn_bg' \
  70. style='text-decoration:none; font-size:11px; width:52px; padding-left:3px; color:inherit;'> \
  71. <span style='background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAMCAYAAAC0qUeeAAAA6klEQVR42o2QvQ6CMBSFuzk4OLojccCGAkZNFKQwEPCnZfEBnHxB38LER/AR1EkXpd5bfyJGjDc5adp+57T3EvJWDpd7a5AW7W6m3Egq3JOqcrg4U3+mbNR4jioqYTfKj2DQIAOZ/bQa9rg8PWEUHU3VXzALwOD/gF0uDywU+gsI02FWhhkXa+z6LlHgFLS4noaeCL4IASvSTJI6C+XWi+QFGlRunKuXAfcgMGyIadZ0umF0G2DYOXF+fRq8xwrnBxpnrdJ36EDYcLF/JBUa5F/AV4NBZmESGjDZ4pOU/KpksYyxqU6Y9j7vbuvheB+14VYeAAAAAElFTkSuQmCC) no-repeat 2px 1px;padding-right:17px;'></span>Download</a>";
  72. download=document.getElementById("swydwnld");
  73. download.addEventListener("mouseover",function(e)
  74. {
  75. e.target.href=($.jPlayer.prototype.instances.jp_0.data('jPlayer').status.src);
  76. e.target.download=mysong.text+"-"+artist.text+"."+($.jPlayer.prototype.instances.jp_0.data('jPlayer').status.formatType || "m4a");
  77. })
  78.  
  79. /* backup the original title */
  80. original_title=document.title;
  81.  
  82. /* whenever the info dock changes */
  83. info=document.getElementsByClassName("info")[0];
  84. info.addEventListener("DOMNodeInserted",function()
  85. {
  86. /* set the title bar to the current song */
  87. document.title=(mysong.text.length > 0 &&
  88. artist.text.length > 0) ? ("Now playing «"+mysong.text+"» by «"+artist.text+"»") : original_title;
  89.  
  90. /* direct the download link to the current song, redundant -- but should fix fixefox's strange behavior on mouse down (?) */
  91. download=document.getElementById("swydwnld");
  92. download.href=($.jPlayer.prototype.instances.jp_0.data('jPlayer').status.src);
  93. download.download=mysong.text+"-"+artist.text+"."+($.jPlayer.prototype.instances.jp_0.data('jPlayer').status.formatType || "m4a");
  94. })
  95.  
  96. /* unlock theme chooser */
  97. sknTab=document.getElementById("skinTab");
  98. sknTab.style.display="inherit";
  99.  
  100. /* create a new css link and pin it on the document's head, we'll be using it later */
  101. link=document.createElement("link");
  102. link.rel="stylesheet";
  103.  
  104. document.head.appendChild(link);
  105.  
  106.  
  107. /* ugly jquery callback, launched whenever a theme-preview-button-thingie is clicked */
  108. $(document).on("mouseup",".skin_definition_container",function(e)
  109. {
  110.  
  111. /* pick our id from .skin_definition_container > a > img[src="/preview_{skinid}.png"] */
  112. skin_name=e.currentTarget.children[0].children[0].src.match(/preview_(.+)\.png$/)[1];
  113. /* a few of the preview ids don't match with their css counterparts, if that's the case, fallback... to the pandora designers: that's malapraxis */
  114. skin_name=(
  115. { "default": "pandoraone",
  116. "cosmic": "cosmicrift",
  117. "sea": "deepseadisco"
  118. }[skin_name]) || skin_name;
  119.  
  120. skin_set();
  121. console.log("New theme selected!",skin_name);
  122. })
  123.  
  124.  
  125. function skin_set()
  126. {
  127. /* set our previously made CSS link to the selection */
  128. link.href="http://www.pandora.com/static/pandora_one/skins/"+skin_name+"/skin.css";
  129. /* store as persistent preference */
  130. try{ GM_setValue('swyPandoraTheme', skin_name) }catch(e){ localStorage['swyPandoraTheme']=skin_name}
  131. }
  132.  
  133. /* restore a theme in case it has been selected previously. ugly, i know */
  134. try{ skin_name = GM_getValue('swyPandoraTheme') || skin_name }catch(e){ skin_name = localStorage['swyPandoraTheme'] || skin_name }; skin_set()
  135.  
  136. /* call the recurrent function */
  137. thingie();