查找图片

查找图片哦

  1. // ==UserScript==
  2. // @name 查找图片
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description 查找图片哦
  6. // @author Chengguan
  7. // @match https://*.huaban.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=huaban.com
  9. // @grant GM_registerMenuCommand
  10. // @run-at document-body
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function () {
  15. 'use strict';
  16.  
  17. const styleEle = document.createElement('style');
  18. styleEle.innerText = `
  19. .hb-img {outline: #F007 dashed 5px; outline-offset: -5px;}
  20.  
  21. .hb-imgTip { font-size: 11px; position: absolute; z-index: 999999; opacity: 0.8; color: #F00; background-color: #FFF; word-break: break-all; padding: 4px;}
  22. .hb-imgTip:hover { z-index: 9999999999; opacity: 1;}
  23.  
  24. .hb-container { position: fixed; top: 50px; left: 50px; width: 320px; box-shadow: 0px 0px 20px 4px #686868; background-color: #FFF; z-index: 99999999999; padding: 10px; resize: both; overflow: auto; text-align: center; border: 1px solid #CCC; border-radius: 5px; user-select: none; }
  25.  
  26. .hb-container h1 { font-size: 22px; cursor: move; background-color: #efefef }
  27. .hb-container h1:hover { background-color: #d9d9d9 }
  28.  
  29. .hb-container h2 { font-size: 18px; }
  30.  
  31. .hb-container label { padding: 8px; display: block; text-align: left;}
  32. .hb-container label:hover { background-color: #efefef}
  33.  
  34. .hb-container label select { margin-left: 5px; padding: 3px; }
  35. `;
  36. document.head.appendChild(styleEle);
  37.  
  38. let allTips = [];
  39. let allImages = [];
  40. let allFileSizePromises = [];
  41.  
  42. function mark(
  43. reg = /./,
  44. {
  45. reverseSelect = false,
  46. showImageSrc = false,
  47. showImageSize = false,
  48. domainReplace = '',
  49. urlReplaceInput = false,
  50. urlReplaceFrom = '',
  51. urlReplaceTo = '',
  52. } = {},
  53. ) {
  54. allTips.forEach((tip) => tip.remove());
  55. allImages.forEach((img) => img.classList.remove('hb-img'));
  56.  
  57. allTips = [];
  58. allImages = [];
  59. allFileSizePromises = [];
  60.  
  61. [...document.querySelectorAll('img')].forEach((img) => {
  62. console.count('Image');
  63. const src =
  64. (img.dataset.bakSrc || '') + (img.dataset.bakSrcset || '') ||
  65. img.currentSrc;
  66.  
  67. let testValue = reg.test(src);
  68. testValue = reverseSelect ? !testValue : testValue;
  69.  
  70. if (testValue) {
  71. img.classList.add('hb-img');
  72.  
  73. // 替换域名 或 替换文件地址
  74. if (domainReplace || urlReplaceInput) {
  75. const urlObj = new URL(src);
  76. const oldHost = urlObj.host;
  77.  
  78. // 备份
  79. if (!img.getAttribute('data-bak-src')) {
  80. img.src && img.setAttribute('data-bak-src', img.src);
  81. img.srcset && img.setAttribute('data-bak-srcset', img.srcset);
  82. }
  83.  
  84. let newSrc = img.getAttribute('data-bak-src') || img.src;
  85. let newSrcset = img.getAttribute('data-bak-srcset') || img.srcset;
  86.  
  87. // 替换
  88. if (domainReplace) {
  89. newSrc = newSrc.replace(oldHost, domainReplace);
  90. newSrcset = newSrcset.replace(oldHost, domainReplace);
  91. }
  92.  
  93. if (urlReplaceInput) {
  94. newSrc = newSrc.replace(urlReplaceFrom, urlReplaceTo);
  95. newSrcset = newSrcset.replace(urlReplaceFrom, urlReplaceTo);
  96. }
  97.  
  98. const srcAddRandom = (src) => {
  99. const random = Math.random()
  100. .toString(36)
  101. .substring(2, 15)
  102. .replace('.', '');
  103. const srcParts = src.split(' ');
  104. srcParts[0] =
  105. srcParts[0] + (srcParts[0].includes('?') ? '&' : '?') + random;
  106.  
  107. return srcParts.join(' ');
  108. };
  109.  
  110. newSrc && img.setAttribute('src', srcAddRandom(newSrc));
  111. newSrcset && img.setAttribute('srcset', srcAddRandom(newSrcset));
  112. } else if (img.getAttribute('data-bak-src')) {
  113. // 恢复
  114. img.dataset.bakSrc && (img.src = img.getAttribute('data-bak-src'));
  115. img.dataset.bakSrcset &&
  116. (img.srcset = img.getAttribute('data-bak-srcset'));
  117.  
  118. // // 清除
  119. // img.removeAttribute('data-bak-src');
  120. // img.removeAttribute('data-bak-srcset');
  121. }
  122.  
  123. function imageCompleted() {
  124. if (showImageSrc || showImageSize) {
  125. const { tip, fileSize } = createImgTip(img, {
  126. showImageSrc,
  127. showImageSize,
  128. });
  129. document.scrollingElement.appendChild(tip);
  130. allTips.push(tip);
  131. allFileSizePromises.push(fileSize);
  132. }
  133.  
  134. img.removeEventListener('onload', imageCompleted);
  135. }
  136.  
  137. imageCompleted();
  138.  
  139. allImages.push(img);
  140. }
  141. });
  142.  
  143. return allImages;
  144. }
  145.  
  146. function createImgTip(img, { showImageSrc, showImageSize }) {
  147. let tip = document.createElement('p');
  148. tip.className = 'hb-imgTip';
  149. tip.style.top =
  150. img.getBoundingClientRect().top +
  151. document.scrollingElement.scrollTop +
  152. 'px';
  153. tip.style.left =
  154. img.getBoundingClientRect().left +
  155. document.scrollingElement.scrollLeft +
  156. 'px';
  157. tip.style.maxWidth =
  158. Math.max(img.getBoundingClientRect().width, 200) + 'px';
  159.  
  160. let fileSize;
  161.  
  162. if (showImageSrc) {
  163. tip.innerText = img.currentSrc.startsWith('data:image')
  164. ? 'data:image/ xxxx'
  165. : img.currentSrc + '\n';
  166. }
  167.  
  168. if (showImageSize) {
  169. let currentSrc = img.currentSrc.startsWith('data:image')
  170. ? ''
  171. : img.currentSrc;
  172.  
  173. if (currentSrc) {
  174. fileSize = getImageSize(currentSrc).then((result) => {
  175. let size = (result / 1024).toFixed(2);
  176. tip.innerText += ' 尺寸:' + size + 'KB';
  177. return size;
  178. });
  179. }
  180. }
  181. return { tip, fileSize };
  182. }
  183.  
  184. // 拖拽元素
  185. function dragableElement({ element, trigger }) {
  186. trigger = trigger || element;
  187.  
  188. let sx = 0;
  189. let sy = 0;
  190.  
  191. trigger.addEventListener('mousedown', (e) => {
  192. let x = parseInt(window.getComputedStyle(element).left, 10);
  193. let y = parseInt(window.getComputedStyle(element).top, 10);
  194.  
  195. sx = e.clientX;
  196. sy = e.clientY;
  197.  
  198. const moveHandler = (e) => {
  199. console.info(e.clientX, e.clientY);
  200. console.info(e.clientX - sx, e.clientY - sy);
  201.  
  202. element.style.left = x + (e.clientX - sx) + 'px';
  203. element.style.top = y + (e.clientY - sy) + 'px';
  204. };
  205.  
  206. const upHandler = () => {
  207. document.body.removeEventListener('mousemove', moveHandler);
  208. document.body.removeEventListener('mouseup', upHandler);
  209. };
  210.  
  211. document.body.addEventListener('mousemove', moveHandler);
  212. document.body.addEventListener('mouseup', upHandler);
  213. });
  214. }
  215.  
  216. // 获取文件大小
  217. function getImageSize(url) {
  218. return fetch(url)
  219. .then((response) => {
  220. if (!response.ok) {
  221. throw new Error('Network response was not ok');
  222. }
  223. return response.headers.get('content-length');
  224. })
  225. .then((contentLength) => {
  226. // console.log('远程图片大小为: ' + contentLength + ' 字节');
  227. return contentLength;
  228. })
  229. .catch((error) => {
  230. console.error(
  231. 'There has been a problem with your fetch operation:',
  232. error,
  233. );
  234. });
  235. }
  236.  
  237. GM_registerMenuCommand(
  238. '查找图片',
  239. () => {
  240. // 容器
  241. const container = document.createElement('div');
  242. container.className = 'hb-container';
  243. document.body.appendChild(container);
  244.  
  245. // 标题
  246. const titleEle = document.createElement('h1');
  247. titleEle.innerText = '查找图片';
  248. container.appendChild(titleEle);
  249.  
  250. dragableElement({ element: container, trigger: titleEle });
  251.  
  252. // 匹配图片数量
  253. const matchCount = document.createElement('h2');
  254. container.appendChild(matchCount);
  255.  
  256. // 匹配正则
  257. const matchRegLabel = document.createElement('label');
  258. matchRegLabel.innerText = '匹配图片:';
  259. container.appendChild(matchRegLabel);
  260.  
  261. const input = document.createElement('input');
  262. matchRegLabel.appendChild(input);
  263.  
  264. // 显示图片地址
  265. const showSrcLabel = document.createElement('label');
  266. showSrcLabel.innerText = ' 显示图片地址 ';
  267. container.appendChild(showSrcLabel);
  268.  
  269. const showSrcInput = document.createElement('input');
  270. showSrcInput.setAttribute('type', 'checkbox');
  271. showSrcLabel.prepend(showSrcInput);
  272.  
  273. // 显示图片文件大小
  274. const showFileSizeLabel = document.createElement('label');
  275. showFileSizeLabel.innerText = ' 显示图片大小';
  276. container.appendChild(showFileSizeLabel);
  277.  
  278. const showFileSizeInput = document.createElement('input');
  279. showFileSizeInput.setAttribute('type', 'checkbox');
  280. // showFileSizeInput.setAttribute('checked', true);
  281. showFileSizeLabel.prepend(showFileSizeInput);
  282.  
  283. // 反选图片
  284. const reverseLabel = document.createElement('label');
  285. reverseLabel.innerText = ' 反选';
  286. container.appendChild(reverseLabel);
  287.  
  288. const reverseSelect = document.createElement('input');
  289. reverseSelect.setAttribute('type', 'checkbox');
  290. reverseLabel.prepend(reverseSelect);
  291.  
  292. // 域名替换
  293. const domainReplaceLabel = document.createElement('label');
  294. domainReplaceLabel.innerText = ' 域名替换';
  295. container.appendChild(domainReplaceLabel);
  296.  
  297. const domainReplaceInput = document.createElement('input');
  298. domainReplaceInput.setAttribute('type', 'checkbox');
  299. domainReplaceLabel.prepend(domainReplaceInput);
  300.  
  301. const domainReplaceSelect = document.createElement('select');
  302. domainReplaceSelect.innerHTML = `
  303. <option value=''>默认</option>
  304. <option value='gd-hbimg.huaban.com'>gd-hbimg.huaban.com</option>
  305. <option value='gd-hbimg.huabanimg.com'>gd-hbimg.huabanimg.com</option>
  306. `;
  307. domainReplaceLabel.appendChild(domainReplaceSelect);
  308.  
  309. // 图片URL替换
  310. const urlReplaceLabel = document.createElement('label');
  311. urlReplaceLabel.innerText = ' 图片URL替换 ';
  312. container.appendChild(urlReplaceLabel);
  313.  
  314. const urlReplaceInput = document.createElement('input');
  315. urlReplaceInput.setAttribute('type', 'checkbox');
  316. urlReplaceLabel.prepend(urlReplaceInput);
  317.  
  318. const urlReplaceFromInput = document.createElement('input');
  319. urlReplaceLabel.appendChild(urlReplaceFromInput);
  320. const urlReplaceToInput = document.createElement('input');
  321. urlReplaceToInput.style.width = '100%';
  322. urlReplaceLabel.appendChild(urlReplaceToInput);
  323.  
  324. function render() {
  325. try {
  326. const reg = new RegExp(input.value);
  327. mark(reg, {
  328. reverseSelect: reverseSelect.checked,
  329. showImageSrc: showSrcInput.checked,
  330. showImageSize: showFileSizeInput.checked,
  331. domainReplace: domainReplaceInput.checked
  332. ? domainReplaceSelect.value
  333. : '',
  334. urlReplaceInput: urlReplaceInput.checked,
  335. urlReplaceFrom: urlReplaceFromInput.value,
  336. urlReplaceTo: urlReplaceToInput.value,
  337. });
  338. matchCount.innerText = `匹配文件数:${allImages.length}`;
  339.  
  340. Promise.all(allFileSizePromises).then((allSizes) => {
  341. const result = allSizes.reduce((total, size) => {
  342. return total + Number(size);
  343. }, 0);
  344. matchCount.innerText += `,总大小:${result.toFixed(2)}KB`;
  345. });
  346. } catch (e) {
  347. // nothing;
  348. }
  349. }
  350.  
  351. input.addEventListener('input', render);
  352. container.addEventListener('click', (e) => {
  353. if (e.target === container) {
  354. render();
  355. }
  356. });
  357. showSrcInput.addEventListener('change', render);
  358. showFileSizeInput.addEventListener('change', render);
  359. reverseSelect.addEventListener('change', render);
  360.  
  361. domainReplaceInput.addEventListener('change', render);
  362. domainReplaceSelect.addEventListener('change', render);
  363.  
  364. urlReplaceInput.addEventListener('change', render);
  365.  
  366. render();
  367. },
  368. 's',
  369. );
  370. })();

QingJ © 2025

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