Greasy Fork is available in English.

Download Original Picture

A tool to help you download full size images from websites

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