您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 Civitai 的下载按钮旁添加复制按钮,轻松复制直链地址 还有去广告
// ==UserScript== // @name Civitai Direct Link Helper // @name:zh-CN Civitai 下载助手 // @namespace http://tampermonkey.net/ // @version 1.21 // @description Adds a convenient copy button next to download buttons on Civitai to easily get direct download links for models // @description:zh-CN 在 Civitai 的下载按钮旁添加复制按钮,轻松复制直链地址 还有去广告 // @author hua // @match https://civitai.com/* // @match https://civitai.green/* // @grant unsafeWindow // @grant GM_xmlhttpRequest // @run-at document-start // @license MIT // ==/UserScript== (function () { 'use strict'; unsafeWindow.setInterval = function (fn, time) { }; const origin_setTimeout = unsafeWindow.setTimeout; unsafeWindow.setTimeout = function (fn, time) { const tags = ['schedule', 'coreAdServerStart', 'exited', 'maybeFetchNotificationAndTrackCurrentUrl', 'googletagservices', '/api/internal/activity', 'iframe_api']; if (tags.some(tag => fn.toString().includes(tag))) { return; } function fn_() { fn(); } origin_setTimeout(fn_, time); }; hookCreateElement(); changeInfo(); modifywebpack(); function modifywebpack() { let webpackChunk_N_E; const hookPush = () => { const originPush = webpackChunk_N_E.push; webpackChunk_N_E.push = function (chunk) { const funs = chunk?.[1]; if (funs?.['68714'] && !funs['68714'].inject) { let funStr = funs['68714'].toString(); // funStr = funStr.replace('function(e,i,t){', 'function(e,i,t){debugger;'); // funStr = funStr.replace('function(e,t,n){"use strict";', 'function(e,t,n){"use strict";debugger;'); let match_tag = funStr.match(/return (.{1,5})\.length\?\(0,/); if (match_tag) { const tag = match_tag[1]; funStr = funStr.replace(`return ${tag}.length?(0,`, `${tag}=${tag}.filter(item => item.type !== "ad");return ${tag}.length?(0,`); } funs['68714'] = new Function('return ' + funStr)(); funs['68714'].inject = true; } if (funs?.['56053'] && !funs['56053'].inject) { let funStr = funs['56053'].toString(); // funStr = funStr.replace('function(e,t,i){"use strict";', 'function(e,t,i){"use strict";debugger;'); let match_tag = funStr.match(/children\:(.{1,5})\.map\(\(/); if (match_tag) { const tag = match_tag[1]; const re_match = funStr.match(/return\(0,(.{1,5}).jsx\)\("div"/); if (re_match) { const re_str = re_match[0]; funStr = funStr.replace(re_str, `${tag}.forEach((item,i)=>{ ${tag}[i] = item.filter(ite => ite.data.type !== "ad")});${re_str}`); } } funs['56053'] = new Function('return ' + funStr)(); funs['56053'].inject = true; } originPush.call(this, chunk); }; }; Object.defineProperty(unsafeWindow, 'webpackChunk_N_E', { get: function () { return webpackChunk_N_E; }, set: function (value) { webpackChunk_N_E = value; hookPush(); } }); } function changeInfo() { const originFetch = unsafeWindow.fetch; unsafeWindow.fetch = function (url, options) { async function fetch_request(response) { if (url.includes('/announcement.getAnnouncements')) { try { const data = await response.json(); if (data.result?.data?.json) data.result.data.json = []; console.log('modify announcement.getAnnouncements'); response = new Response(JSON.stringify(data), response); } catch (e) { console.log('fetch_request error', e); } } if (url.includes('auth/session')) { try { const data = await response.json(); if (data.user) { const user = data.user; user.allowAds = false; console.log('modify auth/session'); } response = new Response(JSON.stringify(data), response); } catch (e) { console.log('fetch_request error', e); } } return response; } return originFetch(url, options).then(fetch_request); }; let monitorcount = 1; const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { for (const node of mutation.addedNodes) { if (node.tagName === 'SCRIPT' && node.id === '__NEXT_DATA__') { monitorcount--; console.log('modify __NEXT_DATA__'); if (monitorcount <= 0) { observer.disconnect(); } modify(node); } } } }); observer.observe(document.documentElement, { childList: true, subtree: true }); function modify(node) { const initalData = JSON.parse(node.textContent); const trpcData = initalData.props?.pageProps?.trpcState?.json; if (trpcData) { const queries = trpcData.queries || []; if (queries.length > 0) { const query = queries[0]; const data = query.state?.data || []; const remveIndex = []; data.forEach((item, index) => { const ignoreFlags = ['Announcement', 'Event', 'CosmeticShop']; if (ignoreFlags.includes(item.type)) { remveIndex.push(index); } }); remveIndex.reverse(); remveIndex.forEach(index => { data.splice(index, 1); }); } } const flags = initalData.props?.pageProps?.flags; if (flags) { flags.adsEnabled = false; } const session = initalData.props?.pageProps?.session; if (session?.user) { const user = session.user; user.allowAds = false; } node.textContent = JSON.stringify(initalData); } } function paraseDownloadUrl(button) { let originalColor = window.getComputedStyle(button).color; const restoreTimeout = 10000; let interval = null; const restore = () => { button.style.color = originalColor; }; const onError = () => { clearInterval(interval); interval = null; button.style.color = '#FF0000'; setTimeout(() => { restore(); }, restoreTimeout); }; const onSuccess = () => { clearInterval(interval); interval = null; navigator.clipboard.writeText(button.downloadUrl).then(() => { }).catch((e) => { alert('copy error:' + e.message); }); button.style.color = '#00FF00'; setTimeout(() => { restore(); }, restoreTimeout); }; if (button.downloadUrl) { onSuccess(); return; } const uri = button.getAttribute('href'); interval = setInterval(() => { button.style.color = `rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`; }, 100); GM_xmlhttpRequest({ method: "GET", url: `https://civitai.com${uri}`, timeout: 10000, anonymous: false, redirect: 'manual', maxRedirects: 0, onload: function (response) { const downloadUrl = response.responseHeaders.match(/location:(.*?)(?:\r?\n)/i)?.[1]; button.downloadUrl = downloadUrl; downloadUrl ? onSuccess() : onError(); }, onerror: function (error) { console.log('onerror', error); onError(); }, ontimeout: function () { console.log('ontimeout'); onError(); } }); } function hookDownloadButton(node) { let isClick = false; let timers = []; node.addEventListener('click', function (e) { if (isClick) { isClick = false; return; } e.preventDefault(); const timer = setTimeout(() => { isClick = true; timers.forEach(timer => clearTimeout(timer)); timers.length = 0; node.click(); }, 300); timers.push(timer); }); node.addEventListener('dblclick', function (e) { e.preventDefault(); timers.forEach(timer => clearTimeout(timer)); timers.length = 0; paraseDownloadUrl(node); }); } function hookCreateElement() { const origin_createElement = unsafeWindow.document.createElement; unsafeWindow.document.createElement = function () { const node = origin_createElement.apply(this, arguments); if (arguments[0].toUpperCase() === 'A') { const originSetAttribute = node.setAttribute; node.setAttribute = function (name, value) { if (name === 'href' ) { if (value?.startsWith('/api/download/models/')){ console.log('hookButton'); hookDownloadButton(node); } if (value?.includes('/pricing?utm_campaign=holiday_promo')) { node.style.display = 'none'; console.log('hookPricing'); } } return originSetAttribute.call(this, name, value); }; } if (arguments[0].toUpperCase() === 'IFRAME') { return null; } return node; }; unsafeWindow.document.createElement.toString = origin_createElement.toString.bind(origin_createElement); } })();