很久前社区某个大佬开发过一个脚本“CL社区收藏夹”,非常喜欢,后来停更了,插件失效。因本人不懂技术,无力,甚是想念该插件。昨日用gpt优化尝试,没想到可用,主要优化了图片下载,甚是喜爱这功能。
借此,再次呼唤原开发大佬,期待您的继续更新优化,原帖地址如下:
https://openuserjs.org/scripts/zhangsan/CL%E7%A4%BE%E5%8C%BA%E6%94%B6%E8%97%8F%E5%A4%B9顺便发个码,夹子绕行,新手请熟读版规,得码者跟帖回复下,否则举报取消。
c212*75*90*4a3a7 05-21 13:57
“*”隐藏3个相同字母,字母范围,前10
具体操作,将代码复制,打开油猴,点击 已安装脚本,随便一个脚本点击 操作下的 编辑,删除现有代码,把复制的代码粘贴到编辑器中,点击编辑器左上角保存,生成新的脚本,即可正常使用。
目前谷歌测试良好,
代码如下:
// ==UserScript==
// @name CL社区收藏夹 最终增强稳定版
// @namespace
http://tampermonkey.net/// @version 3.8
// @description 收藏 + 导入导出 + 仅下载1楼图片 + 顺序下载 + 正确楼主ID + 版块名命名
// @author niuhe + ChatGPT
// @match http*://*/htm_data/*
// @match http*://*/htm_mob/*
// @match http*://*/profile.php?action=favor
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_listValues
// @grant GM_setClipboard
// @grant GM_download
// @grant GM_deleteValue
// ==/UserScript==
(function () {
'use strict';
const url = location.href;
const isCollectionPage =
url.includes("profile.php?action=favor");
// =====================================
// 工具
// =====================================
function $(selector) {
return document.querySelector(selector);
}
function safeName(str) {
return str
.replace(/[\\/:*?"<>|]/g, "_")
.replace(/\s+/g, " ")
.trim();
}
function getText(selector, fallback = "") {
const el = $(selector);
return el
? el.textContent.trim()
: fallback;
}
function genKey(url) {
try {
return new URL(url).pathname;
} catch {
return url;
}
}
function getTagContainer() {
return $(".tiptop")
|| $(".guide")
|| $("h3")
|| document.body;
}
function addSeparator() {
const span =
document.createElement("span");
span.innerText = " | ";
getTagContainer()
.appendChild(span);
}
function createBtn(
text,
color = "#2F5FA1"
) {
const a =
document.createElement("a");
a.href = "javascript:void(0)";
a.innerText = text;
a.style.margin = "0 5px";
a.style.color = color;
a.style.cursor = "pointer";
a.style.fontWeight = "bold";
return a;
}
// =====================================
// 获取标题
// =====================================
function getTitle() {
let title =
getText("h4")
|| getText(".f18")
|| document.title;
title = title
.replace(" [复制链接]", "")
.trim();
return safeName(title);
}
// =====================================
// 获取楼主ID
// =====================================
function getAuthor() {
try {
const node =
document.querySelector("th b");
if (
node &&
node.textContent.trim()
) {
const username =
node.textContent.trim();
console.log(
"成功读取楼主:",
username
);
return safeName(username);
}
} catch (err) {
console.log(
"读取楼主失败:",
err
);
}
return "未知用户";
}
// =====================================
// 获取版块名称
// =====================================
function getForumName() {
try {
const links =
document.querySelectorAll('a[href*="thread"]');
for (const node of links) {
const text =
node.textContent.trim();
if (
text &&
text.length < 30 &&
!text.includes("版規") &&
!text.includes("返回")
) {
console.log(
"成功读取版块:",
text
);
return safeName(text);
}
}
} catch (err) {
console.log(
"读取版块失败:",
err
);
}
return "未知版块";
}
const title = getTitle();
const author = getAuthor();
const forumName = getForumName();
// =====================================
// 收藏数据
// =====================================
const saveUrl = url
.replace(location.host, "**")
.replace("htm_data", "htm_mob");
const key = genKey(saveUrl);
const content = JSON.stringify({
title,
author,
forumName,
url: saveUrl,
time: Date.now()
});
// =====================================
// 收藏按钮
// =====================================
function addFavoriteBtn() {
addSeparator();
const saved =
GM_getValue(key);
const btn = createBtn(
saved
? "已收藏"
: "收藏",
saved
? "#ff0000"
: "#2F5FA1"
);
btn.addEventListener("click", () => {
if (GM_getValue(key)) {
GM_deleteValue(key);
btn.innerText = "收藏";
btn.style.color = "#2F5FA1";
} else {
GM_setValue(key, content);
btn.innerText = "已收藏";
btn.style.color = "#ff0000";
}
});
getTagContainer()
.appendChild(btn);
}
// =====================================
// 导出
// =====================================
function exportCollection() {
addSeparator();
const btn =
createBtn("导出");
btn.addEventListener("click", () => {
const keys =
GM_listValues();
const data = [];
keys.forEach(k => {
data.push(
GM_getValue(k)
);
});
const blob = new Blob(
[data.join("\n")],
{
type: "text/plain"
}
);
const link =
document.createElement("a");
link.href =
URL.createObjectURL(blob);
link.download =
"favorite.dat";
link.click();
alert("导出成功");
});
getTagContainer()
.appendChild(btn);
}
// =====================================
// 导入
// =====================================
function importCollection() {
addSeparator();
const input =
document.createElement("input");
input.type = "file";
input.accept = ".dat";
input.style.width = "90px";
input.addEventListener("change", e => {
const file =
e.target.files[0];
if (!file) return;
const reader =
new FileReader();
reader.onload = () => {
const arr =
reader.result.split("\n");
arr.forEach(line => {
if (!line.trim()) return;
try {
const obj =
JSON.parse(line);
GM_setValue(
genKey(obj.url),
line
);
} catch {}
});
alert("导入成功");
};
reader.readAsText(file);
});
getTagContainer()
.appendChild(input);
}
// =====================================
// 下载图片(顺序下载)
// =====================================
async function downloadImages() {
addSeparator();
const btn =
createBtn("下载图片");
btn.addEventListener("click", async () => {
let firstFloor =
document.querySelector(".tpc_content")
|| document.querySelector(".tpc_cont");
if (!firstFloor) {
alert("未找到1楼正文");
return;
}
const imgList =
firstFloor.querySelectorAll("img");
let images = [];
imgList.forEach(img => {
let src =
img.getAttribute("ess-data")
|| img.getAttribute("data-src")
|| img.src;
if (!src) return;
// 排除头像/LOGO/GIF
if (
src.includes("face")
|| src.includes("avatar")
|| src.includes("logo")
|| src.includes("icon")
|| src.includes(".gif")
) {
return;
}
// 仅图片
if (
/\.(jpg|jpeg|png|webp)$/i
.test(src)
) {
images.push(src);
}
});
// 去重
images =
[...new Set(images)];
if (images.length === 0) {
alert("未找到图片");
return;
}
// 复制链接
GM_setClipboard(
images.join("\n")
);
// 文件夹名
const folderName =
`${author}_${forumName}_${title}`;
const ok = confirm(
`检测到 ${images.length} 张图片\n\n`
+ `保存目录:${folderName}\n\n`
+ `是否开始下载?`
);
if (!ok) return;
let i = 1;
// 顺序下载
for (const img of images) {
let ext = "jpg";
try {
ext = new URL(img)
.pathname
.split(".")
.pop();
} catch {}
const filename =
`${folderName}/${i}.${ext}`;
console.log(
"开始下载:",
filename
);
// 等待当前图片完成
await new Promise(resolve => {
GM_download({
url: img,
name: filename,
saveAs: false,
timeout: 60000,
onload: () => {
console.log(
"下载完成:",
filename
);
resolve();
},
onerror: (e) => {
console.log(
"下载失败:",
filename,
e
);
resolve();
},
ontimeout: () => {
console.log(
"下载超时:",
filename
);
resolve();
}
});
});
i++;
// 下载间隔
await new Promise(r =>
setTimeout(r, 1000)
);
}
alert("全部下载任务完成");
});
getTagContainer()
.appendChild(btn);
}
// =====================================
// 收藏页
// =====================================
function showCollectionPage() {
const keys =
GM_listValues();
const list = [];
keys.forEach(k => {
try {
const obj =
JSON.parse(
GM_getValue(k)
);
list.push(obj);
} catch {}
});
list.sort((a, b) =>
b.time - a.time
);
const html = list.map((item, index) => {
const realUrl = item.url
.replace("**", location.host)
.replace("htm_mob", "htm_data");
return `
<div style="
padding:12px;
border-bottom:1px solid #ddd;
">
<a href="${realUrl}"
target="_blank"
style="
font-size:16px;
color:#2F5FA1;
font-weight:bold;
">
${index + 1}. ${item.title}
</a>
<div style="
color:#666;
margin-top:6px;
font-size:13px;
">
${item.author}
</div>
</div>
`;
}).join("");
document.body.innerHTML = `
<div style="
max-width:900px;
margin:auto;
background:#fff;
">
<h2 style="
padding:15px;
">
本地收藏夹 (${list.length})
</h2>
${html}
</div>
`;
}
// =====================================
// 主逻辑
// =====================================
if (!isCollectionPage) {
addFavoriteBtn();
exportCollection();
importCollection();
downloadImages();
} else {
showCollectionPage();
}
console.log(
"CL社区收藏夹最终增强稳定版加载成功"
);
})();
赞(57)