豆瓣助手

恢复IMDB的链接,展示IMDB评分,以及增加快捷搜索SubHD、字幕库、射手网、opensubtitle、6V电影网、电影天堂、新电影天堂、WebHD、rargb、海盗湾、watchsomuch、tgx、EXT、腾讯视频、优酷视频、爱奇艺、哔哩哔哩、西瓜视频、欢喜首映中资源的功能

  1. // ==UserScript==
  2. // @name 豆瓣助手
  3. // @version 0.0.19
  4. // @namespace airbash/DoubanAssistant
  5. // @homepageURL https://github.com/AirBashX/UserScript
  6. // @author airbash
  7. // @description 恢复IMDB的链接,展示IMDB评分,以及增加快捷搜索SubHD、字幕库、射手网、opensubtitle、6V电影网、电影天堂、新电影天堂、WebHD、rargb、海盗湾、watchsomuch、tgx、EXT、腾讯视频、优酷视频、爱奇艺、哔哩哔哩、西瓜视频、欢喜首映中资源的功能
  8. // @match *://movie.douban.com/subject/*
  9. // @connect www.hao6v.me
  10. // @connect www.imdb.com
  11. // @connect dy2018.com
  12. // @grant GM_registerMenuCommand
  13. // @grant GM_unregisterMenuCommand
  14. // @grant GM_setValue
  15. // @grant GM_getValue
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_addStyle
  18. // @license GPL-3.0
  19. // @icon https://img1.doubanio.com/favicon.ico
  20. // @require https://fastly.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.all.min.js
  21. // @require https://fastly.jsdelivr.net/npm/encode-gb2312@0.0.2/encodeToGb2312.min.js
  22. // @run-at document-end
  23. // ==/UserScript==
  24. /* global encodeToGb2312 Swal*/
  25. ///<reference path="./tampermonkey-reference.d.ts" />
  26. ///<reference path="./sweetalert2.d.ts" />
  27. (function () {
  28. // "use strict";
  29. const url = location.href;
  30. const head = document.head;
  31.  
  32. //获取imdbitem
  33. let info_item = document.querySelector("#info");
  34. let imdb_item = document.evaluate('//span[text()="IMDb:"]', info_item, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  35. let imdb_id_item = document.evaluate('//span[text()="IMDb:"]/following::text()[1]', info_item, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  36.  
  37. //获取imdb_id
  38. let imdb_id = imdb_id_item.textContent.trim();
  39.  
  40. //获取douban_id
  41. let douban_id = url.split("/")[4];
  42.  
  43. //获取douban_cn_name
  44. let douban_cn_name = head.querySelector("title").innerText.slice(9, -6);
  45. //获取GBK编码的douban_cn_name
  46. let douban_cn_name_gbk = encodeToGb2312(douban_cn_name).replace(/(.{2})/gi, "%$1");
  47.  
  48. //获取douban_en_name
  49. let douban_all_name = document.querySelector("#content > h1 > span:nth-child(1)").innerHTML;
  50. let douban_en_name = douban_all_name.split(douban_cn_name)[1].trim();
  51. if (douban_en_name == null) {
  52. douban_en_name = douban_cn_name;
  53. }
  54.  
  55. /**
  56. * 恢复IMDB链接
  57. */
  58. function imdb_link() {
  59. let div = document.createElement("div");
  60. div.innerHTML = "<span class='pl'>IMDb:</span><a target='_blank' href='https://www.imdb.com/title/" + imdb_id + "'>&nbsp" + imdb_id + "</a><br>";
  61. imdb_id_item.after(div);
  62. //删除原本的idmb链接
  63. imdb_item.remove();
  64. imdb_id_item.remove();
  65. }
  66.  
  67. /**
  68. * 获取IMDB评分
  69. */
  70. function imdb_score() {
  71. GM_xmlhttpRequest({
  72. url: "https://www.imdb.com/title/" + imdb_id,
  73. onload: function (response) {
  74. let text = response.responseText;
  75. let dp = new DOMParser();
  76. let html = dp.parseFromString(text, "text/html");
  77. let item = html.querySelector("[data-testid=hero-rating-bar__aggregate-rating__score] span");
  78. let imdb_score = item.innerText;
  79.  
  80. let str = html.querySelector("#__NEXT_DATA__").textContent;
  81. let json = JSON.parse(str);
  82. let imdb_vote = json.props.pageProps.aboveTheFoldData.ratingsSummary.voteCount;
  83.  
  84. let self = document.querySelector(".rating_self").cloneNode(true);
  85. let logo = document.querySelector(".rating_logo").cloneNode(true);
  86. let sectl = document.querySelector("#interest_sectl");
  87. sectl.append(logo);
  88. sectl.append(self);
  89.  
  90. //增加IMDB名
  91. logo.innerText = "IMDB评分";
  92. //修改文字评分
  93. self.querySelector(".rating_num").innerText = imdb_score;
  94. //修改图形评分
  95. let classList = self.querySelector(".bigstar").classList;
  96. classList.replace(classList.item(2), "bigstar" + (Math.floor(imdb_score) / 2) * 10);
  97. //修改IMDB人数
  98. self.querySelector(".rating_people span").innerText = imdb_vote;
  99. self.querySelector(".rating_people").href = "https://www.imdb.com/title/" + imdb_id + "/ratings";
  100.  
  101. // let div = document.createElement("div");
  102. // div.innerHTML = "<span class='pl'>IMDb评分:</span>" + score + "<br>";
  103. // document.querySelector(".rating_wrap").after(div);
  104. },
  105. });
  106. }
  107.  
  108. /**
  109. * 侧边栏功能列表
  110. */
  111. const webSites = [
  112. {
  113. id: "group1",
  114. name: "字幕搜索",
  115. links: [
  116. {
  117. name: "SubHD",
  118. url: "subhd.tv",
  119. search: "https://subhd.tv/search/" + douban_cn_name,
  120. },
  121. {
  122. name: "射手网",
  123. url: "assrt.net",
  124. search: "https://assrt.net/sub/?searchword=" + douban_cn_name,
  125. },
  126. {
  127. name: "字幕库",
  128. url: "zimuku.org",
  129. search: "https://so.zimuku.org/search?q=" + douban_cn_name,
  130. },
  131. {
  132. name: "opensubtitle",
  133. url: "opensubtitles.org",
  134. search: "https://www.opensubtitles.com/zh-CN/zh-CN/search-all/q-" + imdb_id + "/hearing_impaired-include/machine_translated-/trusted_sources-",
  135. },
  136. ],
  137. },
  138. {
  139. id: "group2",
  140. name: "英文资源",
  141. links: [
  142. {
  143. name: "rargb",
  144. url: "rargb.to",
  145. search: "https://rargb.to/search/?search=" + douban_en_name,
  146. },
  147. {
  148. name: "海盗湾",
  149. url: "thepiratebay10.xyz",
  150. search: "https://thepiratebay10.xyz/search/" + douban_en_name,
  151. },
  152. {
  153. name: "WSM",
  154. url: "watchsomuch.to",
  155. search: "https://watchsomuch.to/Movies/" + douban_en_name,
  156. },
  157. {
  158. name: "tgx",
  159. url: "tgx.rs",
  160. search: "https://tgx.rs/torrents.php?search=" + douban_en_name,
  161. },
  162. {
  163. name: "1377x",
  164. url: "www.1377x.to",
  165. search: "https://www.1377x.to/search/" + douban_en_name + "/1/",
  166. },
  167. {
  168. name: "EXT",
  169. url: "extranet.torrentbay.st",
  170. search: "https://extranet.torrentbay.st/browse/?q=" + douban_en_name,
  171. },
  172. ],
  173. },
  174. {
  175. id: "group3",
  176. name: "中文资源",
  177. links: [
  178. {
  179. name: "6v电影网",
  180. url: "www.hao6v.tv",
  181. search: "https://www.hao6v.me/e/search/index.php",
  182. data: "show=title%2Csmalltext&tempid=1&keyboard=" + douban_cn_name_gbk + "&tbname=article&x=0&y=0",
  183. type: "xhr",
  184. anonymous: true,
  185. },
  186. {
  187. name: "电影天堂",
  188. url: "www.dy2018.com/",
  189. search: "https://www.dy2018.com/",
  190. },
  191. {
  192. name: "新电影天堂",
  193. url: "www.xl720.com",
  194. search: "https://www.xl720.com/?s=" + douban_cn_name,
  195. },
  196. ],
  197. },
  198. {
  199. id: "group4",
  200. name: "正版资源",
  201. links: [
  202. {
  203. name: "腾讯视频",
  204. url: "v.qq.com",
  205. search: "https://v.qq.com/x/search/?q=" + douban_cn_name,
  206. },
  207. {
  208. name: "优酷视频",
  209. url: "youku.com",
  210. search: "https://so.youku.com/search_video/q_" + douban_cn_name,
  211. },
  212. {
  213. name: "爱奇艺",
  214. url: "iqiyi.com",
  215. search: "https://so.iqiyi.com/so/q_" + douban_cn_name,
  216. },
  217. {
  218. name: "哔哩哔哩",
  219. url: "bilibili.com",
  220. search: "https://search.bilibili.com/all?keyword=" + douban_cn_name,
  221. },
  222. {
  223. name: "西瓜视频",
  224. url: "www.ixigua.com",
  225. search: "https://www.ixigua.com/search/" + douban_cn_name,
  226. },
  227. {
  228. name: "欢喜首映",
  229. url: "www.huanxi.com",
  230. search: "https://www.huanxi.com/search.shtml?sv=" + douban_cn_name,
  231. },
  232. ],
  233. },
  234. ];
  235.  
  236. /**
  237. * 功能列表
  238. */
  239. const GMValues = [
  240. {
  241. id: "imdb_link",
  242. name: "IMDB链接",
  243. fun: imdb_link,
  244. },
  245. {
  246. id: "imdb_score",
  247. name: "IMDB评分",
  248. fun: imdb_score,
  249. },
  250. ];
  251.  
  252. if (imdb_id) {
  253. for (let GMValue of GMValues) {
  254. if (GM_getValue(GMValue.id, true)) {
  255. GMValue.fun();
  256. }
  257. }
  258. }
  259.  
  260. aside();
  261.  
  262. /**
  263. * 脚本工具菜单
  264. */
  265. GM_registerMenuCommand("配置", () => {
  266. configuration();
  267. });
  268.  
  269. /**
  270. * 侧边栏功能
  271. */
  272. function aside() {
  273. let aside = document.querySelector(".aside");
  274. for (let webSite of webSites) {
  275. let div = document.createElement("div");
  276. div.className = "resource";
  277. if (GM_getValue(webSite.id, true)) {
  278. div.innerHTML = "<h2><i>" + webSite.name + "</i>· · · · · ·</h2>";
  279. aside.prepend(div);
  280. let ul = document.createElement("ul");
  281. ul.className = "resources";
  282. div.appendChild(ul);
  283. for (let link of webSite.links) {
  284. if (link.type != "xhr") {
  285. let str = '<a href="' + link.search + '" target="_blank">' + link.name + "</a>";
  286. let a = document.createRange().createContextualFragment(str);
  287. ul.appendChild(a);
  288. } else {
  289. GM_xmlhttpRequest({
  290. url: link.search,
  291. method: "POST",
  292. headers: {
  293. "Content-Type": "application/x-www-form-urlencoded",
  294. },
  295. timeout: 5000,
  296. anonymous: link.anonymous,
  297. data: link.data,
  298. onload: function (response) {
  299. //chrome+tm会出现兼容性问题:
  300. let finalUrl = response.finalUrl;
  301. if (finalUrl.search("/index.php/") != -1) {
  302. finalUrl = finalUrl.replace("/index.php", "");
  303. }
  304.  
  305. let str;
  306. if (finalUrl == link.search) {
  307. str = '<a href="' + finalUrl + '" target="_blank" style="color:pink" title="没有资源">' + link.name + "</a>";
  308. } else {
  309. str = '<a href="' + finalUrl + '" target="_blank">' + link.name + "</a>";
  310. }
  311. let a = document.createRange().createContextualFragment(str);
  312. ul.appendChild(a);
  313. },
  314. });
  315. }
  316. }
  317. }
  318. }
  319.  
  320. const resourceStyle = document.createElement("style");
  321. resourceStyle.innerHTML =
  322. ".resource {margin-bottom: 30px} .resources {padding: 0 12px;*letter-spacing: normal} .resources a {border-radius: 6px;color: #37A;display: inline-block;letter-spacing: normal;margin: 0 8px 8px 0;padding: 0 8px;text-align: center;width: 65px} .resources a:link, .resources a:visited {background-color: #f5f5f5;color: #37A} .resources a:hover, .resources a:active {background-color: #e8e8e8;color: #37A} .resources a.disabled {text-decoration: line-through} .resources a.available {background-color: #5ccccc;color: #006363} .resources a.available:hover, .resources a.available:active {background-color: #3cc} .resources a.honse {background-color: #fff0f5;color: #006363} .resources a.honse:hover, .resources a.honse:active {background-color: #3cc} .resources a.sites_r0 {text-decoration: line-through}";
  323. document.head.appendChild(resourceStyle);
  324. }
  325.  
  326. /**
  327. * 配置
  328. */
  329. function configuration() {
  330. Swal.fire({
  331. title: "豆瓣助手 配置",
  332. html: swal_html,
  333. showCloseButton: true,
  334. didRender: () => {
  335. GM_addStyle(
  336. '.swal2-html-container{text-align:left !important;line-height:unset !important;}.smail_div{width:33%;float:left;}.switch{float:left;position:relative;top:3px;width:40px;height:20px;display:flex;}.checkbox{z-index:3;position:relative;width:100%;height:100%;cursor:pointer;opacity:0;}.bt{z-index:2;position:absolute;top:0;bottom:0;}.bt:before{position:absolute;top:2.5px;left:2.5px;content:"";width:15px;height:15px;background-color:red;border-radius:50%;transition:0.3s cubic-bezier(0.18,0.89,0.35,1.15) all;}.checkbox:checked + .bt:before{left:20px;background-color:#03a9f4;}.bg{z-index:1;position:absolute;top:0;right:0;bottom:0;left:0;border-radius:100px;background-color:#fcebeb;}.checkbox:checked ~ .bg{background-color:#ebf7fc;}',
  337. );
  338. /**
  339. * 侧边栏开关
  340. */
  341. for (let webSite of webSites) {
  342. if (GM_getValue(webSite.id, true)) {
  343. document.querySelector("#DA_div #" + webSite.id).checked = true;
  344. }
  345. }
  346. if (imdb_id) {
  347. for (let GMValue of GMValues) {
  348. if (GM_getValue(GMValue.id, true)) {
  349. document.querySelector("#DA_div #" + GMValue.id).checked = true;
  350. }
  351. }
  352. }
  353. },
  354. }).then((result) => {
  355. let change;
  356. if (result.isConfirmed) {
  357. for (let webSite of webSites) {
  358. if (document.querySelector("#DA_div #" + webSite.id).checked != GM_getValue(webSite.id, true)) {
  359. GM_setValue(webSite.id, document.querySelector("#DA_div #" + webSite.id).checked);
  360. change = true;
  361. }
  362. }
  363. if (imdb_id) {
  364. for (let GMValue of GMValues) {
  365. if (document.querySelector("#DA_div #" + GMValue.id).checked != GM_getValue(GMValue.id, true)) {
  366. GM_setValue(GMValue.id, document.querySelector("#DA_div #" + GMValue.id).checked);
  367. change = true;
  368. }
  369. }
  370. }
  371.  
  372. if (change) {
  373. location.reload();
  374. }
  375. }
  376. });
  377. }
  378.  
  379. let swal_html = "<div id='DA_div'>";
  380. for (let webSite of webSites) {
  381. swal_html += '<div class="smail_div"><div class="switch"><input type="checkbox" class="checkbox" id="' + webSite.id + '"/><div class="bt"></div><div class="bg"></div></div>' + webSite.name + "</div>";
  382. }
  383. for (let GMValue of GMValues) {
  384. swal_html += '<div class="smail_div"><div class="switch"><input type="checkbox" class="checkbox" id="' + GMValue.id + '"/><div class="bt"></div><div class="bg"></div></div>' + GMValue.name + "</div>";
  385. }
  386. swal_html += "</div>";
  387. })();