bt_search_for_bgm

add search icons in bangumi.tv for search anime

  1. // ==UserScript==
  2. // @name bt_search_for_bgm
  3. // @name:zh-CN bangumi 辅助搜索
  4. // @namespace https://bgm.tv/user/a_little
  5. // @description add search icons in bangumi.tv for search anime
  6. // @description:zh-cn 条目页面、合集页面增加搜索图标,辅助搜索
  7. // @include /^https?://(bangumi|bgm|chii)\.(tv|in)/(subject|index|anime|game|book|subject_search)/.*$/
  8. // @include /^https?://(bangumi|bgm|chii).(tv|in)/$/
  9. // @author 22earth
  10. // @version 1.1.0
  11. // @note 1.0.0 使用定期更新搜索引擎列表的方式
  12. // @grant GM_addStyle
  13. // @grant GM_registerMenuCommand
  14. // @grant GM_xmlhttpRequest
  15. // ==/UserScript==
  16.  
  17. /******/ (function(modules) { // webpackBootstrap
  18. /******/ // The module cache
  19. /******/ var installedModules = {};
  20. /******/
  21. /******/ // The require function
  22. /******/ function __webpack_require__(moduleId) {
  23. /******/
  24. /******/ // Check if module is in cache
  25. /******/ if(installedModules[moduleId]) {
  26. /******/ return installedModules[moduleId].exports;
  27. /******/ }
  28. /******/ // Create a new module (and put it into the cache)
  29. /******/ var module = installedModules[moduleId] = {
  30. /******/ i: moduleId,
  31. /******/ l: false,
  32. /******/ exports: {}
  33. /******/ };
  34. /******/
  35. /******/ // Execute the module function
  36. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  37. /******/
  38. /******/ // Flag the module as loaded
  39. /******/ module.l = true;
  40. /******/
  41. /******/ // Return the exports of the module
  42. /******/ return module.exports;
  43. /******/ }
  44. /******/
  45. /******/
  46. /******/ // expose the modules object (__webpack_modules__)
  47. /******/ __webpack_require__.m = modules;
  48. /******/
  49. /******/ // expose the module cache
  50. /******/ __webpack_require__.c = installedModules;
  51. /******/
  52. /******/ // define getter function for harmony exports
  53. /******/ __webpack_require__.d = function(exports, name, getter) {
  54. /******/ if(!__webpack_require__.o(exports, name)) {
  55. /******/ Object.defineProperty(exports, name, {
  56. /******/ configurable: false,
  57. /******/ enumerable: true,
  58. /******/ get: getter
  59. /******/ });
  60. /******/ }
  61. /******/ };
  62. /******/
  63. /******/ // getDefaultExport function for compatibility with non-harmony modules
  64. /******/ __webpack_require__.n = function(module) {
  65. /******/ var getter = module && module.__esModule ?
  66. /******/ function getDefault() { return module['default']; } :
  67. /******/ function getModuleExports() { return module; };
  68. /******/ __webpack_require__.d(getter, 'a', getter);
  69. /******/ return getter;
  70. /******/ };
  71. /******/
  72. /******/ // Object.prototype.hasOwnProperty.call
  73. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  74. /******/
  75. /******/ // __webpack_public_path__
  76. /******/ __webpack_require__.p = "";
  77. /******/
  78. /******/ // Load entry module and return exports
  79. /******/ return __webpack_require__(__webpack_require__.s = 0);
  80. /******/ })
  81. /************************************************************************/
  82. /******/ ([
  83. /* 0 */
  84. /***/ (function(module, exports, __webpack_require__) {
  85.  
  86. "use strict";
  87.  
  88.  
  89. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  90.  
  91. var _gmFetch = __webpack_require__(1);
  92.  
  93. var _index = __webpack_require__(2);
  94.  
  95. var USERJS_PREFIX = "E_USERJS_SEARCH_";
  96. var API_STR = USERJS_PREFIX + "SEARCH_APIS";
  97. var UPDATE_INTERVAL = 24 * 60 * 60 * 1000 * 30;
  98. var VERSION = "1.1.0";
  99. var SEARCH_APIS_URL = "https://raw.githubusercontent.com/22earth/gm_scripts/master/searchapis.json";
  100.  
  101. if (GM_registerMenuCommand) {
  102. // 用户脚本命令清除缓存信息
  103. GM_registerMenuCommand("获取最新搜索引擎列表", function () {
  104. return (0, _index.clearInfoStorage)(USERJS_PREFIX);
  105. }, "f");
  106. }
  107.  
  108. async function getSearchAPIs(str) {
  109. var searchAPIsResource = localStorage.getItem(str);
  110. if (!searchAPIsResource || (0, _index.infoOutdated)(USERJS_PREFIX, UPDATE_INTERVAL, VERSION)) {
  111. console.log("begin fetch apis");
  112. searchAPIsResource = await (0, _gmFetch.gmFetch)(SEARCH_APIS_URL);
  113. var myRules = JSON.parse(searchAPIsResource);
  114. var magnetWRules = await getMagnetWRule();
  115. var apis = Object.assign({}, magnetWRules, myRules);
  116. localStorage.setItem(str, JSON.stringify(apis));
  117.  
  118. localStorage.setItem(USERJS_PREFIX + "VERSION", VERSION);
  119. localStorage.setItem(USERJS_PREFIX + "LATEST_UPDATE_TIME", new Date().getTime());
  120. return apis;
  121. } else if (searchAPIsResource) {
  122. return JSON.parse(searchAPIsResource);
  123. } else {
  124. (0, _index.clearInfoStorage)(USERJS_PREFIX);
  125. return {};
  126. }
  127. }
  128. async function getMagnetWRule() {
  129. var URL = "https://magnetw.app/rule.json";
  130.  
  131. var rules = JSON.parse((await (0, _gmFetch.gmFetch)(URL)));
  132. var myRules = {};
  133. rules.forEach(function (obj) {
  134. var url = obj.url;
  135. if (obj.paths && obj.paths.preset) {
  136. var preset = obj.paths.preset.replace("{k}", "{searchTerms}").replace("{p}", "1");
  137. url = "" + url + preset;
  138. myRules[obj.id] = [obj.name, obj.icon || obj.url + "/favicon.ico", url];
  139. }
  140. });
  141. return myRules;
  142. }
  143.  
  144. async function init() {
  145. var deprecatedEngines = ["btdigg", "camoe", "btcherry"];
  146. var allSearchEngineLists = [["dmhy"], // CN
  147. ["google", "sukebei", "tokyotosho"]];
  148.  
  149. if (!localStorage.getItem("searchEngines") || _typeof(JSON.parse(localStorage.getItem("searchEngines"))) !== "object") {
  150. localStorage.setItem("searchEngines", JSON.stringify(["dmhy", "google"]));
  151. }
  152. // Data format and order like this: name : ["title", "icon", "searchapi"].
  153. // In "searchapi", query string should indead by {searchTerms}.
  154. var searchAPIsUser = {};
  155.  
  156. var searchAPIs = await getSearchAPIs(API_STR);
  157.  
  158. for (var i = 0, len = deprecatedEngines.length; i < len; i++) {
  159. delete searchAPIs[deprecatedEngines[i]];
  160. }
  161. var searchEngineLists = Object.keys(searchAPIs);
  162. var searchEngines = JSON.parse(localStorage.getItem("searchEngines"));
  163. searchEngines = searchEngines.filter(function (e) {
  164. if (searchEngineLists.indexOf(e) !== -1) return true;
  165. });
  166.  
  167. var addSearchIcon = {
  168. init: function init() {
  169. if (window.location.href.match("/subject/") && document.getElementById("navMenuNeue").children[2].children[0].className !== "focus chl") this.addIcon1();else if (window.location.href.match("/anime|index|game|book|subject_search/")) this.addIcon2();
  170. },
  171. createLink: function createLink(link) {
  172. var searchIcon = document.createElement("a");
  173. searchIcon.href = link;
  174. searchIcon.target = "_blank";
  175. searchIcon.className = "searchicon";
  176. var searchIconImg = document.createElement("img");
  177. searchIconImg.style.cssText = "display:inline-block;border:none;height:12px;width:14px;margin-left:2px";
  178. searchIcon.appendChild(searchIconImg);
  179. // add title and icon
  180. var re = new RegExp(searchEngineLists.join("|"));
  181. if (link.match(re)) {
  182. var domain = link.match(re)[0];
  183. searchIcon.title = searchAPIs[domain][0];
  184. var iconURL = searchAPIs[domain][1];
  185. searchIconImg.src = iconURL;
  186. }
  187. return searchIcon;
  188. },
  189.  
  190. getChineseName: function getChineseName(title) {
  191. if (window.location.href.match(/subject_search|index/)) return title.getElementsByClassName("l")[0].textContent;
  192. if (title.getElementsByTagName("a")[0].title) return title.children[0].title;
  193. return title.children[0].textContent;
  194. },
  195.  
  196. getJanpaneseName: function getJanpaneseName(title) {
  197. if (window.location.href.match(/subject_search/)) {
  198. if (title.getElementsByClassName("grey").length) return title.getElementsByClassName("grey")[0].textContent;else return title.getElementsByClassName("l")[0].textContent;
  199. }
  200. if (title.tagName === "H3" && title.children[1] !== undefined) {
  201. return title.children[1].textContent;
  202. } else if (title.tagName === "H1") return title.children[0].textContent;
  203. return "";
  204. },
  205. getLink: function getLink(engineName, animeName) {
  206. return searchAPIs[engineName][2].replace(/\{searchTerms\}/, encodeURIComponent(animeName));
  207. },
  208. addIcon1: function addIcon1() {
  209. // add search icon in subject page
  210. var h1 = document.getElementsByTagName("h1")[0];
  211. if (h1) {
  212. for (var i = 0, len = searchEngines.length; i < len; i++) {
  213. var animeName = this.getJanpaneseName(h1);
  214. var engineName = searchEngines[i];
  215. if (allSearchEngineLists[0].indexOf(engineName) > -1 || !animeName.length) animeName = this.getChineseName(h1);
  216.  
  217. h1.appendChild(this.createLink(this.getLink(engineName, animeName)));
  218. }
  219. }
  220. },
  221.  
  222. addIcon2: function addSearchIcon2() {
  223. // add search icon in anime or index page
  224. // if (window.location.href.match(/subject_search/))
  225. for (var i = 0, len = document.getElementsByTagName("h3").length; i < len; i++) {
  226. var h3 = document.getElementsByTagName("h3")[i];
  227. for (var j = 0; j < searchEngines.length; j++) {
  228. var animeName = this.getJanpaneseName(h3);
  229. var engineName = searchEngines[j];
  230. if (allSearchEngineLists[0].indexOf(engineName) > -1 || !animeName.length) animeName = this.getChineseName(h3);
  231. h3.appendChild(this.createLink(this.getLink(engineName, animeName)));
  232. }
  233. }
  234. }
  235. };
  236.  
  237. var searchSwitch = {
  238. init: function init() {
  239. if (this.isHomepge()) {
  240. this.addStyle();
  241. this.insertStatus();
  242. this.insertSearchEngineSwitch();
  243. }
  244. },
  245. isHomepge: function isHomepge() {
  246. return window.location.pathname === "/" && document.getElementById("columnTimelineInnerWrapper") ? true : false;
  247. },
  248. addStyle: function addStyle(css) {
  249. if (css) {
  250. GM_addStyle(css);
  251. } else {
  252. GM_addStyle([".search-switches {display:none;}", "*:hover > .search-switches {display:block;}", ".search-status {padding: 5px 15px 0;}", ".search-switches {overflow:hidden;}", ".search-switches a {display:inline-block;float:left;margin:5px 5px;padding:5px 5px;border-radius:4px;box-shadow:1px 1px 2px #333;}", ".search-switches a.engine-off {background:#ccffcc none repeat scroll 0 0;color:#333;}", ".search-switches a.engine-on {background:#f09199 none repeat scroll 0 0;color:#fff;}"].join(""));
  253. }
  254. },
  255. insertStatus: function insertStatus() {
  256. // move to sidepanel because of confliction of default function
  257. var colB = document.querySelector("#columnHomeB");
  258. var b = document.createElement("div");
  259. // b.style.height = '500px'; // as high as posible to activate mouse hover event.
  260. colB.appendChild(b);
  261. // main div to show status and toggle search engine
  262. var status = document.createElement("div");
  263. status.className = "search-status";
  264. status.textContent = "已开启" + searchEngines.length + "个搜索引擎";
  265. b.appendChild(status);
  266. var div = document.createElement("div");
  267. div.className = "search-switches";
  268. b.appendChild(div);
  269. b.innerHTML += "<br />";
  270. },
  271. insertSearchEngineSwitch: function insertSearchEngineSwitch() {
  272. var div = document.querySelector(".search-switches");
  273. for (var i = 0; i < searchEngineLists.length; i += 1) {
  274. if (searchEngines.indexOf(searchEngineLists[i]) > -1) {
  275. div.appendChild(this.createSwitch(searchEngineLists[i], "engine-on"));
  276. } else {
  277. div.appendChild(this.createSwitch(searchEngineLists[i], "engine-off"));
  278. }
  279. }
  280. },
  281. createSwitch: function createSwitch(name, aclass) {
  282. var a = document.createElement("a");
  283. a.className = aclass;
  284. a.textContent = name;
  285. a.href = "#";
  286. a.addEventListener("click", function (e) {
  287. var engines = searchEngines;
  288. if (e.target.className === "engine-on") {
  289. e.target.className = "engine-off";
  290. var index = engines.indexOf(e.target.textContent);
  291. if (index > -1) engines.splice(index, 1);
  292. } else {
  293. e.target.className = "engine-on";
  294. engines.push(e.target.textContent);
  295. }
  296. var status = document.querySelector(".search-status");
  297. status.textContent = "已开启" + document.querySelectorAll(".engine-on").length + "个搜索引擎";
  298. localStorage.setItem("searchEngines", JSON.stringify(engines));
  299. e.preventDefault();
  300. });
  301. return a;
  302. },
  303. registerEvent: function registerEvent() {}
  304. };
  305.  
  306. try {
  307. searchSwitch.init();
  308. addSearchIcon.init();
  309. } catch (e) {
  310. console.log(e);
  311. }
  312. }
  313.  
  314. init();
  315.  
  316. /***/ }),
  317. /* 1 */
  318. /***/ (function(module, exports, __webpack_require__) {
  319.  
  320. "use strict";
  321.  
  322.  
  323. function gmFetchBinary(url, TIMEOUT) {
  324. return new Promise(function (resolve, reject) {
  325. GM_xmlhttpRequest({
  326. method: "GET",
  327. timeout: TIMEOUT || 10 * 1000,
  328. url: url,
  329. overrideMimeType: "text\/plain; charset=x-user-defined",
  330. onreadystatechange: function onreadystatechange(response) {
  331. if (response.readyState === 4 && response.status === 200) {
  332. resolve(response.responseText);
  333. }
  334. },
  335. onerror: function onerror(err) {
  336. reject(err);
  337. },
  338. ontimeout: function ontimeout(err) {
  339. reject(err);
  340. }
  341. });
  342. });
  343. }
  344.  
  345. function gmFetch(url, TIMEOUT) {
  346. return new Promise(function (resolve, reject) {
  347. GM_xmlhttpRequest({
  348. method: "GET",
  349. timeout: TIMEOUT || 10 * 1000,
  350. url: url,
  351. onreadystatechange: function onreadystatechange(response) {
  352. if (response.readyState === 4 && response.status === 200) {
  353. resolve(response.responseText);
  354. }
  355. },
  356. onerror: function onerror(err) {
  357. reject(err);
  358. },
  359. ontimeout: function ontimeout(err) {
  360. reject(err);
  361. }
  362. });
  363. });
  364. }
  365.  
  366. module.exports = {
  367. gmFetch: gmFetch,
  368. gmFetchBinary: gmFetchBinary
  369. };
  370.  
  371. /***/ }),
  372. /* 2 */
  373. /***/ (function(module, exports, __webpack_require__) {
  374.  
  375. "use strict";
  376.  
  377.  
  378. function infoOutdated(prefix, interval, version) {
  379. var localVersion = localStorage.getItem(prefix + 'VERSION');
  380. var time = localStorage.getItem(prefix + 'LATEST_UPDATE_TIME');
  381. if (!localVersion || !time || localVersion !== version) {
  382. return true;
  383. }
  384. var now = new Date();
  385. if (now - new Date(time) > interval) {
  386. clearInfoStorage(prefix);
  387. return true;
  388. }
  389. }
  390.  
  391. function clearInfoStorage(prefix) {
  392. var now = new Date();
  393. for (var key in localStorage) {
  394. if (key.match(prefix)) {
  395. console.log(localStorage.getItem(key));
  396. localStorage.removeItem(key);
  397. }
  398. }
  399. }
  400.  
  401. module.exports = {
  402. infoOutdated: infoOutdated,
  403. clearInfoStorage: clearInfoStorage
  404. };
  405.  
  406. /***/ })
  407. /******/ ]);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址