BilibiliCover - 获取并显示B站视频封面图片

获取并显示B站视频封面

Od 15.08.2021.. Pogledajte najnovija verzija.

  1. // ==UserScript==
  2. // @name BilibiliCover - 获取并显示B站视频封面图片
  3. // @version 3.8.1
  4. // @description 获取并显示B站视频封面
  5. // @author AnnAngela
  6. // @namespace https://greasyfork.org/users/129402
  7. // @mainpage https://greasyfork.org/zh-CN/scripts/33411-bilibilicover
  8. // @supportURL https://greasyfork.org/zh-CN/scripts/33411-bilibilicover/feedback
  9. // @license GNU General Public License v3.0 or later
  10. // @compatible chrome 80
  11. // @compatible firefox
  12. // @compatible opera
  13. // @compatible safari
  14. // @match https://www.bilibili.com/watchlater/#/*
  15. // @match https://www.bilibili.com/video/av*
  16. // @match https://www.bilibili.com/video/bv*
  17. // @match https://www.bilibili.com/video/BV*
  18. // @match https://www.bilibili.com/bangumi/play/*
  19. // @match https://www.bilibili.com/medialist/play/watchlater/*
  20. // @match https://live.bilibili.com/*
  21. // @run-at document-start
  22. // @grant unsafeWindow
  23. // @grant GM_xmlhttpRequest
  24. // @noframes
  25. // @icon data:image/png;base64,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
  26. // @icon64 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAC4jAAAuIwF4pT92AAAIVklEQVR4Xu2bWYwVRRSGzyzIBYwbAmpwFzPigqCi0ZgoJooa3GKMGBg3VMAIcY3xQREFNW5xfXAF1AiKPrhEURl3E6O4srggojCCIIIgIuIM/t+cauZ6p293j3jDZZiHL5lkpk7956/q6upTNdbY2GgNDQ1bLHbjZ0tszKc/Z6FC7CmOF0PEKHGxOEv0F9uL6gxxNpbq0Ff/0PclYrQYOsa1obEiQ5wmrGrSbKuaOCsLlWKAuFNME3PFR2KquEzsJXKiIkOs/0pF6GOv0Cd9zxDzxGtVrg2NlRliNWE2cbbZhFlpdBX7i9FiuvhW/CrqxUwxWVwpjhPdRHWGmK2lOsQ+LvQ1JfT9k1gu5oo3zDWitWuGmJkN2FecKR4Vv4h1okH8HX5eKD4QY0SNyImKDHGzUhFi1oQ+6Ks+9I2GhvDzUnONaN03Q9zMBtDx2WKCuduNBawUC8SL4mr7f2dC/sgT+4XQ10prqYNZOdFca02G2K0yYHAIHmdANBMYlffFjaK3bfxMiEa+d4hJ7IXWPAPjDJgkzhH7ZYif2YAe4lBxg/gydPRXjABG5UfzmXCVGCC6m49ia42oDm0HhFgvhthxI48WNKENjWjdKUMfmQ1ATGfzqfWy+E6ssZZCmAmIYZTeNR+1g0QnUZmhn4ho5A8MMYi1IMT+O6ZftKAJbWhEa7bHL6MBCCKBfmKk+aPwlRWfCavMR+sla54JzKK0mcDvqsLfHmvZRx4tTP1LzTVWpvTTagMiuoidxXnmbvPqSZsJ74ixoo+lzwR+lwt/Oza0TRr5P8w1oOV8c21bJ8TPZAAiWHVZeI4Wp5ovgBeIEebv2YfFZ2KxWGsthRXOhNfFeDHKfAaNKMLI8DfjQ5tiIx9B32hAyyPm2oiDVjSjnRzIhZxaml9gANOGacoI1JoLeU68J+aI+eYbD6Yd7jMycatx/kxAJEYsCW2hvgjR75eENmstfuQj6BsNaIk2ZmhEK5rRTg7kQk4tH8FgQAfzhYNF5wxxnfkzVWf+fC0Sv5sLSkp4UxMZjlY0o73OPBdyIjdyJFdy3mAAzzYLz4XmGw2mFKPA9FtjzSPRYOVtQKQvMgLt5MAOkZzIjRzJlZybDOC5YHoMNX+O2Ocvs+Spt7lBLuTEgkmO5HqwsSbIAKbCEPNX1myx2tKf7c2N/LWCHHlrYEIHDODL6SZzd1hI2tLIF4IR5Mim6Wbjq1EG1Jp/WrJwtLWRLySaCcxych6KAXeYvzLWxTRgAeG1NMN8AXlGPFXmoBGtH5trj9tHMMvZXt+OAa/qh28sfuqzEcGcW8UJ4hDzr6yaMgVtaETrLeba2UnGGfC1eAUD2DTwyoub+u+bJ3+a+dcVW9lse+xNA9rQiFZ2gWineBL3KPxsLIgyYIX56hhnwJPiGLG76Git+6LbVKARrWg+xjyHOANYB37FABaFYgWG+0OgThk6LjfQjHZyKMwLyHktBkS7pzgD7hY72uYx8oWgGe3kEGeA7xhlwPoifwB3iu0ydFauoJ0ciuXX2G7ARhrAYrOD2EccZV7FAaoyPcU25hWeNKHFqAoxeoaYUXz66hX67pjQvuQGUGToK4aZl8leCNwmBpnX5jsntE+jc4gxKMSM4tPXRaHvbgntS24A3xEk/5j5ZmppgF0WG5FTLFlgGrQdFGK9mxefvh4Pfe+f0L7kBgwUT5sfUXFe8GegXnxo/pG1d0L7NGg7NsRamBd/eeiTvgcmtC+5AdTd+E5gv53/LYHI6JTmgIT2adB2QoiVX3xdF/qk78EJ7UtuQK152amwaotATOCL66CE9mnQdnKIlW9wVPGh79qE9iU3gFL0vIT2z5tXXtISLQZtn0uIT9/nJ7RvN8BKbECt+WdlYSElmqJ8m/dJaJ8Gj8CUECv/EYgKG/Rdm9C+5AawAH1iXsMvXARZqVkED0xon0bSIrgq9H1OQvuSG3CSeNZ8MfrNml9T1OQRx6FEr4T2afQKMVjtqe6sDfFXhD6nipMT2pfcAEZ3uHjCvJzO7RE2KhRSKLVxENEjoX0aHI+fHmIRc5k1b4Q47OAYLOktU3IDEEgJijM9NiXTjDLThFl3mVeRKFN1SWifRpcQAxP4rH1NUMKj9jc89N09oX3JDciZf3NTi+MjhV0Z9bjDzYsR21rWc/p4qkOMPcQRIf7A0FdN6DuX0L7kBpQ77QZYuwHtBrQbYO0GJBvAD8XK4rzPqbuV82lQMdCMdnKIS35DWTz/vm3hH90ndrPkd225guZdzXOIM8DvC8gAPirYX8cZQN2NTQ3Xz9Lu+JULaOxgfj7Y37xeGTf6HI0tw4ComBlnwNvmV09PNN9y5qy8TYgOR/n+QPP15jnEGcCB8BwMqDO/MRF3PD5fvBkCsRWl/r+LubvlCNr4gjzSfODQTg6FeZErN2KmY8C95kfIcRck+Oz8wbwkzSnrQ+JB8UCZgjYucaKVuwFoJ4c4A8j5HgzggIHS1Rorvhi2FaIrdH+a5zwMA/ik5NSFqbLc2v4lqWhWk3M/DNjK/PLgW+ZrAe60tZkQjTwVpXnmCyOnSk3X5DiAPMy8ukJl53trezOBXBh5ZjnrA7nyimy6KMnrgyvmrKJUdurMy1vR3eD8jVKxHWO5kK8RzX4LxHNh1eetwP8UkKtfq7fmy9KUn/hXk1rzQiQFR1ZKZgT7hCy3wzc1Ubl8tbnmeeY5kAs5nWs+28mVnM1yT8yx3KTZERWiWhwqLhL3ijoxSywWK8TvYnWZgjY0LhIzxfSc50Au5ERu5LghZxv3xVIb9/mSfCrFzqKvOEEMFcPFFeIacW2Zg8bLx7lmtJMDuZATuf0rX1u/fr3xD9RbKu0GYMCWzD+zDS/KNPfarwAAAABJRU5ErkJggg==
  27. // ==/UserScript==
  28. "use strict";
  29. unsafeWindow.addEventListener("load", () => {
  30. let response;
  31. const helper = {
  32. Uri: class Uri {
  33. constructor(href = location.href) {
  34. this._url = new URL(href);
  35. this.query = Object.fromEntries(Array.from(this._url.searchParams.entries()));
  36. this.protocol = this._url.protocol.replace(/:$/, "");
  37. this.user = this._url.username;
  38. this.password = this._url.password;
  39. this.host = this._url.hostname;
  40. this.port = this._url.port;
  41. this.path = this._url.pathname;
  42. this.fragment = this._url.hash.length > 0 ? `#${decodeURIComponent(this._url.hash).substring(1)}` : "";
  43. }
  44. _updateUrl() {
  45. this._url.protocol = `${(this.protocol || "").replace(/:$/, "")}:`;
  46. this._url.username = this.user;
  47. this._url.password = this.password;
  48. this._url.hostname = this.host;
  49. this._url.port = this.port;
  50. this._url.pathname = this.path;
  51. this._url.hash = `#${this.fragment.replace(/^#/, "")}`.replace(/^#$/, "");
  52. this._url.search = "";
  53. Object.entries(this.query).forEach(([k, v]) => {
  54. this._url.searchParams.set(k, v);
  55. });
  56. }
  57. getUserInfo() {
  58. this._updateUrl();
  59. return this.user.length > 0 ? `${this.user}:${this.password}` : "";
  60. }
  61. getHostPort() {
  62. this._updateUrl();
  63. return this._url.host;
  64. }
  65. getAuthority() {
  66. this._updateUrl();
  67. const userInfo = this.getUserInfo();
  68. const hostPort = this.getHostPort();
  69. return userInfo.length > 0 ? `${userInfo}@${hostPort}` : hostPort;
  70. }
  71. getQueryString() {
  72. this._updateUrl();
  73. return this._url.search.substring(1);
  74. }
  75. getRelativePath() {
  76. this._updateUrl();
  77. return this.path + this._url.search + this.fragment;
  78. }
  79. toString() {
  80. this._updateUrl();
  81. return this._url.href;
  82. }
  83. },
  84. hsts: function (link) {
  85. try {
  86. let url = link?.toString?.();
  87. if (url.startsWith("//")) {
  88. url = `https:${url}`;
  89. }
  90. else if (url.startsWith("http:")) {
  91. url = url.replace(/^http:/, "https:");
  92. }
  93. return url;
  94. } catch {
  95. return link;
  96. }
  97. },
  98. coverImage: function (url) {
  99. /* 本函数来自 https://greasyfork.org/zh-CN/scripts/30714-获取哔哩哔哩视频的封面图片-get-bilibili-cover-image/code?version=202372 特此感谢*/
  100. let coverImageBigUrl = url;
  101. // 去除url中的裁剪标识
  102. if (url.indexOf("@") > -1) {
  103. //处理以@做裁剪标识的url
  104. coverImageBigUrl = url.split("@")[0];
  105. }
  106. if (url.indexOf("jpg_") > -1) {
  107. //处理以_做裁剪标识的url
  108. coverImageBigUrl = `${url.split("jpg_")[0]}jpg`;
  109. }
  110. if (url.indexOf("png_") > -1) {
  111. //处理以_做裁剪标识的url
  112. coverImageBigUrl = `${url.split("png_")[0]}png`;
  113. }
  114. if (url.indexOf("/320_200/") > -1) {
  115. //有时裁剪标识是在后缀名之前的 目前主要发现的是“番剧”板块的列表里有,但尚不清楚其他地方的情况
  116. coverImageBigUrl = url.replace("/320_200", "");
  117. }
  118. return coverImageBigUrl;
  119. },
  120. window: undefined,
  121. openWin: function (win, src) {
  122. if (this.window) { this.setImg(src); }
  123. else {
  124. const doc = win.document;
  125. const w = win.outerWidth || doc.docElement.clientWidth || doc.body.clientWidth,
  126. h = win.outerHeight || doc.docElement.clientHeight || doc.body.clientHeight;
  127. this.window = window.open("about:blank", "bilibiliCover", `location=1,scrollbars=1,channelmode=1,width=${w * 0.8},height=${h * 0.8},left=${w * 0.1},top=${h * 0.1}`);
  128. setTimeout(() => {
  129. this.window.document.title = "BilibiliCover - 封面获取窗口";
  130. this.window.document.body.innerHTML = `<div style="text-align: start;">视频封面地址:</div><textarea readonly="readonly" style="width: 100%; height: 1.1em; font-size: 24px; box-sizing: content-box; overflow: hidden; background-color: white; color: initial;"></textarea><hr><img src="${src}" style="max-width: 100%; height: auto; min-height: 300px;"><p id="realsize" style="text-align: center;"></p>`;
  131. this.window.document.body.innerHTML += "<style> a { cursor: pointer; background-position: center right;background-repeat: no-repeat;background-image: -webkit-linear-gradient(transparent, transparent), url(data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%2210%22%3E%3Cg%20transform%3D%22translate%28-826.429%20-698.791%29%22%3E%3Crect%20width%3D%225.982%22%20height%3D%225.982%22%20x%3D%22826.929%22%20y%3D%22702.309%22%20fill%3D%22%23fff%22%20stroke%3D%22%2306c%22%2F%3E%3Cg%3E%3Cpath%20d%3D%22M831.194%20698.791h5.234v5.391l-1.571%201.545-1.31-1.31-2.725%202.725-2.689-2.689%202.808-2.808-1.311-1.311z%22%20fill%3D%22%2306f%22%2F%3E%3Cpath%20d%3D%22M835.424%20699.795l.022%204.885-1.817-1.817-2.881%202.881-1.228-1.228%202.881-2.881-1.851-1.851z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E); background-image: linear-gradient(transparent, transparent), url(data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2210%22%20height%3D%2210%22%3E%3Cg%20transform%3D%22translate%28-826.429%20-698.791%29%22%3E%3Crect%20width%3D%225.982%22%20height%3D%225.982%22%20x%3D%22826.929%22%20y%3D%22702.309%22%20fill%3D%22%23fff%22%20stroke%3D%22%2306c%22%2F%3E%3Cg%3E%3Cpath%20d%3D%22M831.194%20698.791h5.234v5.391l-1.571%201.545-1.31-1.31-2.725%202.725-2.689-2.689%202.808-2.808-1.311-1.311z%22%20fill%3D%22%2306f%22%2F%3E%3Cpath%20d%3D%22M835.424%20699.795l.022%204.885-1.817-1.817-2.881%202.881-1.228-1.228%202.881-2.881-1.851-1.851z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E); padding-right: 13px; } </style>";
  132. this.window.document.body.innerHTML += '<p style="display: flex; flex-wrap: nowrap; justify-content: space-around;"><a target="_blank" id="baidu">Baidu识图搜索</a><a target="_blank" id="google">Google识图搜索</a></p>';
  133. this.window.document.body.style.textAlign = "center";
  134. this.setImg(src);
  135. const t = this.window.document.querySelector("textarea");
  136. t.addEventListener("mouseup", (e) => {
  137. if (e.which !== 1) { return; }
  138. const selection = this.window.getSelection();
  139. if (selection.toString() !== "") { return; }
  140. this.focus();
  141. this.select();
  142. });
  143. const gLink = this.window.document.querySelector("#google");
  144. gLink.addEventListener("mousedown", () => {
  145. gLink.href = `https://www.google.com/searchbyimage?encoded_image=&image_content=&filename=&hl=zh-CN&image_url=${encodeURIComponent(t.value)}`;
  146. }, {
  147. capture: true,
  148. });
  149. const bLink = this.window.document.querySelector("#baidu");
  150. bLink.addEventListener("click", (e) => {
  151. const s = getComputedStyle(bLink);
  152. if (s.fontWeight === "700") {
  153. return;
  154. }
  155. if (s.cursor === "not-allowed") {
  156. e.preventDefault();
  157. e.stopImmediatePropagation();
  158. e.stopPropagation();
  159. return;
  160. }
  161. bLink.style.cursor = "not-allowed";
  162. bLink.style.fontStyle = "italic";
  163. bLink.innerText = "正在预搜索中,不要离开,请稍候……";
  164. GM_xmlhttpRequest({
  165. url: `https://graph.baidu.com/upload?tn=pc&from=pc&image_source=PC_UPLOAD_IMAGE_ICONURL&image=${encodeURIComponent(t.value)}`,
  166. method: "POST",
  167. headers: {
  168. referer: "https://image.baidu.com/",
  169. origin: "https://image.baidu.com",
  170. },
  171. onerror: (detail) => {
  172. this.window.alert(`百度识图搜索失败:网络故障\n${detail}`);
  173. bLink.innerText = "Baidu识图搜索";
  174. },
  175. onload: (res) => {
  176. if (getComputedStyle(bLink).cursor !== "not-allowed") {
  177. return;
  178. }
  179. let response;
  180. try {
  181. response = JSON.parse(res.responseText);
  182. console.info("BilibiliCover", "BaiduNetworkResponse", res, JSON.parse(res.response));
  183. } catch (e) {
  184. response = false;
  185. console.error("BilibiliCover", "BaiduNetworkResponse", res, e);
  186. }
  187. if (!response) {
  188. this.window.alert(`百度识图搜索失败:返回数据无法识别\n${res.responseText}`);
  189. bLink.innerText = "Baidu识图搜索";
  190. return;
  191. }
  192. if (!response.data || !response.data.url) {
  193. this.window.alert(`百度识图搜索失败:返回数据格式错误\n${res.responseText}`);
  194. bLink.innerText = "Baidu识图搜索";
  195. return;
  196. }
  197. bLink.href = response.data.url;
  198. bLink.style.cursor = "pointer";
  199. bLink.style.fontStyle = "normal";
  200. bLink.style.fontWeight = "700";
  201. bLink.innerText = "预搜索完成,请再次点击此处以使用Baidu识图搜索!";
  202. },
  203. });
  204. }, {
  205. capture: true,
  206. });
  207. this.window.addEventListener("beforeunload", () => {
  208. this.window = undefined;
  209. });
  210. this.window.focus();
  211. this.window.addEventListener("load", () => {
  212. this.resize();
  213. });
  214. this.window.document.querySelector("img").addEventListener("load", () => {
  215. this.resize();
  216. });
  217. this.window.addEventListener("resize", () => {
  218. this.resize();
  219. });
  220. this.window.setInterval(() => {
  221. this.resize();
  222. }, 500);
  223. this.resize();
  224. }, 0);
  225. }
  226. },
  227. resize: function () {
  228. if (!this.window) { return; }
  229. const collection = this.window.document.querySelectorAll("body > *:not(img):not(style)");
  230. let totalHeight = 0;
  231. Array.from(collection).forEach((t) => {
  232. totalHeight += this.getRealHeight(t);
  233. });
  234. this.window.document.querySelector("body > img").style.maxHeight = `calc(100vh - ${totalHeight}px)`;
  235. },
  236. getRealHeight: function (ele) {
  237. const style = (this.window || window).getComputedStyle(ele);
  238. let realHeight = 0;
  239. ["marginTop", "paddingTop", "height", "paddingBottom", "marginBottom", "borderTopWith", "borderBottomWith"].forEach((p) => {
  240. const v = style[p];
  241. if (/^\d/.test(v)) { realHeight += parseFloat(v); }
  242. });
  243. return realHeight;
  244. },
  245. setImg: function (src) {
  246. if (!this.window) { return; }
  247. const img = this.window.document.querySelector("img");
  248. this.window.document.querySelector("img").src = src;
  249. this.window.document.querySelector("textarea").value = src;
  250. const bLink = this.window.document.querySelector("#baidu");
  251. bLink.style.cursor = "pointer";
  252. bLink.style.fontStyle = "normal";
  253. bLink.style.fontWeight = "400";
  254. bLink.innerText = "Baidu识图搜索";
  255. this.setNaturalSize(img, this.window.document.querySelector("#realsize"));
  256. this.window.focus();
  257. },
  258. setNaturalSize: function (img, node) {
  259. if (img.naturalWidth > 0 && img.naturalHeight > 0) { node.innerText = `(${img.naturalWidth${img.naturalHeight})`; }
  260. else {
  261. setTimeout(() => {
  262. this.setNaturalSize(img, node);
  263. }, 100);
  264. }
  265. },
  266. closeWin: function () {
  267. if (this.window) {
  268. this.window.close();
  269. }
  270. },
  271. };
  272. const url = new helper.Uri();
  273. const doc = unsafeWindow.document;
  274. const match = (str) => (str.match(/(?:\/video\/|\/watchlater\/(?:#\/)?)(av\d+|[bB][vV]1[fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF]{9})/) || [0, -1])[1];
  275. if (unsafeWindow.location.host.includes("www.bilibili.com")) {
  276. const wait = () => {
  277. if (!doc.querySelector(".bilibili-player-video-btn-quality > .bilibili-player-video-quality-menu.bui-select-quality-menu")) {
  278. return true;
  279. }
  280. const v = doc.getElementsByTagName("video");
  281. if (v.length === 0) {
  282. return true;
  283. }
  284. const vb = doc.getElementsByTagName("video")[0].buffered;
  285. if (!(vb instanceof TimeRanges)) {
  286. return true;
  287. }
  288. try {
  289. const vbe = vb.end(vb.length - 1);
  290. if (typeof vbe !== "number" || vbe < 2) {
  291. return true;
  292. }
  293. } catch {
  294. return true;
  295. }
  296. return false;
  297. };
  298. const c = setInterval(() => {
  299. if (wait()) {
  300. return;
  301. }
  302. const plw = doc.querySelector("#arc_toolbar_report > .ops, #toolbar_module, .video-toolbar-module, .play-options-ul");
  303. if (!plw) {
  304. return;
  305. }
  306. const coin = plw.querySelector(".coin, .coin-info span, .coin-box .num, :scope > li:nth-child(2) > span");
  307. if (!/\d+/.test(coin?.innerText?.trim?.() ?? "--")) {
  308. return;
  309. }
  310. clearInterval(c);
  311. let IS_ORIGIN_VIDEO = true;
  312. let IS_NEW_BANGUMI = false;
  313. const IS_WATCHLATER = url.toString().includes("/watchlater/#/");
  314. const IS_NEW_WATCHLATER = url.toString().includes("/watchlater/");
  315. let aid_bvid = match(url.toString());
  316. if (aid_bvid === -1) {
  317. if (!IS_WATCHLATER && !IS_NEW_WATCHLATER && (url.path.match(/\/bangumi\/play\/(?:ep|ss)(\d+)/) || [0, -1])[1] !== -1) {
  318. IS_NEW_BANGUMI = true;
  319. }
  320. IS_ORIGIN_VIDEO = false;
  321. }
  322. const style = doc.querySelector("#bilibiliCoverStyle") || doc.createElement("style");
  323. style.innerText = '.bilibiliCoverButtonInWatcherLater { background: rgba(0, 0, 0, 0) url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'22\' height=\'22\' style=\'\'%3E%3Crect id=\'backgroundrect\' width=\'100%25\' height=\'100%25\' x=\'0\' y=\'0\' fill=\'none\' stroke=\'none\'/%3E%3Cg class=\'currentLayer\' style=\'\'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath d=\'M17.982 9.275L8.06 3.27A2.013 2.013 0 005 4.994v12.011a2.017 2.017 0 003.06 1.725l9.922-6.005a2.017 2.017 0 000-3.45z\' id=\'svg_1\' class=\'selected\' fill=\'%23757575\' fill-opacity=\'1\'/%3E%3C/g%3E%3C/svg%3E") no-repeat scroll 50% 50% / 100% padding-box border-box };';
  324. style.id = "bilibiliCoverStyle";
  325. if (style.parentElement !== doc.body) { doc.body.appendChild(style); }
  326. const img = doc.createElement("img");
  327. img.addEventListener("error", (...args) => {
  328. args.unshift("BilibiliCover");
  329. args.unshift("NetworkError");
  330. console.error(...args);
  331. const s = new helper.Uri(img.src);
  332. s.query.t = new Date().getTime();
  333. img.src = s;
  334. });
  335. try {
  336. const meta = doc.querySelector('meta[property="og:image"], meta[itemprop="image"], meta[itemprop="thumbnailUrl"]');
  337. const metacontent = meta.getAttribute("content");
  338. const metacontentUri = new helper.Uri(metacontent);
  339. if (metacontentUri.host.endsWith("hdslb.com") && metacontentUri.path.startsWith("/bfs/")) {
  340. img.src = helper.hsts(metacontent);
  341. }
  342. } catch { }
  343. const detectType = (ifIsOriginVideo, ifIsNewBangumi, ifIsWatchLater, ifIsNewWatchLater) => {
  344. switch (true) {
  345. case IS_WATCHLATER: return ifIsWatchLater;
  346. case IS_NEW_WATCHLATER: return ifIsNewWatchLater;
  347. case IS_ORIGIN_VIDEO: return ifIsOriginVideo;
  348. case IS_NEW_BANGUMI: return ifIsNewBangumi;
  349. }
  350. };
  351. setInterval(() => {
  352. if (doc.querySelector("#BilibiliCoverButton")) {
  353. return;
  354. }
  355. if (wait()) {
  356. return;
  357. }
  358. const button = doc.createElement(detectType("span", "div", "div", "li"));
  359. button.id = "BilibiliCoverButton";
  360. button.title = "获取封面";
  361. button.innerHTML = detectType("<i class=\"van-icon-info_playnumber\"></i>获取封面", "<i class=\"iconfont icon-views\" style=\"display: inline-block; vertical-align: top; width: 28px; height: 28px; line-height: 28px; font-size: 28px; color: #757575; margin-right: 4px; text-align: center;\"></i><span style=\"display: inline-block; vertical-align: top; width: 62px; height: 28px; line-height: 28px; font-size: 14px; color: #505050;\">获取封面</span>", "<div class=\"btn-item\"><i class=\"icon-move s-icon-moved bilibiliCoverButtonInWatcherLater\" style=\"background-size: 50px;\"></i><span class=\"t\">获取封面</span><span class=\"num\">鼠标悬浮查看</span></div>", '<i class="bilibiliCoverButtonInWatcherLater" style="width: 22px; height: 22px;display: inline-block; vertical-align: middle;"></i> <span>获取封面</span></li>');
  362. if (IS_NEW_BANGUMI) {
  363. button.setAttribute("style", "display: block; float: left; height: 36px; margin-left: 12px; cursor: pointer;");
  364. }
  365. if (IS_WATCHLATER) {
  366. button.setAttribute("style", "position: relative; float: left;");
  367. }
  368. if (IS_NEW_WATCHLATER) {
  369. button.setAttribute(coin.getAttributeNames().filter((n) => n.startsWith("data-v-"))[0], "");
  370. button.querySelectorAll("*").forEach((ele) => {
  371. ele.setAttribute(coin.getAttributeNames().filter((n) => n.startsWith("data-v-"))[0], "");
  372. });
  373. }
  374. let code = null;
  375. button.addEventListener("click", () => {
  376. const src = img.src;
  377. if (src) {
  378. helper.openWin(window, src, 1);
  379. }
  380. });
  381. button.addEventListener("mouseover", () => {
  382. if (code) {
  383. code = null;
  384. clearTimeout(code);
  385. }
  386. img.style.display = "block";
  387. let X = 0,
  388. Y = 0,
  389. W = 0;
  390. let p = button;
  391. do {
  392. X += p.offsetTop;
  393. Y += p.offsetLeft;
  394. p = p.offsetParent;
  395. } while (p !== doc.body);
  396. const style = unsafeWindow.getComputedStyle(button);
  397. X += parseInt(style.marginTop) + parseInt(style.height) + parseInt(style.marginBottom) + 10;
  398. Y += parseInt(style.marginLeft) + parseInt(style.width) + parseInt(style.marginRight) + 10;
  399. W = unsafeWindow.innerWidth - Y - 150;
  400. if (W > img.naturalWidth) { W = img.naturalWidth; }
  401. img.style.top = `${X - W * img.naturalHeight / img.naturalWidth - 10}px`;
  402. img.style.left = `${Y}px`;
  403. img.style.width = `${W}px`;
  404. img.style.opacity = "1";
  405. });
  406. button.addEventListener("mouseleave", () => {
  407. if (code) {
  408. code = null;
  409. clearTimeout(code);
  410. }
  411. img.style.opacity = "0";
  412. code = setTimeout(() => {
  413. img.style.top = "-99999px";
  414. code = null;
  415. }, 130);
  416. });
  417. if (plw.querySelector(".more")) {
  418. plw.insertBefore(button, plw.querySelector(".more"));
  419. }
  420. else {
  421. plw.appendChild(button);
  422. }
  423. }, 500);
  424. style.innerText += ".video-toolbar .ops .app { margin-right: 12px; }";
  425. img.id = "cover_img";
  426. doc.body.appendChild(img);
  427. img.style.position = "absolute";
  428. img.style.top = "-99999px";
  429. img.style.left = "0";
  430. img.style.zIndex = "99999";
  431. img.style.border = "1px black solid";
  432. img.style.opacity = "0";
  433. img.style.transition = "opacity .13s linear";
  434. img.style.display = "none";
  435. let running = false;
  436. const err = (...args) => {
  437. args.unshift("BilibiliCover");
  438. args.unshift("NetworkError:");
  439. console.error(...args);
  440. img.alt = args.map((e) => { return JSON.stringify(e); }).join("\n");
  441. running = false;
  442. };
  443. setInterval(() => {
  444. if (!running && IS_ORIGIN_VIDEO || IS_WATCHLATER) {
  445. const aidDetected = match(new helper.Uri().toString());
  446. if (aidDetected !== -1 && aidDetected !== aid_bvid || !img.src) {
  447. aid_bvid = aidDetected;
  448. running = true;
  449. GM_xmlhttpRequest({
  450. url: `https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword=${aid_bvid}`,
  451. method: "GET",
  452. onerror: err,
  453. onload: (res) => {
  454. try {
  455. response = JSON.parse(res.responseText);
  456. } catch (e) {
  457. response = false;
  458. }
  459. console.info("BilibiliCover", "NetworkResponse:", res, ", parse result:", response);
  460. if (!response) {
  461. err("Unable to parse response");
  462. return;
  463. }
  464. const data = response.data.result;
  465. if (!Array.isArray(data)) {
  466. err(`Backend returns incompatible data(${typeof data})`);
  467. return;
  468. }
  469. let cover;
  470. data.forEach((info) => {
  471. if ((`av${info.aid}` === aid_bvid || info.bvid === aid_bvid) && info.pic) {
  472. cover = helper.hsts(helper.coverImage(info.pic));
  473. }
  474. });
  475. if (cover) {
  476. img.src = helper.hsts(cover);
  477. } else {
  478. err("Unable to get the cover picture url");
  479. }
  480. running = false;
  481. },
  482. });
  483. }
  484. } else {
  485. try {
  486. img.src = helper.hsts(helper.coverImage(doc.querySelector("#bofqi .bilibili-player-watchlater-item[data-state-play=true] .bilibili-player-watchlater-cover-cell img, .bangumi-info-wrapper .info-cover img, #media_module > a > img, .player-auxiliary-playlist-item-active .player-auxiliary-playlist-item-img-self").src));
  487. // clearInterval(loop_code);
  488. img.removeAttribute("alt");
  489. } catch (_) {
  490. console.info("bilibiliCover:", "no img");
  491. img.alt = "bilibiliCover: no img";
  492. }
  493. }
  494. }, 100);
  495. }, 500);
  496. }
  497. else if (unsafeWindow.location.host.includes("live.bilibili.com")) {
  498. const link = unsafeWindow.document.createElement("div");
  499. const error = (...args) => {
  500. const detail = args[0];
  501. link.innerHTML = `<span style="color: red">封面获取失败=。=(${detail})</span>`;
  502. args.unshift("BilibiliCover");
  503. args.unshift("NetworkError");
  504. console.error(...args);
  505. };
  506. (function loop() {
  507. const roomid = unsafeWindow.location.pathname.match(/^\/(?:blanc\/)?(\d+)/)[1];
  508. // const userName = (((unsafeWindow.document.querySelector(".room-owner-username") || {}).href || "").match(/\d+/) || [])[0];
  509. if (roomid) {
  510. const container = unsafeWindow.document.querySelector(".seeds-wrap");
  511. if (!container) { return setTimeout(loop, 100); }
  512. GM_xmlhttpRequest({
  513. url: `https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=${roomid}`,
  514. method: "GET",
  515. onerror: error,
  516. onload: (res) => {
  517. link.style.display = "inline-block";
  518. link.style.marginLeft = link.style.marginRight = "1em";
  519. link.style.verticalAlign = "bottom";
  520. link.innerHTML = '<a href="javascript:void(0);" style="color: #23ade5;">查看封面</a>';
  521. container.insertBefore(link, container.firstChild);
  522. console.info("BilibiliCover", "NetworkResponse", res, JSON.parse(res.response));
  523. try {
  524. response = JSON.parse(res.responseText);
  525. } catch (e) {
  526. response = false;
  527. }
  528. if (!response) { error("无法解析后端返回数据", res); }
  529. const cover = response.data?.room_info?.cover;
  530. if (!cover) {
  531. error("解析出现错误", response);
  532. } else {
  533. link.querySelector("a").addEventListener("click", () => {
  534. helper.openWin(unsafeWindow, cover);
  535. });
  536. }
  537. },
  538. });
  539. }
  540. })();
  541. }
  542. window.addEventListener("beforeunload", () => {
  543. helper.closeWin();
  544. });
  545. });