Skip to content

Instantly share code, notes, and snippets.

@zhanhongtao
Last active November 13, 2024 07:51
Show Gist options
  • Save zhanhongtao/198e58075d4b3415c2f0f5e394204c6a to your computer and use it in GitHub Desktop.
Save zhanhongtao/198e58075d4b3415c2f0f5e394204c6a to your computer and use it in GitHub Desktop.
auto-run-script-for-task
// ==UserScript==
// @name auto-run-script-for-every-task
// @namespace https://x181.cn/
// @version 0.35.16
// @author TT
// @description Hello world!
// @icon https://img.artproglobal.com/c/21736720685ad57d4fb8362b70f62f8d?width=512&height=512&hTag=d7a495871ba01409369e6c2231ad49f0
// @homepage https://x181.cn
// @downloadURL https://gist.github.com/zhanhongtao/198e58075d4b3415c2f0f5e394204c6a/raw/task.user.js
// @updateURL https://gist.github.com/zhanhongtao/198e58075d4b3415c2f0f5e394204c6a/raw/task.user.js
// @match *://*/*
// @connect localhost
// @connect x181.cn
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
const Log = (flag = false) => {
return function log2(...rest) {
if (flag === false)
return;
let list2 = rest;
if (typeof window !== "undefined" && typeof top !== "undefined") {
list2 = [
`%cisTop: ${top === window}. url = ${location.href}.`,
"color: gray; font-size: 1.2em;",
...rest
];
}
console.log(...list2);
};
};
const log$2 = Log(false);
const defaultMeta = { pause: false };
function match(rule, value) {
if (Array.isArray(rule))
return rule.some((rule2) => match(rule2, value));
if (typeof rule === "boolean")
return rule;
if (typeof rule === "string")
return value.includes(rule);
if (rule instanceof RegExp)
return rule.test(value);
if (typeof rule === "function")
return rule(value);
if ("exec" in rule)
return rule.test(value);
let result = true;
if (result && rule.basic)
result = match(rule.basic, value);
if (result && rule.or)
result = rule.or.some((rule2) => match(rule2, value));
if (result && rule.not)
result = match(rule.not, value) == false;
return result;
}
function filter(list2, value) {
return list2.filter((task2) => match(task2.match, value));
}
async function run(task2) {
if (task2.top && top !== window)
return;
log$2("start run task = ", task2);
task2.script(task2);
}
function core(scripts2, url) {
filter(scripts2, url).forEach((task2) => {
log$2("match.task", task2);
run({ ...task2, meta: task2.meta || { ...defaultMeta } });
});
}
const bingTask = {
match: new URLPattern({ hostname: "cn.bing.com" }),
script: task$3,
top: true
};
const selectors = [
"#bgDiv",
".hp_top_cover",
".img_cont"
];
function task$3() {
let nodes = document.querySelectorAll(selectors.join(","));
if (nodes.length === 0)
return;
Array.from(nodes).some((node) => {
let style = window.getComputedStyle(node);
let backgroundImage = style.backgroundImage;
let res = "";
backgroundImage.replace(/url\(('|")?(.*?)\1\)/, (_, __, src) => {
res = src;
return "";
});
return res ? (console.log("%c wallpaper:", `color: red`, res), true) : false;
});
}
function waitForSelector(selector, options) {
let node = null;
return waitForFunction(() => {
const selectors2 = Array.isArray(selector) ? selector : [selector];
const callback = (selector2) => {
node = document.querySelector(selector2);
return node != null;
};
return options ? options.type === "some" ? selectors2.some(callback) : selectors2.every(callback) : selectors2.some(callback);
}, { ...options, polling: 100 }).then(() => node);
}
const defaultWaitForFunctionOptions = {
timeout: 3e3,
polling: "raf"
};
function waitForFunction(fn, options) {
let t = Date.now();
const opt = { ...defaultWaitForFunctionOptions, ...options };
return new Promise((resolve, reject) => {
let f = () => {
const has = fn();
if (has)
resolve(true);
else if (typeof opt.polling === "number") {
if (opt.timeout === 0 || Date.now() - t < opt.timeout) {
setTimeout(f, opt.polling);
} else {
reject("timeout");
}
} else if (opt.polling === "raf") {
requestAnimationFrame(f);
} else if (opt.polling === "mutation") {
let timer = 0;
const observer = new MutationObserver(function() {
const has2 = fn();
if (has2 == false)
return;
if (timer)
clearTimeout(timer);
observer.disconnect();
resolve(true);
});
let node = document.querySelector("html");
if (node == null) {
observer.disconnect();
reject("no root node");
} else {
observer.observe(node, { childList: true });
timer = setTimeout(() => {
observer.disconnect();
reject("timeout");
}, opt.timeout);
}
} else {
reject("not support");
}
};
f();
});
}
const doubanTask = [
{
match: "book.douban.com",
script: bookTask,
top: true
},
{
match: [
"read.douban.com",
"https://www.ituring.com.cn/book/"
],
script: readTask,
top: true
},
// 配置豆瓣读书...
{
// https://freembook.com/?
match: "https://zebra.9farm.com/",
script: bookBeforeSearchTask,
top: true
},
{
match: "movie.douban.com",
script: movieTask,
top: true
},
{
match: "fm.douban.com",
script: musicTask,
top: true
}
];
function createLinkElement(template, keyword) {
let a = document.createElement("a");
a.href = template.replaceAll("%s", encodeURIComponent(keyword));
a.target = "_blank";
a.setAttribute("target", "_blank");
a.setAttribute("rel", "noopener");
a.setAttribute("referrerpolicy", "no-referrer");
return a;
}
function createElement(source, title, target, keyword) {
let span = document.createElement("span");
span.className = "pl";
span.append(document.createTextNode(source));
const a = createLinkElement(target, keyword ? keyword : title);
a.append(document.createTextNode(title));
let fragment = document.createDocumentFragment();
fragment.appendChild(span);
fragment.appendChild(a);
let br = document.createElement("br");
fragment.appendChild(br);
return fragment;
}
function createElements(list2) {
let fragment = document.createDocumentFragment();
for (let i = 0; i < list2.length; ++i) {
let item = list2[i];
let element = createElement(
item.description,
item.title,
item.template,
item.keyword
);
fragment.appendChild(element);
}
return fragment;
}
function movieTask() {
let node = document.querySelector("#content > h1 span:first-child");
if (!node)
return;
let title = node.innerText.trim();
let infoNode = document.querySelector("#info");
if (!infoNode)
return;
let list2 = [
{
description: "g(ddz):",
title,
keyword: title,
template: "https://www.google.com/search?newwindow=1&safe=strict&source=hp&ei=--05W5X6CoqD8wX_zZmwCg&btnG=Google+%E6%90%9C%E7%B4%A2&q=%s&oq=beego&gs_l=psy-ab.3..0j0i203k1l2j0j0i203k1j0i10i203k1j0i203k1l4.6311.7597.0.7829.9.6.0.0.0.0.132.522.2j3.6.0....0...1c.1.64.psy-ab..3.6.609.6..35i39k1j0i131k1j0i67k1j0i10k1.87.jNu-vssuoKQ".replace("%s", `%s ${encodeURIComponent("site:dandanzan.club")}`)
}
];
let fragment = createElements(list2);
infoNode.appendChild(fragment);
}
function readTask() {
waitForSelector(".article-profile-primary h1, .book-name").then((node) => {
waitForFunction(
() => node.innerText.trim() != ""
).then(() => updateTitle(node));
});
}
function updateTitle(node) {
const title = node.innerText.trim();
const search = { description: "z-lib:", template: "https://zh.singlelogin.re/s/%s" };
const a = createLinkElement(search.template, title);
const parentNode = node.parentNode;
if (parentNode == null)
return;
parentNode.insertBefore(a, node);
a.appendChild(node);
}
function findCodeText(node) {
const pl = node.querySelectorAll("span.pl");
const list2 = Array.from(pl);
for (let i = 0; i < list2.length; ++i) {
const item = list2[i];
const text = item.innerHTML;
if (text.includes("ISBN")) {
let code = item.nextSibling;
if (code) {
const value = code.nodeValue;
if (value) {
return value.trim();
}
}
}
}
return "";
}
function bookTask() {
let node = document.querySelector("#wrapper > h1");
if (!node)
return;
let infoNode = document.querySelector("#info");
if (!infoNode)
return;
let title = node.innerText.trim();
let code = findCodeText(infoNode);
let list2 = [
{ description: "z-lib:", title, template: "https://zh.singlelogin.re/s/%s", keyword: title },
{ description: "Annas-Archive:", title, template: "https://annas-archive.org/search?q=%s", keyword: code }
];
let fragment = createElements(list2);
infoNode.appendChild(fragment);
}
function bookBeforeSearchTask() {
const qs = new URLSearchParams(location.search);
const q = qs.get("q");
if (q == null)
return;
const key = "session";
const value = JSON.stringify({
name: q,
author: "",
lang: "",
publisher: "",
ext: "",
isbn: "",
id: ""
});
const old = localStorage.getItem(key);
localStorage.setItem(key, value);
if (old != value) {
location.reload();
}
}
function musicTask() {
const _send = XMLHttpRequest.prototype.send;
const _open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(_, url) {
_open.apply(this, arguments);
this.__url = url;
};
XMLHttpRequest.prototype.send = function() {
let t = this.onreadystatechange;
this.onreadystatechange = function(e) {
if (t)
t.call(this, e);
const readyState = this.readyState;
if (readyState === 4) {
const type = this.responseType || "text";
if (type === "text" && this.response) {
let data;
try {
data = JSON.parse(this.response);
} catch (e2) {
console.log("douban.fm.parse.response.failed");
}
if (data && data.song) {
const songs = data.song;
songs.forEach((song) => {
console.log(`%c${song.title}`, "color: red", song.url);
});
}
}
}
return this;
};
_send.apply(this, arguments);
return this;
};
}
function sleep(n) {
return new Promise(function(resolve) {
setTimeout(resolve, n * 1e3);
});
}
const log$1 = Log(false);
const dubokuTask = {
match: [
"k.jhooslea.com",
"k.rbaa.top",
/\w+\.duboku\.(?:io|fun|com|tv|top)/,
/duboku\.(?:io|fun|com|tv|top)/
],
script: task$2
};
let entered = false;
let pageButtonInit = false;
function insertMovie() {
let selector = ".myui-header__menu";
let links = document.querySelectorAll(`${selector} > li > a`);
let is = Array.from(links).some((link) => link.getAttribute("href") == "/vodtype/1.html");
if (is)
return;
let box = document.querySelector(selector);
if (box == null)
return;
let children = box.children;
let newNode = document.createElement("li");
newNode.classList.add("hidden-sm", "hidden-xs");
if (/(?:vodtype|vodshow)\/1(-.*)?\.html/i.test(location.href))
newNode.classList.add("active");
newNode.innerHTML = `<a href="/vodtype/1.html">电影</a>`;
if (children.length > 1)
box.insertBefore(newNode, children[1]);
}
function task$2() {
if (top == null)
return;
insertMovie();
let path = top.location.href;
if (/voddetail/i.test(path)) {
if (pageButtonInit)
return;
let doc = top.document;
let latest = doc.querySelector("#playlist1 li:last-child a");
let posNode = doc.querySelector(".myui-content__operate");
if (posNode && latest) {
let playNode = posNode.firstElementChild;
if (playNode == null)
return;
let newNode = playNode.cloneNode(true);
newNode.href = latest.href;
newNode.innerHTML = `<i class="fa fa-play"></i> 播放最新一集`;
newNode.className = `btn btn-primary`;
posNode.appendChild(newNode);
pageButtonInit = true;
}
return;
}
let sidebar = document.querySelector(".myui-sidebar");
if (sidebar) {
sidebar.style.display = "none";
let previous = sidebar.previousElementSibling;
if (previous) {
previous.style.cssText = "width: 100% !important";
}
}
let videos = document.querySelectorAll("video");
if (videos.length === 0) {
log$1("No video tag. nextTick");
if (top !== window) {
sleep(1).then(task$2);
}
return;
}
let video = Array.from(videos).find((video2) => video2.id === "playerCnt_html5_api");
if (video == null) {
log$1("No video tag matched. nextTick");
if (top !== window)
sleep(1).then(task$2);
return;
}
if (entered)
return;
entered = true;
let promise = video.play();
if (promise) {
promise.catch(() => {
let playButton = document.querySelector(".vjs-big-play-button");
if (playButton) {
playButton.click();
}
});
}
video.addEventListener("ended", function() {
log$1("enter video ended handler.");
log$1("去 top 页面查找下一集");
if (top == null)
return;
let links = top.document.links;
for (let i = 0; i < links.length; ++i) {
let link = links[i];
let text = link.innerText;
if (text.indexOf("下集") > -1) {
log$1(`next url: ${link.href}`);
if (link.href !== top.location.href) {
top.location.href = link.href;
break;
}
}
}
});
}
function createRedirectTask() {
return function() {
if (top !== window)
return;
let search = new URLSearchParams(location.search);
for (let value of search.values()) {
if (URL.canParse(value)) {
location.href = value;
}
}
};
}
const linkTask$1 = {
match: {
or: [
(url) => {
const u = new URL(url);
const pathname = u.pathname.toLowerCase();
const keywords = ["goto", "link", "redirect", "jump"];
return keywords.some((key) => pathname.includes(key));
},
new URLPattern({ "hostname": "(link|go).*" }),
new URLPattern({ "pathname": "/link*" })
]
},
script: createRedirectTask()
};
function stopImmediatePropagation() {
["click"].forEach((name) => {
document.addEventListener(name, function(e) {
e.stopImmediatePropagation();
}, true);
});
}
function createHiddenNodesBySelectorsTask(selectors2) {
return () => {
selectors2.forEach((selector) => {
let nodes = document.querySelectorAll(selector);
nodes.forEach((node) => {
if (node)
node.style.display = "none";
});
});
};
}
function removeRemovedAdBlockerModal() {
let elements = document.querySelectorAll("cloudflare-app");
Array.from(elements).forEach((element) => {
let style = window.getComputedStyle(element, null);
let zIndex = style.getPropertyValue("z-index");
if (+zIndex >= 400) {
element.style.display = "none";
}
});
}
function remove(node) {
let previous;
while (node && (previous = node.previousSibling)) {
previous.remove();
}
}
function useObserver(selector, options = { childList: true }) {
let observer;
return (handler2) => {
return (task2) => {
if (observer)
observer.disconnect();
observer = new MutationObserver(() => handler2(task2));
let node = document.querySelector(selector);
if (node)
observer.observe(node, options);
};
};
}
let fetchInit = false;
function fetchLocationPerMinute() {
fetch(location.href).finally(() => {
sleep(60).then(fetchLocationPerMinute);
});
}
function addEventListener(task2) {
let box = document.querySelector(".board-list");
if (box) {
let links = box.querySelectorAll("a");
Array.from(links).forEach((link) => {
link.setAttribute("target", "_blank");
});
const f = () => handler$1();
window.removeEventListener("hashchange", f);
window.addEventListener("hashchange", f);
} else {
sleep(0.2).then(() => addEventListener());
}
}
function handler$1(task2) {
addEventListener();
let body = document.body;
if (body)
body.style.cssText = "max-width: 1280px; margin: 0 auto;";
createHiddenNodesBySelectorsTask(["#ban_ner", "#banner_slider"])();
if (fetchInit == false) {
fetchInit = true;
fetchLocationPerMinute();
}
let style = document.createElement("style");
style.innerText = ".board-list tr { line-height: 2 } .board-list td { padding: 4px 0 } .title_9 a { font-size: 1.2em } .title_10 { width: 120px !important }";
let head = document.querySelector("head");
if (head)
head.appendChild(style);
}
const mysmthTask = {
match: [
"https://www.newsmth.net/",
"https://www.mysmth.net/"
],
script: useObserver("#body")(handler$1)
};
function nextTick(fn) {
return Promise.resolve().then(fn);
}
const log = Log(false);
const weChatTask = [
{
match: /https:\/\/mp\.weixin\.qq\.com\/s.*/,
script: task$1
},
{
match: /weixin110\.qq.com/,
script: linkTask
}
];
function linkTask() {
let node = document.querySelector("p");
if (node == null)
return;
let text = node.innerText.trim();
if (/https?:\/\//i.test(text)) {
location.href = text;
return;
}
}
function task$1() {
stopImmediatePropagation();
weChatTextToLink();
weChatQRCodeToLink();
logAudioLink();
}
function logAudioLink() {
let template = `https://res.wx.qq.com/voice/getvoice?mediaid=`;
sleep(1).then(() => {
if (typeof window == "undefined")
return;
if (typeof window.voiceList !== "undefined") {
let list2 = window.voiceList.voice_in_appmsg;
if (Array.isArray(list2)) {
list2.forEach((item, index) => {
let voice_id = item.voice_id;
let url = `${template}${voice_id}`;
log(`音频(${index + 1})地址: ${url}`);
});
}
}
});
}
let weChatQRCodeToLinkDone = false;
function weChatQRCodeToLink() {
if (weChatQRCodeToLinkDone)
return;
let nodes = document.querySelectorAll("span[data-src] img");
Array.from(nodes).forEach((node) => {
let a = document.createElement("a");
let p = node.parentNode;
let href = p.dataset.src;
if (href == null)
return;
a.href = href;
a.target = "_blank";
a.style.display = "block";
a.appendChild(document.createTextNode(href));
p.appendChild(a);
});
if (nodes.length) {
weChatQRCodeToLinkDone = true;
}
}
function weChatTextToLink() {
if (document.body == null) {
return sleep(0.2).then(() => {
nextTick(weChatTextToLink);
});
}
log("enter wechat textToLink func");
let walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
{
acceptNode: (node) => {
let p = node.parentNode;
while (p && ["A", "SCRIPT", "STYLE"].indexOf(p.nodeName) === -1) {
p = p.parentNode;
}
if (p)
return NodeFilter.FILTER_SKIP;
let text = node.nodeValue;
if (text && /https?:\/\//i.test(text)) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_REJECT;
}
}
);
let links = [];
while (walker.nextNode()) {
let node = walker.currentNode;
let text = node.nodeValue.toLowerCase();
let offset = text.indexOf("http");
let linkTextNode = node.splitText(offset);
if (linkTextNode == null)
continue;
if (linkTextNode.nodeValue == null)
continue;
let spaceOffset = linkTextNode.nodeValue.search(/\s|[))]/);
let linkNode = linkTextNode;
if (spaceOffset > -1) {
let t = linkTextNode.splitText(spaceOffset).previousSibling;
if (t)
linkNode = t;
}
let a = document.createElement("a");
if (linkNode.nodeValue)
a.href = linkNode.nodeValue;
a.setAttribute("target", "_blank");
linkNode.parentNode.insertBefore(a, linkNode);
a.appendChild(linkNode);
links.push(a);
}
if (links.length) {
suptolink(links);
}
return;
}
function suptolink(links) {
let sups = document.querySelectorAll("sup");
let lastFindedNode;
Array.from(sups).reverse().forEach((sup) => {
let text = sup.innerText.trim();
if (text === "")
return;
let link = findLinkByText(text, lastFindedNode || links[links.length - 1]);
if (link == null)
return;
lastFindedNode = link;
let a = document.createElement("a");
a.href = link.href;
a.setAttribute("target", "_blank");
sup.parentNode.insertBefore(a, sup);
a.appendChild(sup);
});
}
function findLinkByText(text, link) {
let find;
let node = link.previousSibling || link.parentNode;
while (node && (node.nodeType == 1 || node.nodeType === 3)) {
let t = node.innerText || node.nodeValue || "";
if (node.nodeType === 1) {
if (node.nodeName === "A")
link = node;
else {
let a = node.querySelector("a");
if (a)
link = a;
}
}
if (t.indexOf(text) > -1) {
find = link;
break;
}
node = node.previousSibling || node.parentNode;
}
return find;
}
function mutationObserver(meta, callback, selector, options = { childList: true }) {
if (meta == null)
return;
if (meta.observer) {
meta.observer.disconnect();
}
meta.observer = new MutationObserver(function() {
callback();
});
let node = document.querySelector(selector);
if (node) {
meta.observer.observe(node, options);
}
}
const zhihuTask = [
{
match: /^https?:\/\/.*?\.zhihu\.com/,
script: replaceExternalLinkTask,
exclude: "link.zhihu.com"
},
{
match: (url) => {
if ("URLPattern" in window) {
let p = new URLPattern("/people/*", "https://www.zhihu.com/");
return p.test(url);
}
return false;
},
script: removeReadQuestionsTask
}
];
function removeReadQuestionsTask() {
function handler2() {
let selector = "#Profile-activities .List-item";
let list2 = document.querySelectorAll(selector);
let max = 40;
if (list2.length > max) {
let index = list2.length - max;
let node = list2[index];
remove(node);
}
}
window.addEventListener("scroll", handler2);
}
function replaceExternalLinkTask({ meta }) {
function handler2() {
let selector = 'a[href^="https://link.zhihu.com/"]';
let links = document.querySelectorAll(selector);
Array.from(links).forEach((node) => {
let href = node.href;
let url = new URL(href);
let search = new URLSearchParams(url.search);
let target = search.get("target");
if (target) {
node.href = decodeURIComponent(target);
}
});
}
mutationObserver(meta, handler2, "body");
handler2();
}
const weiboTask = [
{
match: /(?:t|weibo)\.cn/i,
script: task
},
{
match: /wx\d+\.sinaimg\.cn/i,
script: navigateToRawImageTask
}
];
function task() {
let node = document.querySelector(".link,.desc,.navigate_link");
if (node == null)
return;
if (node.tagName === "A") {
location.href = node.getAttribute("href");
return;
}
let text = node.innerText.trim();
if (/https?:\/\//i.test(text)) {
location.href = text;
return;
}
}
function navigateToRawImageTask() {
let url = location.href;
if (url.includes("/oslarge/"))
return;
let i = new URL(url);
i.pathname = `/oslarge` + i.pathname.replace(/\/[^\/]*/, "");
let newUrl = i.toString();
location.href = newUrl;
}
function useInterval(n, handler2) {
return (task2) => {
const inner = () => {
handler2(task2);
setTimeout(() => {
var _a;
if (((_a = task2.meta) == null ? void 0 : _a.pause) == false) {
inner();
}
}, n * 1e3);
};
inner();
};
}
const hosts = [
"www.newsmth.net",
"www.mysmth.net"
];
function handler() {
const links = document.querySelectorAll("a");
Array.from(links).forEach((link) => {
if (link.protocol === "http:" && hosts.some((host) => link.host === host)) {
link.href = link.href.replace(/^http:/i, "https:");
}
});
}
const yurixuTask = {
match: "http://uz.yurixu.com/",
script: useInterval(0.2, handler)
};
const okJiKeTask = {
match: [
"https://cdn.jellow.site/",
"https://cdnv2.ruguoapp.com/"
],
script: function() {
let href = location.href;
if (href.includes("?")) {
location.href = href.replace(/\?.+$/, "");
}
}
};
function toRedirectUrl(raw, source, dest) {
const p = new URLPattern(source);
const result = p.exec(raw);
if (!result)
return "";
const pathname = result.pathname;
const groups = pathname.groups;
let url = dest;
Object.keys(groups).forEach((key) => {
url = url.replace(`:${key}`, groups[key]);
});
return url;
}
function initialize(url, list2) {
for (let i = 0; i < list2.length; ++i) {
const config = list2[i];
if (config.skip)
continue;
let destination = url;
if (config.pattern) {
destination = toRedirectUrl(url, config.source, config.destination || config.source);
} else if (url == config.source && config.destination) {
destination = config.destination;
}
if (url !== destination) {
return destination;
}
}
return url;
}
const list = [
{ source: "https://store.pailixiang.com/album/:album/:name.jpg" },
{ source: "https://www.baidu.com/", destination: "https://google.com/", pattern: false, skip: true }
];
const last = initialize(location.href, list);
const redirectTask = {
match: (url) => last !== url,
script: () => {
location.href = last;
}
};
var _GM_openInTab = /* @__PURE__ */ (() => typeof GM_openInTab != "undefined" ? GM_openInTab : void 0)();
var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)();
var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)();
function promisifyRequest(request) {
return new Promise((resolve, reject) => {
request.oncomplete = request.onsuccess = () => resolve(request.result);
request.onabort = request.onerror = () => reject(request.error);
});
}
function createStore(dbName, storeName) {
const request = indexedDB.open(dbName);
request.onupgradeneeded = () => request.result.createObjectStore(storeName);
const dbp = promisifyRequest(request);
return (txMode, callback) => dbp.then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName)));
}
let defaultGetStoreFunc;
function defaultGetStore() {
if (!defaultGetStoreFunc) {
defaultGetStoreFunc = createStore("keyval-store", "keyval");
}
return defaultGetStoreFunc;
}
function get(key, customStore = defaultGetStore()) {
return customStore("readonly", (store) => promisifyRequest(store.get(key)));
}
function set(key, value, customStore = defaultGetStore()) {
return customStore("readwrite", (store) => {
store.put(value, key);
return promisifyRequest(store.transaction);
});
}
function del(key, customStore = defaultGetStore()) {
return customStore("readwrite", (store) => {
store.delete(key);
return promisifyRequest(store.transaction);
});
}
function requestNotificationPermission() {
const p = Notification.permission;
if (p === "granted")
return Promise.resolve(true);
if (p === "denied")
return Promise.resolve(false);
return Notification.requestPermission().then((p2) => {
return Promise.resolve(p2 == "granted");
});
}
async function notify(message, options) {
const permission = await requestNotificationPermission();
if (!permission)
return;
if (!message)
return;
new Notification(message, options);
}
const idbKey = "directory";
const id = "photo-viewer";
const userSaveButtonClass = "user-save-btn";
function ping(image) {
const domain = "https://x181.cn";
const url = `${domain}/api/us/save?url=${encodeURIComponent(image)}`;
return _GM_xmlhttpRequest({
method: "GET",
url,
headers: {
"Content-Type": "application/json"
}
});
}
function createTwitterSaveImageOption() {
function imageUrlMatcher(node) {
const urlRegexp = /\/media\//;
return urlRegexp.test(node.src);
}
function siblingsMatcher(node, debug = false) {
const p = node.previousElementSibling;
if (p == null)
return false;
const style = p.style.backgroundImage;
const url = style.replace(/url\("(.*?)"\)/, "$1");
const src = node.src;
const match2 = url == src;
if (match2 && debug)
console.log("url: ", url);
return match2;
}
function toImageUrl(rawImageUrl, debug = false) {
const src = rawImageUrl;
debug && console.log("url.raw", src);
const tmp = src.replace(/&name=.*$/, "");
debug && console.log("url.name.remove", tmp);
const url = tmp + "&name=4096x4096";
debug && console.log("url.name.fix", url);
return url;
}
function getImageUrls(node, debug = false) {
const images = node.querySelectorAll("img");
const matchUrlList = Array.from(images).filter((node2) => imageUrlMatcher(node2));
const checkedUrlList = matchUrlList.filter((node2) => siblingsMatcher(node2, debug));
return checkedUrlList.map((node2) => toImageUrl(node2.src, debug));
}
function getSingleImageUrl(node, debug = false) {
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const elements = document.elementsFromPoint(x, y);
for (let i = 0; i < elements.length; ++i) {
const element = elements[i];
if (element.nodeType !== 1)
continue;
if (element.nodeName != "IMG")
continue;
const pos = node.compareDocumentPosition(element);
if (!(pos & Node.DOCUMENT_POSITION_CONTAINED_BY))
continue;
const el = element;
const m1 = imageUrlMatcher(el);
if (!m1)
continue;
const m2 = siblingsMatcher(el, debug);
if (!m2)
continue;
return toImageUrl(el.src, debug);
}
return null;
}
const tweetDialogSelector = '[role="dialog"]';
const tweetSelector = 'article[data-testid="tweet"]';
function injectSaveButton(group) {
const is = group.querySelector("[data-save]");
if (is)
return;
if (group == null)
return;
group.append(
createSaveButtonNode()
);
}
let cancelInjectAction;
function doInjectAction(target) {
if (cancelInjectAction)
cancelInjectAction();
const dialog = target.querySelector(tweetDialogSelector);
if (dialog) {
const s = () => {
const group = dialog.querySelectorAll(`[role="group"] [role="group"]`);
if (group.length) {
const last2 = group[group.length - 1];
injectSaveButton(last2);
cancelInjectAction();
}
};
cancelInjectAction = action(s);
}
const list2 = target.querySelectorAll(tweetSelector);
Array.from(list2).forEach((node) => {
const urls = getImageUrls(node);
if (urls.length === 0)
return;
const group = node.querySelector('[role="group"]');
injectSaveButton(group);
});
}
function tryInjectSaveButton(node) {
const cancel = action(() => {
doInjectAction(node);
});
const observer = new MutationObserver(() => {
doInjectAction(node);
cancel();
});
observer.observe(node, { childList: true, subtree: true });
}
return {
inject() {
const selectors2 = ["#react-root"];
selectors2.forEach((selector) => {
waitForSelector(selector).then(() => {
const node = document.querySelector(selector);
if (node == null)
return;
tryInjectSaveButton(node);
});
});
},
query(element) {
const selectors2 = ['[role="dialog"]', tweetSelector];
for (let i = 0; i < selectors2.length; ++i) {
const node = element.closest(selectors2[i]);
if (node == null)
continue;
let urls = [];
const debug = true;
const is = node.closest(tweetSelector);
if (is)
urls = getImageUrls(node, debug);
else {
const single = getSingleImageUrl(node, debug);
if (single)
urls = [single];
}
if (urls.length === 0)
continue;
return urls.map((url) => {
return {
url,
filename: url.replace(/.*?\/media\/(.*?)\?.*/, "$1")
};
});
}
return [];
}
};
}
function createXHSSaveImageOption() {
function doInjectAction() {
const selector = ".bottom-container";
waitForSelector(selector).then(() => {
const node = document.querySelector(selector);
if (node == null)
return;
const is = node.querySelector("[data-save]");
if (is)
return;
node.append(
createSaveButtonNode()
);
});
}
return {
query() {
const selector = ".swiper-wrapper";
const node = document.querySelector(selector);
if (node == null)
return [];
const images = node.querySelectorAll("img");
if (images.length == 0)
return [];
return Array.from(images).map((img) => {
const url = img.src;
return {
url,
filename: url.replace(/^.*\//, "")
};
});
},
inject() {
const observer = new MutationObserver(() => {
doInjectAction();
});
observer.observe(document.body, { childList: true, subtree: true });
}
};
}
const twitterTask = [
{
match: /(?:x|twitter)\.com/,
script: createSaveImageScript(
createTwitterSaveImageOption()
)
},
{
match: new URLPattern("/explore/:id?", "https://www.xiaohongshu.com"),
script: createSaveImageScript(
createXHSSaveImageOption()
)
}
];
function createSaveImageScript(option) {
return () => saveImageScript(option);
}
async function saveImageScript(option) {
document.addEventListener("click", (e) => {
const target = e.target;
if (target == null)
return;
const can = target.closest(`.${userSaveButtonClass}`);
if (!can)
return;
const images = option.query(target);
if (images.length === 0)
return;
trySaveImagesToLocale(images);
e.preventDefault();
e.stopImmediatePropagation();
}, true);
option.inject();
}
function createSaveButtonNode(cssText = "") {
const div = document.createElement("div");
div.dataset.save = "1";
div.classList.add(userSaveButtonClass);
div.style.cssText = `cursor: pointer;` + (cssText ? cssText : `align-self: center;`);
div.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" style="display: block; color: lightskyblue" width="28" height="28" viewBox="0 0 24 24"><path fill="currentColor" d="M20 7.423v10.962q0 .69-.462 1.153T18.384 20H5.616q-.691 0-1.153-.462T4 18.384V5.616q0-.691.463-1.153T5.616 4h10.961zm-8.004 9.115q.831 0 1.417-.582T14 14.543t-.582-1.418t-1.413-.586t-1.419.581T10 14.535t.582 1.418t1.414.587M6.769 9.77h7.423v-3H6.77z"/></svg>`;
return div;
}
async function onSelectFolder() {
const dirHandle = await window.showDirectoryPicker({ id, mode: "readwrite" });
await set(idbKey, dirHandle);
return dirHandle;
}
async function writeImage(item, dirHandler) {
const fileHandle = await get(item.url);
if (fileHandle) {
try {
const file = await fileHandle.getFile();
if (file)
return console.log(`url=${item.url} exists`);
} catch (e) {
if (e.code == DOMException.NOT_FOUND_ERR) {
console.log(`url=${item.url} file has removed, pls download regain`);
}
}
}
let filetype = "";
const image = await fetch(item.url).then((res) => {
const headers = res.headers;
const contentType = headers.get("Content-Type");
if (contentType)
filetype = contentType.replace(/image\//, "");
return res;
}).then((res) => res.blob());
const filename = item.filename + "." + filetype;
for await (let name of dirHandler.keys())
if (name == filename)
return;
const fileHandler = await dirHandler.getFileHandle(filename, { create: true });
const w = await fileHandler.createWritable();
await w.write(image);
await w.close();
await set(item.url, fileHandler);
}
async function onWriteImage(item, dirHandler) {
if (!item.url)
return;
ping(item.url);
await writeImage(item, dirHandler);
}
function onSaveImagesToLocal(list2, folder) {
return new Promise((resolve) => {
function save(list22, index = 0) {
if (list22.length === index) {
resolve(true);
return;
}
const p = onWriteImage(list22[index], folder);
p.finally(() => {
save(list22, index + 1);
});
}
save(list2);
});
}
async function trySaveImagesToLocale(images) {
let dirHandle = await get(idbKey);
if (!dirHandle)
dirHandle = await onSelectFolder();
const date = /* @__PURE__ */ new Date();
const dirname = `${date.getFullYear()}${String(date.getMonth() + 1).padStart(2, "0")}${String(date.getDate()).padStart(2, "0")}`;
let permission = true;
let folder = null;
try {
folder = await dirHandle.getDirectoryHandle(dirname, { create: true });
} catch (e) {
permission = false;
}
if (permission === false) {
await del(idbKey);
return trySaveImagesToLocale(images);
}
if (folder == null) {
console.log("no folder selected.");
return;
}
try {
await onSaveImagesToLocal(images, folder);
notify("Saved!");
} catch (e) {
console.log("save directory handle failed");
}
}
function action(fn) {
let timer = 0;
function run2() {
if (typeof fn === "function") {
try {
fn();
} catch (e) {
}
}
timer = setTimeout(() => {
run2();
}, 60);
}
run2();
return function cancel() {
if (timer) {
clearTimeout(timer);
timer = 0;
}
};
}
function* treeWalker(root = document.body, filter2 = () => NodeFilter.FILTER_ACCEPT) {
const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, filter2);
do {
yield walker.currentNode;
const shadowRoot = walker.currentNode.shadowRoot;
for (let child = shadowRoot ? shadowRoot.firstElementChild : walker.currentNode.firstElementChild; child; child = child.nextElementSibling) {
yield* treeWalker(child, filter2);
}
} while (walker.nextNode());
}
const otherTask = [
{
match: "https://www.cguardian.com/auctions/live-bidding",
meta: { pause: false },
script: useInterval(0.1, (task2) => {
for (let node of treeWalker()) {
if (node.nodeName == "VIDEO") {
node.setAttribute("controls", "");
task2.meta.pause = true;
break;
}
}
})
},
{
match: "https://helloacm.com/",
script: removeRemovedAdBlockerModal
},
{
match: "https://www.flickr.com/photos/",
script: createHiddenNodesBySelectorsTask([
".facade-of-protection-zoom"
])
},
{
match: true,
script: () => {
const tags = [
"html",
"body",
".grayTheme"
];
tags.forEach((tag) => {
const nodeList = document.querySelectorAll(tag);
nodeList.forEach((node) => {
node.style.filter = "initial";
});
});
}
},
{
match: true,
script: () => {
_GM_registerMenuCommand("telegram.share", function() {
const url = `https://t.me/share/url?url=${encodeURIComponent(top.location.href)}&text=${encodeURIComponent(top.document.title)}`;
_GM_openInTab(url, { active: true });
});
}
},
{
match: new URLPattern({ protocol: "https", hostname: "paulgraham.com" }),
script: function() {
const selectors2 = ["table", "td"];
selectors2.forEach((selector) => {
const elements = document.querySelectorAll(selector);
Array.from(elements).forEach((element) => {
if (element.hasAttribute("width")) {
element.removeAttribute("width");
}
});
});
}
}
];
const scripts = [
bingTask,
...doubanTask,
dubokuTask,
linkTask$1,
mysmthTask,
...otherTask,
...weiboTask,
...weChatTask,
yurixuTask,
...zhihuTask,
okJiKeTask,
redirectTask,
...twitterTask
// ...windTask,
];
core(scripts, location.href);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment