// ==UserScript==
// @name t66y助手
// @namespace com.t66y.jujufatu
// @author jujufatu
// @version 0.0.1
// @description 优化t66y网页使用体验
// @license CC BY-NC-ND 4.0
// @match http*://*t66y.com/*
// @grant GM_setClipboard
// @grant unsafeWindow
// @run-at document-start
// ==/UserScript==
//code start
(function () {
"use strict";
// 访问页面的原始window对象
var pageWindow = unsafeWindow;
// 去除无效图床,定义需要检查的域名列表
const BLOCK_IMG_LIST = [
"23img.com",
"bdimg.com",
"sinaimg.cn",
"ovkwiz.xyz",
"huluxia.com",
"imgs.moe",
];
// 替换显示图片:svg:过期图床
const BAD_IMG_SVG =
"data:image/svg+xml;charset=utf-8;base64,PHN2ZyB3aWR0aD0iMjAwcHgiIGhlaWdodD0iMjAwcHgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgICAgICAgICA8cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjMTBiMDE4IiAvPgogICAgICAgICAgPHRleHQgeD0iNTAlIiB5PSI3MS40IiBkb21pbmFudC1iYXNlbGluZT0ibWlkZGxlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjZjdjNTAwIiBmb250LXNpemU9IjUyIiBmb250LWZhbWlseT0iQXJpYWwiPjx0c3BhbiB4PSI1MCUiIGR5PSIwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ov4fmnJ88L3RzcGFuPjx0c3BhbiB4PSI1MCUiIGR5PSI1Ny4yIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7lm77luoo8L3RzcGFuPjwvdGV4dD48L3N2Zz4=";
// dom刚加载完成,图片脚本等其他资源还在加载中
document.addEventListener("DOMContentLoaded", function () {
// 热门异常图床图片替换,通常是头像
replaceImg();
// 找到每个楼层的磁力并替换
let floors = [...document.querySelectorAll(".tpc_content")];
floors.forEach((floor) => {
floor.innerHTML = createMagnetLink(floor.innerHTML);
});
// 初始评论,后续点击加载更多评论在loadComment中处理
let initComments = [...document.querySelectorAll(".post_cont")];
initComments.forEach((c) => {
c.innerHTML = createMagnetLink(c.innerHTML);
});
// 处理加载评论
if (typeof pageWindow.loadComment === "function") {
const originalLoadComment = pageWindow.loadComment;
pageWindow.loadComment = function () {
var data = pageWindow["comm" + arguments[0]];
for (var key in data) {
//----------修改的内容--------------
data[key].c = createMagnetLink(data[key].c);
//----------修改的内容--------------
}
originalLoadComment.apply(this, arguments); // 调用原始函数
};
}
});
// 将传入的字符中的磁力替换为a标签地址
function createMagnetLink(str) {
str = decodeURIComponent(str);
str = decodeHtmlEntities(str);
// 正则表达式匹配哈希值、可选的dn和tr参数
const magnetRegex =
/(?<!<[^>]*)(?:magnet:\?xt=urn:btih:)?([a-fA-F0-9]{40})(?:&dn=([^&\s]+))?(?:&tr=([^&\s]+))?/gi;
// 替换函数,用于生成a标签
function replaceFunc(match, hash, dn, tr, offset, string) {
const magnetPrefix = "magnet:?xt=urn:btih:";
const decodedDn = dn
? `磁力链接-${decodeURIComponent(dn)}`
: `磁力链接-${hash}`;
const trParam = tr ? `&tr=${encodeURIComponent(tr)}` : "";
const fullMatch = string.substring(
offset,
string.indexOf(" ", offset) > -1
? string.indexOf(" ", offset)
: string.length
);
// 判断并添加磁力链接前缀
let href = match.startsWith(magnetPrefix)
? fullMatch
: `${magnetPrefix}${hash}${
dn ? `&dn=${encodeURIComponent(dn)}` : ""
}${trParam}`;
href = href.replace(/\s+/g, "");
return `<a href="${href}">${decodedDn}</a> | <a style="cursor: pointer;" onclick="copy('${href}')">复制</a>`;
}
// 使用正则表达式替换符合条件的部分
return str.replace(magnetRegex, replaceFunc);
}
// 将失效图床的图片替换成默认头像
function replaceImg() {
// 初次进入页面直接遍历修改
// 获取所有img标签
const images = document.getElementsByTagName("img");
// 遍历每个img标签
for (const img of images) {
// 如果src符合条件
if (BLOCK_IMG_LIST.some((domain) => img.src.includes(domain))) {
img.src = BAD_IMG_SVG;
}
}
// 后续监听dom
const isBlockedSrc = (src) =>
BLOCK_IMG_LIST.some((blockedDomain) => src.includes(blockedDomain));
const replaceSrc = (node) => {
if (node.tagName === "IMG" && isBlockedSrc(node.src)) {
node.src = BAD_IMG_SVG;
}
};
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
// 检查和替换 node 自身(如果它是 img)
replaceSrc(node);
// 遍历所有子节点
node.querySelectorAll("img").forEach(replaceSrc);
}
});
} else if (
mutation.type === "attributes" &&
mutation.attributeName === "src"
) {
replaceSrc(mutation.target);
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ["src"],
});
}
// 解码html实体符号
function decodeHtmlEntities(str) {
return str
.replace(/"/g, '"')
.replace(/'/g, "'")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/ /g, " ")
.replace(/¡/g, "¡")
.replace(/¢/g, "¢")
.replace(/£/g, "£")
.replace(/¤/g, "¤")
.replace(/¥/g, "¥")
.replace(/¦/g, "¦")
.replace(/§/g, "§")
.replace(/¨/g, "¨")
.replace(/©/g, "©")
.replace(/ª/g, "ª")
.replace(/«/g, "«")
.replace(/¬/g, "¬")
.replace(/­/g, "")
.replace(/®/g, "®")
.replace(/¯/g, "¯")
.replace(/°/g, "°")
.replace(/±/g, "±")
.replace(/²/g, "²")
.replace(/³/g, "³")
.replace(/´/g, "´")
.replace(/µ/g, "µ")
.replace(/¶/g, "¶")
.replace(/·/g, "·")
.replace(/¸/g, "¸")
.replace(/¹/g, "¹")
.replace(/º/g, "º")
.replace(/»/g, "»")
.replace(/¼/g, "¼")
.replace(/½/g, "½")
.replace(/¾/g, "¾")
.replace(/¿/g, "¿")
.replace(/&/g, "&");
// 添加更多实体替换规则
// 注意: 如果增加新的替换规则,请确保不会互相冲突
// 例如,& 必须最后替换,以免在替换其他实体时干扰 & 符号
}
// 复制文本到剪切板
pageWindow.copy = function (str) {
GM_setClipboard(str);
Message({ msg: "复制成功", duration: 666 });
};
// Message 工具函数
function Message({ msg, duration }) {
// 创建消息元素
const messageElement = document.createElement("div");
// 设置消息文本
messageElement.textContent = msg;
// 设置消息样式
messageElement.style.position = "fixed";
messageElement.style.top = "66px";
messageElement.style.left = "50%";
messageElement.style.transform = "translate(-50%, -50%)";
messageElement.style.backgroundColor = "#10b018";
messageElement.style.color = "#f7c500";
messageElement.style.fontSize = "3rem";
messageElement.style.padding = "20px";
messageElement.style.boxShadow = "0 4px 8px rgba(0,0,0,0.1)";
messageElement.style.borderRadius = "10px";
messageElement.style.zIndex = "1000";
messageElement.style.transition = "opacity 0.5s, transform 0.5s";
messageElement.style.opacity = "0";
// 将消息元素添加到文档中
document.body.appendChild(messageElement);
// 动画效果:淡入
setTimeout(() => {
messageElement.style.opacity = "1";
messageElement.style.transform = "translate(-50%, -50%) scale(1)";
}, 10); // 使用setTimeout来确保元素已经插入DOM
// 设置显示时长
setTimeout(() => {
// 动画效果:淡出
messageElement.style.opacity = "0";
messageElement.style.transform = "translate(-50%, -50%) scale(0.9)";
// 动画完成后从文档中移除消息元素
setTimeout(() => {
document.body.removeChild(messageElement);
}, 500); // 与transition的时间一致
}, duration);
}
/*************************** 自定义样式 ***************************/
// 创建并插入样式
let style = document.createElement("style");
document.head.appendChild(style);
style.sheet.insertRule(
`.bad_bg { background-image: url("${BAD_IMG_SVG}") !important; }`,
0
);
})();