eBird媒体下载器

添加从eBird下载媒体或在浏览器中浏览的按钮

  1. // ==UserScript==
  2. // @name eBird media downloader
  3. // @name:zh-CN eBird媒体下载器
  4. // @namespace https://github.com/sun-jiao
  5. // @match https://*.ebird.org/*
  6. // @match https://*.macaulaylibrary.org/*
  7. // @match https://birdsoftheworld.org/*
  8. // @grant GM_xmlhttpRequest
  9. // @version 3.0
  10. // @author Sun Jiao
  11. // @license GPL_v3
  12. // @description Add a button to download the medias from eBird or view it in browser.
  13. // @description:zh-cn 添加从eBird下载媒体或在浏览器中浏览的按钮
  14. // ==/UserScript==
  15.  
  16.  
  17. const PREFIX = 'https://cdn.download.ams.birds.cornell.edu/api/v1/asset/';
  18. const SUFFIX = '/2400';
  19. const SPEC = '/spectrogram_small';
  20. const AUDIO = '/audio';
  21. const VIDEO = '/mp4/1280';
  22.  
  23. var download = "Download";
  24.  
  25. (function() {
  26. 'use strict';
  27.  
  28. setDownload();
  29.  
  30. try {
  31. ebird()
  32. } catch (error) {
  33. console.error(error);
  34. }
  35.  
  36. window.addEventListener('load', function () {
  37. try {
  38. ebird()
  39. } catch (error) {
  40. console.error(error);
  41. }
  42. })
  43.  
  44. MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
  45. var observer = new MutationObserver(function(mutations, observer) {
  46. // fired when a mutation occurs
  47. try {
  48. ebird()
  49. } catch (error) {
  50. console.error(error);
  51. }
  52. // console.log(mutations, observer);
  53. });
  54.  
  55. // define what element should be observed by the observer
  56. // and what types of mutations trigger the callback
  57. observer.observe(document, {
  58. subtree: true,
  59. attributes: true
  60. });
  61. })();
  62.  
  63. function setDownload(){
  64. var userLang = navigator.language || navigator.userLanguage;
  65. switch(userLang.split("-")[0]){
  66. case("zh"):{
  67. download = "下载";
  68. break;
  69. }
  70. case("fr"):{
  71. download = "Télécharger";
  72. break;
  73. }
  74. case("ja"):{
  75. download = "ダウンロード";
  76. break;
  77. }
  78. case("uk"):{
  79. download = "Завантаження файлів";
  80. break;
  81. }
  82. case("ko"):{
  83. download = "다운로드";
  84. break;
  85. }
  86. case("ru"):{
  87. download = "скачать";
  88. break;
  89. }
  90. case("pt"):{
  91. download = "Baixar";
  92. break;
  93. }
  94. }
  95. }
  96.  
  97. function ebird(){
  98. let lists = document.getElementsByClassName('Lightbox-links');
  99. for (const list of lists) {
  100. var href1 = list.getElementsByTagName("div").at(-1).getElementsByTagName("a")[0].getAttribute("href");
  101. let ID = getID(href1);
  102. process(list, ID);
  103. }
  104.  
  105. if (window.location.hostname == "macaulaylibrary.org") {
  106. let lists2 = document.getElementsByClassName("actions");
  107. for (const list of lists2) {
  108. let ID = getID(window.location.href);
  109. process(list, ID);
  110. }
  111. }
  112. }
  113.  
  114. function process(list, ID){
  115. console.log('ID:' + ID);
  116.  
  117. var href = PREFIX + ID + SUFFIX;
  118.  
  119. if (document.getElementsByClassName("SpectrogramPlayer").length > 0){
  120. href = PREFIX + ID + AUDIO;
  121. } else if (document.getElementsByClassName("VideoPlayer-video").length > 0){
  122. href = PREFIX + ID + VIDEO;
  123. }
  124.  
  125. if(list.getElementsByClassName("Raccoon-s-script").length > 0){
  126. return;
  127. }
  128.  
  129. var text = "\t<div class=\"Raccoon-s-script\">\r\t\t\t\t<a target=\"_blank\" rel=\"noopener\" class=\"\" href=\"" + href + "\">\r\t\t\t\t\t<svg class=\"Icon Icon--download\" role=\"img\"><use xlink:href=\"#Icon--download\"></use></svg>\r\t\t\t\t\t" + download + "\r\t\t\t\t</a>\r\t\t\t</div>"
  130. var downloadBtn = stringToHTML(text);
  131. var new_div = downloadBtn.getElementsByTagName("div")[0]
  132. list.appendChild(new_div);
  133. }
  134.  
  135. function getID(href){
  136. //一般来说该媒体文件在Macaulay Library的链接的末尾即是媒体id,例如:https://macaulaylibrary.org/zh-CN/asset/230539581
  137. //有些情况下,上面那种链接之后还会以#号或者?问号为分隔附一段,应当被删除。
  138. //例如:https://macaulaylibrary.org/asset/180854341#_ga=2.225998489.2098077357.1654784315-1293864495.1651583523
  139. //或者:https://macaulaylibrary.org/asset/180854341?_gl=1*v5o4c3*_ga*MTI5Mzg2NDQ5NS4xNjUxNTgzNTIz*_ga_QR4NVXZ8BM*MTY1NDg0MDgwMC4xOC4xLjE2NTQ4NDA4MTQuNDY.#_ga=2.28237631.2098077357.1654784315-1293864495.1651583523
  140. //有些时候不是以asset结尾,例如:https://macaulaylibrary.org/photo/106358811
  141. if (href.includes("asset/")){
  142. return href.split("asset/")[1].split(/[^0-9]/)[0];
  143. } else {
  144. return href.split("/").at(-1);
  145. }
  146. }
  147.  
  148. /**
  149. * Convert a template string into HTML DOM nodes
  150. * Copied from: https://www.codegrepper.com/code-examples/javascript/convert+string+to+html+format+in+javascript
  151. * @param {String} str The template string
  152. * @return {Node} The template HTML
  153. */
  154. function stringToHTML(str) {
  155. var parser = new DOMParser();
  156. var doc = parser.parseFromString(str, 'text/html');
  157. return doc.body;
  158. }
  159.  
  160. /**
  161. * In fact this is an official function for Array, but not supported for HTMLCollection.
  162. * see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at
  163. * @param {Int} num The index number.
  164. */
  165. HTMLCollection.prototype.at = function(num) {
  166. if(num >= 0){
  167. return this[num];
  168. } else {
  169. return this[this.length + num];
  170. }
  171. };