NPR.org HTML5 player

Listen to NPR without having to install Flash, downloads, no ads.

As of 2016-03-08. See the latest version.

  1. // ==UserScript==
  2. // @name NPR.org HTML5 player
  3. // @description Listen to NPR without having to install Flash, downloads, no ads.
  4. // @namespace https://greatest.deepsurf.us/users/4813-swyter
  5. // @match *://www.npr.org/player/v2/mediaPlayer.html*
  6. // @version 2016.03.08
  7. // @grant GM_addStyle
  8. // @run-at document-start
  9. // ==/UserScript==
  10.  
  11. // https://api.npr.org/query?id=466555217&format=json&apiKey=MDAzMzQ2MjAyMDEyMzk4MTU1MDg3ZmM3MQ010
  12.  
  13. // http://www.npr.org/player/v2/mediaPlayer.html?action=1&t=1&islist=false&id=466555217&m=468149502
  14. // http://www.npr.org/player/v2/mediaPlayer.html?action=1&t=1&islist=false&id=468901493&m=468940337
  15. // http://www.npr.org/player/v2/mediaPlayer.html?action=1&t=1&islist=false&id=468933562&m=469337177&live=1
  16. // http://www.npr.org/player/v2/mediaPlayer.html?action=2&t=1&islist=false&id=469027281&m=469383445
  17.  
  18. /* if there's no ID just redirect to this neat HTML5 page with 24h news */
  19. try
  20. {
  21. id = location.search.split('id=')[1].split('&')[0];
  22. }
  23. catch(e)
  24. {
  25. location.href = 'https://npr.today';
  26. }
  27.  
  28. window.xhr = new XMLHttpRequest();
  29. xhr.open('GET', 'https://api.npr.org/query?id=' + id + '&format=json&apiKey=MDAzMzQ2MjAyMDEyMzk4MTU1MDg3ZmM3MQ010');
  30. xhr.responseType = 'json';
  31. xhr.onload = function(e)
  32. {
  33. console.log(this.response);
  34.  
  35. container = document.createElement("fieldset");
  36. divholder = document.createElement("article");
  37. selector = document.createElement("select");
  38. aplayer = document.createElement("audio");
  39.  
  40. /* replace the Flash warning with our HTML5 player */
  41. flash_sucks = document.querySelector('#homepageFlash, body');
  42. flash_sucks.parentElement.replaceChild(container, flash_sucks);
  43.  
  44. /* add our visible title */
  45. legend = document.createElement("legend");
  46. legend.textContent = this.response.list.story[0].title.$text;
  47.  
  48. /* set a related image in the left side */
  49. divholder.style.backgroundImage = "url(" + this.response.list.story[0].image[0].src + ")";
  50.  
  51. /* set a default song */
  52. aplayer.controls = true;
  53.  
  54. /* ten songs seen at once */
  55. selector.size = 10;
  56.  
  57. audios=this.response.list.story[0].audio;
  58.  
  59. /* fill out the list box with the tracklist */
  60. for(var entry in audios)
  61. {
  62. console.log("=> ", entry % 11, audios[entry]);
  63.  
  64. elem = document.createElement("option");
  65. elem.value = audios[entry].title.$text;
  66. len = audios[entry].duration.$text;
  67. len = (len / 60|0) + ":" + ('00' + (len % 60 | 0)).substr(-2);
  68.  
  69. selector.add(new Option(((entry|0) + 1) + ". " + audios[entry].title.$text + " — " + len, entry));
  70. }
  71.  
  72. container.appendChild(legend);
  73. container.appendChild(divholder);
  74. divholder.appendChild(selector);
  75. divholder.appendChild(aplayer);
  76. /* play the sound track selected in the list box */
  77. selector.onchange = function(e)
  78. {
  79. index = e.explicitOriginalTarget.value;
  80. console.log("(*)", e, xhr.response.list.story[0].audio[index].format.mp3[0].$text);
  81.  
  82. aplayer.src = xhr.response.list.story[0].audio[index].format.mp3[0].$text;
  83. aplayer.play();
  84. };
  85.  
  86. /* skip to the next song/audio in the playlist once the current one has finished */
  87. aplayer.addEventListener('ended', function(e)
  88. {
  89. console.log("(/)", e, selector.value, (selector.value + 1) % (selector.options.length) );
  90.  
  91. /* don't do anything if there's just a single item */
  92. if (selector.length <= 1)
  93. return;
  94.  
  95. /* wrap around with the modulo operator looping back last->first */
  96. selector.value++; //selector %= selector.options.length;
  97. console.log(selector.value, selector.options[selector.value]);
  98. /* as stupid as it looks like, javascript is a flaming pit of madness */
  99. selector.onchange({explicitOriginalTarget: selector.options[selector.value]});
  100. });
  101. /* autostart from the first song */
  102. selector.options[0].selected = true;
  103. selector.onchange({explicitOriginalTarget: selector.options[0]});
  104. };
  105.  
  106. xhr.send();
  107.  
  108. GM_addStyle("audio, select {display:block; width:100%;} \
  109. article {padding-left:240px; background-size: 240px auto; background-repeat: no-repeat;} \
  110. legend {font-size: medium; padding: 5px; color: #4067b2; background: #EBEBEB url('http://media.npr.org/chrome/news/npr-home.png') repeat-y scroll 0px 0px / 88px auto; padding-left: 93px; left: -88px; background-position: left bottom; margin-left: 152px;} \
  111. * {font-family: 'Gotham SSm',Helvetica,Arial,sans-serif !important;} \
  112. @media(max-width: 480px) {article {padding-left:0} legend {margin-left:0}}");
  113.  
  114. /* will this generated stylesheet used in the main site last? who knows */
  115. document.querySelector("link[rel=stylesheet]").href = 'https://s.npr.org/templates/css/generated/fingerprint/persistent-73cb243d716b971f095c14e7dfdb3432d2ecb623.css';