下载原始图片

一个帮你从网站下载原始尺寸图片的工具

  1. // ==UserScript==
  2. // @name Download Original Picture
  3. // @name:zh-CN 下载原始图片
  4. // @description A tool to help you download full size images from websites
  5. // @description:zh-CN 一个帮你从网站下载原始尺寸图片的工具
  6. // @namespace https://hx.fyi/
  7. // @version 0.1.5
  8. // @license GPL-3.0
  9. // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTA4IDUwOCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+IDxjaXJjbGUgc3R5bGU9ImZpbGw6I0ZGRDA1QjsiIGN4PSIyNTQiIGN5PSIyNTQiIHI9IjI1NCIvPiA8cGF0aCBzdHlsZT0iZmlsbDojRkZGRkZGOyIgZD0iTTM3Mi44LDE5NkgzNjhjLTIuNC00MC40LTM1LjYtNzIuNC03Ni40LTcyLjRjLTQsMC04LDAuNC0xMS42LDAuOGMtMTYtMjguNC00Ni00Ny42LTgwLjgtNDcuNiBjLTUxLjIsMC05Mi40LDQxLjYtOTIuNCw5Mi40YzAsMTAuOCwyLDIxLjIsNS4yLDMwLjhjLTI1LjIsMTAtNDIuOCwzNC00Mi44LDYyLjRjMCwzNi40LDI5LjYsNjYuNCw2Ni40LDY2LjRoMjM3LjIgYzM2LjQsMCw2Ni40LTI5LjYsNjYuNC02Ni40QzQzOC44LDIyNS42LDQwOS4yLDE5NiwzNzIuOCwxOTZ6Ii8+IDxwYXRoIHN0eWxlPSJmaWxsOiNGRjcwNTg7IiBkPSJNMzI1LjIsMzYyLjRsLTY2LjQsNjYuNGMtMi44LDIuOC03LjIsMi44LTEwLDBsLTY2LTY2LjRjLTQuNC00LjQtMS4yLTEyLDQuOC0xMmgxNC44IGM0LDAsNy4yLTMuMiw3LjItNy4ydi05NmMwLTQsMy4yLTcuMiw3LjItNy4yaDc0LjhjNCwwLDcuMiwzLjIsNy4yLDcuMnY5NmMwLDQsMy4yLDcuMiw3LjIsNy4yaDE0LjggQzMyNi40LDM1MC40LDMyOS42LDM1OCwzMjUuMiwzNjIuNHoiLz4gPC9zdmc+IA==
  10. // @author hzfi < hzfi@hx.fyi >
  11. // @supportURL https://github.com/hzfi/user-scripts-and-styles/issues/new
  12. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=your.email.here@example.com&item_name=Greasy+Fork+donation
  13. // @contributionAmount 5
  14. // @include *://medium.com/*
  15. // @include *://x.com/*
  16. // @include *://*.x.com/*
  17. // @include *://weibo.com/*
  18. // @include *://*.weibo.com/*
  19. // @include *://*.vmgirls.com/*
  20. // @include *://wallpaperhub.app/*
  21. // @include *://*.bing.com/*
  22. // @include *://*.msn.cn/*
  23. // @include *://instagram.com/*
  24. // @include *://*.instagram.com/*
  25. // @include *://tiktok.com/*
  26. // @include *://*.tiktok.com/*
  27. // @include *://*.douyin.com/*
  28. // @include *://*.kuaishou.com/*
  29. // @include *://*.xiaohongshu.com/*
  30. // @match *://nijijourney.com/*
  31. // @match *://*.midjourney.com/*
  32. // @match *://dribbble.com/*
  33. // @match *://*.dribbble.com/*
  34. // @match *://bsky.app/*
  35. // @match *://mastodon.social/*
  36. // @match *://mastodon.online/*
  37.  
  38.  
  39. // @noframes
  40. // @grant unsafeWindow
  41. // @grant GM_setClipboard
  42. // @grant GM_xmlhttpRequest
  43. // @grant GM_openInTab
  44. // @grant GM_registerMenuCommand
  45. // @grant GM_getValue
  46. // @grant GM_setValue
  47. // @grant GM_getResourceText
  48. // @grant GM_info
  49. // @grant GM_addStyle
  50. // ==/UserScript==
  51.  
  52. const styleContent = `.hx-download-original-images-tool{
  53. position: absolute;
  54. background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTA4IDUwOCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+IDxjaXJjbGUgc3R5bGU9ImZpbGw6I0ZGRDA1QjsiIGN4PSIyNTQiIGN5PSIyNTQiIHI9IjI1NCIvPiA8cGF0aCBzdHlsZT0iZmlsbDojRkZGRkZGOyIgZD0iTTM3Mi44LDE5NkgzNjhjLTIuNC00MC40LTM1LjYtNzIuNC03Ni40LTcyLjRjLTQsMC04LDAuNC0xMS42LDAuOGMtMTYtMjguNC00Ni00Ny42LTgwLjgtNDcuNiBjLTUxLjIsMC05Mi40LDQxLjYtOTIuNCw5Mi40YzAsMTAuOCwyLDIxLjIsNS4yLDMwLjhjLTI1LjIsMTAtNDIuOCwzNC00Mi44LDYyLjRjMCwzNi40LDI5LjYsNjYuNCw2Ni40LDY2LjRoMjM3LjIgYzM2LjQsMCw2Ni40LTI5LjYsNjYuNC02Ni40QzQzOC44LDIyNS42LDQwOS4yLDE5NiwzNzIuOCwxOTZ6Ii8+IDxwYXRoIHN0eWxlPSJmaWxsOiNGRjcwNTg7IiBkPSJNMzI1LjIsMzYyLjRsLTY2LjQsNjYuNGMtMi44LDIuOC03LjIsMi44LTEwLDBsLTY2LTY2LjRjLTQuNC00LjQtMS4yLTEyLDQuOC0xMmgxNC44IGM0LDAsNy4yLTMuMiw3LjItNy4ydi05NmMwLTQsMy4yLTcuMiw3LjItNy4yaDc0LjhjNCwwLDcuMiwzLjIsNy4yLDcuMnY5NmMwLDQsMy4yLDcuMiw3LjIsNy4yaDE0LjggQzMyNi40LDM1MC40LDMyOS42LDM1OCwzMjUuMiwzNjIuNHoiLz4gPC9zdmc+IA==);
  55. background-size: cover;
  56. width: 50px;
  57. height: 50px;
  58. cursor: pointer;
  59. opacity: .35;
  60. z-index: 50000;
  61. transform: scale(.75);
  62. transition: all cubic-bezier(0.18, 0.89, 0.32, 1.28) 250ms;
  63. }
  64. .hx-download-original-images-tool.white{
  65. background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9ImluaGVyaXQiIGltcGxpY2l0LWNvbnNlbnQtc291cmNlPSJ0cnVlIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KIDxnIHRyYW5zZm9ybT0ibWF0cml4KDEuMDEyMiAwIDAgMS4wMTIyIC0yOC42ODQgLTMuNDMzOSkiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLXdpZHRoPSIxLjMiPgogIDxjaXJjbGUgY3g9IjQwLjE5NCIgY3k9IjE1LjI0OCIgcj0iOC45ODI0Ii8+CiAgPHBhdGggZD0ibTQ1IDE3LTQuNTMzIDMuNTQ3Yy0wLjE2NjU5IDAuMTMwMzUtMC4zODQ2MSAwLjE0OTU3LTAuNTM0MTggMGwtNC41NjY0LTMuNTQ3Yy0wLjIzNTA0LTAuMjM1MDQtMC4wNjQxLTAuNjQxMDIgMC4yNTY0MS0wLjY0MTAyaDIuNDM2MmMwLjIxMzY3IDAgMC4zODQ2MS0wLjE3MDk0IDAuMzg0NjEtMC4zODQ2MXYtNS43MzI5YzAtMC4yMTM2NyAwLjE3MDk0LTAuMzg0NjEgMC4zODQ2MS0wLjM4NDYxaDIuNzAzNmMwLjIxMzY3IDAgMC4zODQ2MSAwLjE3MDk0IDAuMzg0NjEgMC4zODQ2MXY1LjczMjljMCAwLjIxMzY3IDAuMTcwOTQgMC4zODQ2MSAwLjM4NDYxIDAuMzg0NjFoMi40NjM5YzAuMjk5MTQgMCAwLjQ5NjgyIDAuNDM2MTggMC4yMzUwNCAwLjY0MTAyeiIvPgogPC9nPgo8L3N2Zz4K);
  66. width: 24px;
  67. height: 24px;
  68. }
  69. .hx-download-original-images-tool:hover {
  70. opacity:1;
  71. transform: scale(.9);
  72. }
  73. .hx-download-original-images-tool:active {
  74. opacity:.8;
  75. transform: scale(.7) rotateZ(360deg);
  76. }
  77. .hx-download-original-images-tool-msg {
  78. position: fixed;
  79. left: -250px;
  80. bottom: 50px;
  81. width: 250px;
  82. background: linear-gradient(to bottom right, #00000037, #0004 , #00000057 );
  83. box-shadow: 1px 0 20px 1px #64646433;
  84. padding: 2px 20px;
  85. z-index: 65536;
  86. border-radius: 100px;
  87. color: #fff;
  88. transform: translateX(280px) translateY(0);
  89. transition: all cubic-bezier(0.18, 0.89, 0.32, 1.28) 250ms;
  90. }`
  91.  
  92. const head = document.getElementsByTagName('head');
  93. head[0].insertAdjacentHTML('beforeend', `<style type="text/css">${styleContent}</style>`);
  94.  
  95. GM_addStyle(styleContent);
  96.  
  97.  
  98. console.warn('Welcome to %c \ud83d\ude48\ud83d\ude49\ud83d\ude4a\u0020\u0048\u007a\u00b2\u0020\u0053\u0063\u0072\u0069\u0070\u0074\u0020\u004c\u0069\u0062\u0072\u0061\u0072\u0079 %c v0.06 ', 'background-color:teal;color: white;border:1px solid teal;border-radius: 4px 0 0 4px;border-left-width:0;padding:1px;margin:2px 0;font-size:1.1em', 'background-color:#777;color: white;border:1px solid #777;border-radius: 0 4px 4px 0;border-right-width:0;padding:1px;margin:5px 0;');
  99.  
  100. try {
  101. customElements.define('hxdownload-message',
  102. class extends HTMLElement {
  103. constructor () {
  104. super();
  105.  
  106. const divElem = document.createElement('div');
  107. // divElem.textContent = this.getAttribute('text');
  108. divElem.className = 'text-node'
  109. // style
  110. const style = document.createElement('style');
  111. style.append(document.createTextNode(`
  112. .text-node{
  113. font-size: 14px;
  114. line-height: 21px;
  115. font-family: sans-serif;
  116. width: 100%;
  117. overflow: hidden;
  118. word-break: break-word;
  119. }
  120. `))
  121. const shadowRoot = this.attachShadow({
  122. mode: 'open'
  123. });
  124. shadowRoot.appendChild(style);
  125. shadowRoot.appendChild(divElem);
  126. }
  127. }
  128. );
  129.  
  130. } catch (error) {
  131.  
  132. }
  133.  
  134.  
  135. globalThis.__hx_Msg_list = new Set();
  136.  
  137. class __hx_MsgIns {
  138. constructor (text) {
  139. this.text = text;
  140. this.el = document.createElement('hxdownload-message')
  141. document.body.insertAdjacentElement('beforeend', this.el)
  142. this.el.className = 'hx-download-original-images-tool-msg';
  143. this.textEl = this.el.shadowRoot.querySelector('.text-node')
  144. this.textEl.innerText = text;
  145. __hx_Msg_list.add(this);
  146. this.el.style.transform = `translateX(280px) translateY(-${(__hx_Msg_list.size - 1) * 50}px)`
  147. }
  148. /**
  149. * @param {any} text
  150. */
  151. update(text) {
  152. this.textEl.innerText = text
  153. }
  154. close() {
  155. this.textEl.innerText = ''
  156. this.el.parentElement.removeChild(this.el)
  157. __hx_Msg_list.delete(this);
  158. }
  159. }
  160.  
  161. function formatBytes(bytes, decimals = 2) {
  162. if (!+bytes) return '0 Bytes'
  163.  
  164. const k = 1024
  165. const dm = decimals < 0 ? 0 : decimals
  166. const sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
  167.  
  168. const i = Math.floor(Math.log(bytes) / Math.log(k))
  169.  
  170. return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
  171. }
  172.  
  173. const openDown = (url, e, name) => {
  174. e && e.preventDefault();
  175. e && e.stopPropagation()
  176.  
  177.  
  178. const downBlobUrl = (blobUrl) => {
  179. let el = document.createElement("a");
  180. el.setAttribute("href", blobUrl);
  181. if (name) {
  182. el.setAttribute("download", name)
  183. }
  184. if (document.createEvent) {
  185. const event = document.createEvent("MouseEvents");
  186. event.initEvent("click", true, true);
  187. el.dispatchEvent(event);
  188. } else {
  189. el.click();
  190. }
  191.  
  192. }
  193.  
  194. if (url.startsWith('blob')) {
  195. downBlobUrl(url)
  196. return
  197.  
  198. }
  199.  
  200. fetch(url, {
  201. mode: "cors"
  202. })
  203. .then(async resp => {
  204. // instead of response.json() and other methods
  205. const reader = resp.body.getReader();
  206. const contentLength = +resp.headers.get('Content-Length');
  207. const ct = (resp.headers && resp.headers.get('Content-Type')) || '';
  208. console.log('ct', ct);
  209. // Step 3: read the data
  210. let receivedLength = 0; // received that many bytes at the moment
  211. let chunks = []; // array of received binary chunks (comprises the body)
  212.  
  213.  
  214. const __hx_Msg = new __hx_MsgIns('Loading');
  215.  
  216.  
  217. // infinite loop while the body is downloading
  218. while (true) {
  219. // done is true for the last chunk
  220. // value is Uint8Array of the chunk bytes
  221. const {
  222. done,
  223. value
  224. } = await reader.read();
  225.  
  226. if (done) {
  227. break;
  228. }
  229. chunks.push(value);
  230. receivedLength += value.length;
  231. const text =
  232. __hx_Msg.update(`Received ${formatBytes(receivedLength)} / ${formatBytes(contentLength)}`)
  233. }
  234. __hx_Msg.close()
  235. return new Blob(chunks, {
  236. type: ct
  237. });
  238.  
  239.  
  240. // // Step 4: concatenate chunks into single Uint8Array
  241. // let chunksAll = new Uint8Array(receivedLength); // (4.1)
  242. // let position = 0;
  243. // for(let chunk of chunks) {
  244. // chunksAll.set(chunk, position); // (4.2)
  245. // position += chunk.length;
  246. // }
  247.  
  248. // // Step 5: decode into a string
  249. // let result = new TextDecoder("utf-8").decode(chunksAll);
  250.  
  251. // // We're done!
  252. // let commits = JSON.parse(result);
  253. // alert(commits[0].author.login);
  254.  
  255. // return resp.blob()
  256. })
  257. .then(r => {
  258. const blobUrl = URL.createObjectURL(r)
  259. downBlobUrl(blobUrl)
  260. })
  261. .catch(err => {
  262. console.log("Request failed", err);
  263. });
  264. }
  265.  
  266. const hostname = window.location.hostname
  267.  
  268. const lastItem = (arr, index = 0) => arr.length ? arr[arr.length - 1 - index] : ''
  269.  
  270. const createDomAll = (item, fn) => {
  271. let domDL = document.createElement('a');
  272. domDL.className = 'hx-download-original-images-tool'
  273. domDL.title = '下载原始图片'
  274.  
  275. // item.addEventListener('load', _ => {
  276. let link = fn(item.src)
  277. domDL.href = link || item.src
  278. domDL.addEventListener('click', e => {
  279. e.preventDefault()
  280. e.stopPropagation()
  281. openDown(link, e, lastItem(link.split('/')))
  282. })
  283. // })
  284. item.insertAdjacentElement('afterEnd', domDL)
  285. }
  286.  
  287. const createDom = (cfg) => {
  288. const {
  289. parent,
  290. link,
  291. name,
  292. className = '',
  293. style = '',
  294. target,
  295. postion = 'afterEnd',
  296. linkArr
  297. } = cfg
  298.  
  299. const genDomDL = (dom) => {
  300. let domDL = dom || document.createElement('a');
  301. Object.assign(domDL, {
  302. title: '下载原始图片',
  303. className: 'hx-download-original-images-tool ' + className,
  304. style: style,
  305. href: link,
  306. })
  307. domDL.onclick = e => {
  308. e && e.preventDefault();
  309. e && e.stopPropagation()
  310. const newName = name || lastItem(link.split('/'))
  311. if (linkArr) {
  312. linkArr.forEach(({
  313. link,
  314. name
  315. }) => openDown(link, e, name))
  316. } else {
  317. openDown(link, e, newName);
  318. }
  319. }
  320. return domDL
  321. }
  322.  
  323. let parent2 = parent
  324. if (!parent && target) {
  325. parent2 = target.parentElement
  326. }
  327. // if (['afterEnd', 'beforeBegin'].includes(postion)) {
  328. // parent2 = target.parentElement.parentElement
  329. // }
  330. const exist = parent2.querySelector('.hx-download-original-images-tool')
  331. if (exist) {
  332. genDomDL(exist)
  333. } else {
  334. parent2.insertAdjacentElement(postion, genDomDL())
  335. }
  336. }
  337.  
  338. const updateLink = (dom, link) => {
  339. dom.href = link
  340. const newName = lastItem(link.split('/'))
  341. dom.onclick = e => openDown(link, e, newName)
  342. }
  343.  
  344. const init = () => {
  345.  
  346. if ([
  347. 'x.com',
  348. 'mobile.x.com',
  349. 'tweetdeck.x.com',
  350. ].includes(hostname)) {
  351. //x
  352. window.addEventListener('mouseover', ({
  353. target
  354. }) => {
  355. const src = target && target.src
  356. const parent = target.parentElement
  357. const next = parent && parent.nextElementSibling
  358. if (target.tagName == 'IMG' &&
  359. !(next && next.className.includes('hx-download-original-images-tool')) &&
  360. !/profile_images|emoji|video_thumb/g.test(src)) {
  361. const link = src.replace(/\&name=\w+/g, '&name=orig')
  362. const name = lastItem(link.split('/')).replace(/\?format=(\w+)\&name=orig/g, (_, b) => `.${b}`)
  363. const style = 'margin-left: 10px;margin-top: 10px;'
  364. const cfg = {
  365. parent,
  366. link,
  367. name,
  368. style
  369. }
  370. createDom(cfg)
  371. }
  372. })
  373. } else if (hostname.includes('weibo')) {
  374. const isWeiboNode = dom => {
  375. const getNodeValue = el => el.attributes['node-type'] && el.attributes['node-type'].nodeValue
  376. if (getNodeValue(dom.parentElement) === 'artwork_box' || getNodeValue(dom) === 'img_box' || dom.className.includes('woo-picture-main') || dom.className.includes('woo-picture-slot') || dom.className.includes('imgInstance')) {
  377. return true
  378. } else {
  379. return false
  380. }
  381. }
  382. window.addEventListener('mouseover', ({
  383. target
  384. }) => {
  385. const parent = target.parentElement
  386. const next = parent && parent.nextElementSibling
  387. if (target.tagName == 'IMG' && isWeiboNode(parent)) {
  388. const link = target.src.replace(/orj\d+|mw\d+/g, 'large')
  389. if (next && next.className.includes('hx-download-original-images-tool')) {
  390. updateLink(next, link)
  391. } else {
  392. const style = 'top: 40px;right: 10px;'
  393. const cfg = {
  394. parent,
  395. link,
  396. style
  397. }
  398. createDom(cfg)
  399. }
  400. }
  401. })
  402. } else if (hostname === "www.vmgirls.com") {
  403. // vmgirls
  404. let domDL = document.createElement('a');
  405. domDL.className = 'hx-download-original-images-tool '
  406. domDL.style = 'position: relative;margin-right: 10px;display: inline-block;vertical-align: -20px;'
  407. domDL.title = '下载原始图片'
  408. domDL.onclick = e => {
  409. const list1 = [...document.querySelector('.post').querySelectorAll('a')].filter(x => x.src && x.src.indexOf('static.vmgirls.com/image') !== -1)
  410. const list2 = [...document.querySelector('.post-content').querySelectorAll('img')].filter(x => x.src && x.src.indexOf('t.cdn.ink/image') !== -1)
  411. const imgList = [...list1, ...list2].map((x, i) => ({
  412. link: x.src && x.src.replace('-scaled', ''),
  413. name: `${x.alt || x.title}_${i}.jpg`
  414. }))
  415. domDL.title += ' ' + imgList.length
  416. imgList.forEach(x => openDown(x.link, e, x.name))
  417. const link1 = imgList.map(x => x.link).join('\n')
  418. const link2 = imgList.map(x => `aria2c -o ${x.name} ${x.link}`).join('\n')
  419. const content = `<html><head><meta charset="utf-8"><title>获取链接</title></head><body><textarea style="width: 850px; height: 250px; margin: 30px;">${link1}</textarea>
  420. <textarea style="width: 850px; height: 250px; margin: 30px;">${link2}</textarea>
  421. </body></html>`
  422. window.open(URL.createObjectURL(new Blob([content], {
  423. type: 'text/html'
  424. })))
  425. }
  426. document.querySelector('.main-submenu').insertAdjacentElement('afterBegin', domDL)
  427. } else if (hostname === "medium.com") {
  428. // medium
  429. document.querySelector('article').querySelectorAll('img').forEach(x => {
  430. if (x.width < 80) {
  431. return
  432. } else if (x.src.includes('max')) {
  433. createDomAll(x, src => src.replace(/max\/\d+\//g, 'max/30000/'))
  434. } else if (x.src.includes('resize')) {
  435. // https://miro.medium.com/v2/resize:fit:700/1*OG99lac_uxo6nUOcJtUrNw.jpeg
  436. createDomAll(x, src => src.replace(/resize:fit:\d+\//g, ''))
  437. }
  438. })
  439. } else if (hostname === "wallpaperhub.app") {
  440. // wallpaperhub
  441. const odomList = [...document.querySelectorAll('.downloadButton')]
  442. odomList.forEach(odom => {
  443. if (odom) {
  444. let link0 = odom.href.split('downloadUrl=')[1]
  445. const link = link0
  446. const style = 'position: relative;margin-right: 10px;display: inline-block;vertical-align: -20px;'
  447. const cfg = {
  448. parent: odom.parentElement.parentElement,
  449. link,
  450. style,
  451. postion: 'beforeBegin'
  452. }
  453. createDom(cfg)
  454. }
  455. })
  456. } else if (hostname === "ntp.msn.cn") {
  457. // edge 首页
  458. const link = document.querySelector('background-image')._imageSource;
  459. const style = 'position: fixed;right: 80px;top: 40px;'
  460. const cfg = {
  461. parent: document.body,
  462. link,
  463. className: 'white',
  464. style,
  465. postion: 'beforeBegin'
  466. }
  467. createDom(cfg)
  468. } else if (hostname === "www.bing.com") {
  469. // bing 首页
  470. const orig = document.querySelector('[style*="th?id="]').style.backgroundImage
  471. const link = orig.match(/th\?id\=[\w\d\.\-\_]+/g)[0].replace('1920x1080', 'UHD').replace('webp', 'jpg')
  472. const name = link && link.split('=')[1]
  473. const style = 'position: relative;width: 42px;height: 42px;margin: 0;opacity: .9;'
  474. const cfg = {
  475. parent: document.querySelector('#id_h'),
  476. link,
  477. name,
  478. className: 'white',
  479. style,
  480. postion: 'afterBegin'
  481. }
  482. createDom(cfg)
  483. } else if (hostname === "cn.bing.com") {
  484. // bing 首页
  485. const link = document.querySelector('#bgImgProgLoad').dataset.ultraDefinitionSrc.split('&')[0];
  486. const name = link && link.split('=')[1]
  487. const style = 'position: fixed;right: 225px;bottom: 53px;margin: 0px;width: 64px;height: 64px;z-index: 550;opacity: .9;'
  488. const cfg = {
  489. parent: document.body,
  490. link,
  491. name,
  492. className: 'white',
  493. style,
  494. postion: 'beforeBegin'
  495. }
  496. createDom(cfg)
  497. } else if (hostname === "www.instagram.com") {
  498. window.addEventListener('mouseover', ({
  499. target
  500. }) => {
  501. const el = target.previousElementSibling
  502. const el2 = target.parentElement
  503. const img = (el && el.querySelector('img:not([data-testid])') || el2 && el2.querySelector('video:not([data-testid])'))
  504. if (img) {
  505. const src = img.src
  506. const parent = img.parentElement
  507. const link = src
  508. const style = 'left: 10px;top: 10px;'
  509. const cfg = {
  510. parent,
  511. link,
  512. style,
  513. target: img,
  514. name: img.alt ? (img.alt + '.jpg') : src.split(/[\?\/]/g).filter(x => x.endsWith('.jpg'))[0],
  515. postion: 'beforeEnd'
  516. }
  517. createDom(cfg)
  518. }
  519. })
  520. } else if (hostname === "www.tiktok.com") {
  521. window.addEventListener('mouseover', ({
  522. target
  523. }) => {
  524. if (target.tagName == 'VIDEO') {
  525. const src = target.src
  526. const parent = target.parentElement
  527. const link = src
  528. const style = 'left: 10px;top: 10px;'
  529. const cfg = {
  530. parent,
  531. link,
  532. style,
  533. target,
  534. name: lastItem(src.split('?')[0].split('/').filter(x => x)),
  535. postion: 'beforeEnd'
  536. }
  537. createDom(cfg)
  538. }
  539. })
  540. } else if (hostname === "www.douyin.com") {
  541. window.addEventListener('mouseover', ({
  542. target
  543. }) => {
  544. // if (target && target.tagName === 'VIDEO') {
  545. // const src = (target.querySelector('source') || target).src
  546. // const link = src
  547. // const style = 'left: 10px;top: 10px;'
  548. // const cfg = {
  549. // link,
  550. // style,
  551. // target,
  552. // postion: 'afterEnd',
  553. // name: lastItem(src.split('?')[0].split('/').filter(x => x)),
  554. // }
  555. // createDom(cfg)
  556. // return
  557. // } else
  558. if (target && target.parentElement) {
  559. const container = target.parentElement.parentElement || {
  560. className: ''
  561. }
  562. if (container && container.className.includes('videoContainer') && container.querySelector('video')) {
  563. const src = (container.querySelector('source') || container.querySelector('video')).src
  564. const link = src
  565. const style = 'left: 10px;top: 10px;'
  566. const cfg = {
  567. link,
  568. style,
  569. target: container,
  570. postion: 'beforeEnd',
  571. name: lastItem(src.split('?')[0].split('/').filter(x => x)),
  572. }
  573. createDom(cfg)
  574. }
  575.  
  576. }
  577. })
  578. } else if (hostname === "www.kuaishou.com") {
  579. window.addEventListener('mouseover', ({
  580. target
  581. }) => {
  582. if (target && target.parentElement) {
  583. const container = target.parentElement.parentElement || {
  584. className: ''
  585. }
  586. if (container && container.className.includes('kwai-player') && container.querySelector('video')) {
  587. const src = (container.querySelector('source') || container.querySelector('video')).src
  588. const link = src
  589. const style = 'left: 10px;top: 10px;'
  590. const cfg = {
  591. link,
  592. style,
  593. target: container,
  594. postion: 'beforeEnd',
  595. name: lastItem(src.split('?')[0].split('/').filter(x => x)),
  596. }
  597. createDom(cfg)
  598. }
  599.  
  600. }
  601. })
  602. } else if (hostname === "www.xiaohongshu.com") {
  603. window.addEventListener('mouseover', ({
  604. target
  605. }) => {
  606. const container = target && target.parentElement
  607. if (container && container.className && container.className.includes('carousel')) {
  608. const inner = container.querySelector('li:not([style*=none]) .inner')
  609. const src = inner && inner.style['background-image'].replace(/^url\(\"|\"\)$/g, '')
  610. const link = src
  611. const style = 'left: 10px;top: 10px;'
  612. const cfg = {
  613. link,
  614. style,
  615. parent: container,
  616. postion: 'beforeEnd',
  617. name: lastItem(src.split('?')[0].split('/').filter(x => x)) + '.jpg',
  618. }
  619. createDom(cfg)
  620. }
  621.  
  622.  
  623. })
  624. } else if (hostname === "dribbble.com") {
  625. window.addEventListener('mouseover', ({
  626. target
  627. }) => {
  628. const container = target && target.parentElement && target.parentElement.parentElement
  629. if (container && container.className && (container.className.includes('block-media') || container.className.includes('video-container'))) {
  630. const inner = container.querySelector('img') || container.querySelector('video');
  631. let src = '';
  632. if (inner.tagName === 'IMG') {
  633. src = (inner && (inner.src || inner.srcset)).split('?')[0]
  634. } else if (inner.tagName === 'VIDEO') {
  635. src = inner && inner.src
  636. }
  637. const link = src
  638. const style = 'left: 10px;top: 10px;'
  639. const cfg = {
  640. link,
  641. style,
  642. parent: container,
  643. postion: 'beforeEnd',
  644. name: lastItem(src.split('?')[0].split('/').filter(x => x)),
  645. }
  646. createDom(cfg)
  647. }
  648.  
  649.  
  650. })
  651.  
  652. } else if (hostname === "nijijourney.com" || hostname === 'www.midjourney.com') {
  653. window.addEventListener('mouseover', ({
  654. target
  655. }) => {
  656. const container = target && target.parentElement && target.parentElement.parentElement
  657. if (container) {
  658. const inner = container.querySelector('link')
  659. const src = inner && inner.href.replace('_32_N.webp', '.webp')
  660. const link = src || ''
  661. const style = 'left: 10px;top: 10px;'
  662. const baseName = lastItem(link.split(/[\/\?]/), 1);
  663. let linkArr = []
  664. //linkArr.forEach(({ link, name}
  665. const linkCount = lastItem(link.split(/[\.\_\/]/), 1)
  666. const cfg = {
  667. link,
  668. style,
  669. parent: container,
  670. postion: 'beforeEnd',
  671. name: baseName,
  672. }
  673. if (Number(linkCount) > 0) {
  674. cfg.linkArr = Array.from({
  675. length: Number(linkCount) + 1
  676. }).map((x, i) => ({
  677. link: link.replace(/(\d).webp$/, i + '.webp'),
  678. name: baseName + '_' + i
  679. }))
  680. }
  681. createDom(cfg)
  682. }
  683.  
  684.  
  685. })
  686. } else if ([
  687. 'bsky.app',
  688. ].includes(hostname)) {
  689. window.addEventListener('mouseover', ({
  690. target
  691. }) => {
  692. const src = target && target.src
  693. const parent = target.parentElement
  694. const next = parent && parent.nextElementSibling
  695. if (target.tagName == 'IMG' && target.width >= 100 && target.height >= 100 &&
  696. !(next && next.className.includes('hx-download-original-images-tool')) &&
  697. !/profile_images|emoji|video_thumb/g.test(src)) {
  698. const link = src.replace(/^https:\/\/cdn\.bsky\.app\/img\/feed_thumbnail\/plain\/(did:plc:[a-z0-9]+)\/([a-z0-9]+)@(jpg|jpeg|png|gif|bmp|webp|svg|tiff)$/, 'https://bsky.social/xrpc/com.atproto.sync.getBlob?did=$1&cid=$2')
  699. const name = src.replace(/^https:\/\/cdn\.bsky\.app\/img\/feed_thumbnail\/plain\/(did:plc:[a-z0-9]+)\/([a-z0-9]+)@(jpg|jpeg|png|gif|bmp|webp|svg|tiff)$/, '$1.$2.$3')
  700. const style = 'margin-left: 10px;margin-top: 10px;'
  701. const cfg = {
  702. parent,
  703. link,
  704. name,
  705. style
  706. }
  707. createDom(cfg)
  708. }
  709. })
  710. // 长毛象
  711. } else if (
  712. [
  713. 'mastodon.social',
  714. 'mastodon.online',
  715. ].includes(hostname)
  716. ) {
  717. window.addEventListener('mouseover', ({
  718. target
  719. }) => {
  720. const container = target && target.parentElement && target.parentElement.parentElement
  721. if (container && container.className && (container.className.includes('media-gallery__item')
  722.  
  723. )) {
  724. const inner = container.querySelector('img')
  725. || container.querySelector('video');
  726. let src = '';
  727. if (inner.tagName === 'IMG') {
  728. src = (inner && (inner.src || inner.srcset)).split('?')[0]
  729. } else if (inner.tagName === 'VIDEO') {
  730. src = inner && inner.src
  731. }
  732. const link = src?.replace('/small/', '/original/')
  733. const style = 'left: 10px;top: 10px;'
  734. const cfg = {
  735. link,
  736. style,
  737. parent: container,
  738. postion: 'beforeEnd',
  739. name: lastItem(src.split('?')[0].split('/').filter(x => x)),
  740. }
  741. createDom(cfg)
  742. }
  743.  
  744.  
  745. })
  746. } else {
  747. // others
  748. }
  749.  
  750. }
  751.  
  752. setTimeout(() => {
  753. init()
  754. }, 1200);

QingJ © 2025

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