吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 952|回复: 23
收起左侧

[其他原创] 更新1.3 搜索本站全部收藏帖子,以及检测更新。

[复制链接]
981930674 发表于 2026-4-27 15:31
本帖最后由 981930674 于 2026-4-27 19:38 编辑

油猴脚本,搜索全部收藏帖子,以及检测更新。(修改于原作者 ☆千年琥珀☆ https://www.52pojie.cn/forum.php?mod=viewthread&tid=2068863&highlight=%CA%D5%B2%D8)
修改:
- 本站范围内都可以使用
- 可以设置采集帖子的上限页数
- 添加删除按钮(我测试成功了,你们反馈给我是否成功)
- 添加更新收藏按钮,可以更新收藏时间

image.png

使用教程:根据"我的-收藏"页数先设置采集页数上限(默认是50页,而我的是15页如下图)
image.png

image.png



// ==UserScript==
// @name         52pojie全量收藏-右下角菜单+批量可正常启停+更新收藏时间
// @namespace    http://tampermonkey.net/
// @version      4.3
// @description  完美修复:真实删除 + 正确采集favid + 总数/上限显示 + 点击更新重置收藏时间
// @AuThor       专属定制
// @match        *://www.52pojie.cn/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      52pojie.cn
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // ========== 可自定义配置项 ==========
    const DEFAULT_MAX_PAGE = 50;
    // =================================

    function isFavoritePage() {
        return location.href.includes('mod=space') && location.href.includes('do=favorite');
    }

    let favCacheList = null;
    let isLoading = false;
    let stopCheck = false;
    let formhashCache = '';

    function getMaxPage() {
        const val = GM_getValue("favMaxPage", "");
        const num = parseInt(val);
        return isNaN(num) || num < 1 ? DEFAULT_MAX_PAGE : num;
    }

    function getFormhash() {
        if (formhashCache) return formhashCache;
        const hashInput = document.querySelector('input[name="formhash"]');
        if (hashInput) formhashCache = hashInput.value;
        return formhashCache || '';
    }

    try {
        const cacheStr = GM_getValue("favCacheData", "");
        if (cacheStr) favCacheList = JSON.parse(cacheStr);
    } catch (e) {
        favCacheList = null;
    }

    GM_addStyle(`
        #cfMenuWrap {position:fixed;bottom:20px;right:20px;z-index:10000;font-size:13px;}
        #cfMenuBtn {width:50px;height:50px;background:#007bff;color:#fff;border:none;border-radius:8px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.25);}
        #cfMenuBtn:hover {background:#0056b3;}
        #cfMenuList {display:none;position:absolute;bottom:58px;right:0;background:#fff;border-radius:6px;box-shadow:0 2px 12px rgba(0,0,0,0.2);overflow:hidden;min-width:160px;}
        #cfMenuList.show {display:block;}
        .cf-menu-item {padding:10px 12px;cursor:pointer;white-space:nowrap;border-bottom:1px solid #f0f0f0;}
        .cf-menu-item:last-child {border-bottom:none;}
        .cf-menu-item:hover {background:#f5f7fa;}
        .update-indicator {color:#ff0000;font-weight:bold;margin-left:5px;}
        .progress-container {position:fixed;bottom:90px;right:20px;width:220px;background:#fff;border-radius:5px;box-shadow:0 2px 10px rgba(0,0,0,0.2);padding:10px;z-index:9999;display:none;}
        .progress-bar {height:10px;background:#e9ecef;border-radius:5px;overflow:hidden;}
        .progress-fill {height:100%;background:#28a745;width:0%;transition:width 0.3s ease;}
        .progress-text {font-size:12px;margin-top:5px;text-align:center;}
        #favModal {position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90%;max-width:700px;max-height:75vh;background:#fff;border-radius:10px;box-shadow:0 4px 30px rgba(0,0,0,0.35);z-index:100001;display:none;overflow:hidden;}
        .modal-head {padding:12px 15px;background:#f8f9fa;border-bottom:1px solid #eee;display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap;}
        .modal-title {font-size:14px;font-weight:bold;color:#333;}
        .modal-close {color:#f56c6c;cursor:pointer;font-size:18px;font-weight:bold;}
        .head-btn {padding:4px 8px;border:none;border-radius:3px;cursor:pointer;font-size:12px;}
        #refreshFavBtn {background:#007bff;color:#fff;}
        #checkAllFavBtn {background:#dc3545;color:#fff;}
        #checkAllFavBtn.stop-bg {background:#6c757d;}
        #favSearchInput {width:calc(100% - 20px);margin:10px;padding:8px 12px;border:1px solid #007bff;border-radius:4px;outline:none;font-size:14px;background:#fff;}
        .modal-body {padding:10px;overflow-y:auto;max-height:calc(75vh - 120px);}
        .fav-item {padding:8px 10px;border-bottom:1px solid #f1f1f1;color:#0066cc;font-size:13px;display:flex;justify-content:space-between;align-items:center;gap:8px;}
        .fav-title {flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer;}
        .fav-update {color:red;font-weight:bold;}
        .fav-page-tag {color:#999;font-size:12px;white-space:nowrap;}
        .fav-btn {padding:2px 6px;color:#fff;border:none;border-radius:3px;font-size:12px;cursor:pointer;white-space:nowrap;margin-left:4px;}
        .fav-update-btn {background:#009688;}
        .fav-update-btn:hover {background:#00796b;}
        .fav-del-btn {background:#ff4444;}
        .fav-del-btn:hover {background:#cc0000;}
        .mask-layer {position:fixed;inset:0;background:rgba(0,0,0,0.4);z-index:100000;display:none;}
        .load-all-tip {text-align:center;padding:15px;color:#666;}
        .empty-tip {text-align:center;padding:30px;color:#999;}
    `);

    const menuWrap = document.createElement("div");
    menuWrap.id = "cfMenuWrap";
    menuWrap.innerHTML = `
        <button id="cfMenuBtn">收藏</button>
        <div id="cfMenuList">
            <div class="cf-menu-item" data-action="openFav">全部收藏列表</div>
            <div class="cf-menu-item" data-action="checkSingle">本页检测更新</div>
            <div class="cf-menu-item" data-action="setPageLimit">设置采集页数上限</div>
        </div>
    `;
    document.body.appendChild(menuWrap);

    const menuBtn = document.getElementById("cfMenuBtn");
    const menuList = document.getElementById("cfMenuList");
    const menuItems = menuList.querySelectorAll(".cf-menu-item");
    menuBtn.onclick = () => menuList.classList.toggle("show");
    function closeMenu() { menuList.classList.remove("show"); }

    function setPageLimit() {
        closeMenu();
        const now = getMaxPage();
        const input = prompt(`请输入收藏采集最大页数上限(当前:${now})`, now);
        if (input === null) return;
        const num = parseInt(input.trim());
        if (isNaN(num) || num < 1) {
            alert("请输入合法数字!");
            return;
        }
        GM_setValue("favMaxPage", num);
        alert(`设置成功!当前采集上限:${num} 页`);
    }

    const progressContainer = document.createElement('div');
    progressContainer.className = 'progress-container';
    progressContainer.innerHTML = `<div class="progress-bar"><div class="progress-fill"></div></div><div class="progress-text">准备中...</div>`;
    document.body.appendChild(progressContainer);

    function updateProgress(current, total, text) {
        const fill = progressContainer.querySelector('.progress-fill');
        const txt = progressContainer.querySelector('.progress-text');
        const pct = total ? Math.round((current / total) * 100) : 0;
        fill.style.width = pct + "%";
        txt.textContent = text || `${current}/${total} ${pct}%`;
    }
    function showProgress() { progressContainer.style.display = "block"; }
    function hideProgress() { progressContainer.style.display = "none"; }

    function parseChineseDate(str) {
        const m = str.match(/(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2})/);
        return m ? new Date(m[1], m[2] - 1, m[3], m[4], m[5]) : null;
    }

    function getLastEditDate(url) {
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: "GET", url: url, timeout: 8000,
                onload: res => {
                    const doc = new DOMParser().parseFromString(res.responseText, "text/html");
                    const s = doc.querySelector("i.pstatus");
                    if (!s) return resolve(null);
                    const m = s.textContent.match(/于\s+(\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{1,2})\s+编辑/);
                    resolve(m ? parseChineseDate(m[1]) : null);
                },
                onerror: () => resolve(null), ontimeout: () => resolve(null)
            });
        });
    }

    // ====================== 核心修复:正确采集 favid ======================
    function parseFavItem(item, pageNum) {
        const link = item.querySelector('a[href*="thread-"]');
        const dateDom = item.querySelector(".xg1");
        const favDate = dateDom ? parseChineseDate(dateDom.textContent.trim()) : null;

        // 从页面标签 id="fav_18081556" 提取真实收藏ID
        const favid = item.id?.replace("fav_", "") || "";

        if (!link || !favid) return null;
        const tid = link.href.match(/thread-(\d+)-/)?.[1] || "";

        return {
            title: link.title || link.textContent.trim(),
            url: link.href.startsWith("http") ? link.href : "https://www.52pojie.cn/" + link.href,
            favDate: favDate,
            update: false,
            page: pageNum,
            favid: favid,
            tid: tid
        };
    }

    // ====================== 真实删除接口 ======================
    async function deleteFavorite(favid, itemEl) {
        const formhash = getFormhash();
        if (!formhash || !favid) {
            alert("删除失败:formhash 或 favid 缺失");
            return false;
        }

        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: "POST",
                url: "https://www.52pojie.cn/home.php?mod=spacecp&ac=favorite&op=delete&favid=" + favid,
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded",
                    "Referer": "https://www.52pojie.cn/home.php?mod=space&do=favorite&view=me"
                },
                data: `deletesubmit=true&formhash=${formhash}&handlekey=a_delete_${favid}`,
                onload: function (res) {
                    if (res.status === 200) {
                        if (itemEl) itemEl.style.display = "none";
                        favCacheList = favCacheList.filter(x => x.favid !== favid);
                        GM_setValue("favCacheData", JSON.stringify(favCacheList));
                        resolve(true);
                    } else {
                        alert("删除失败:服务器返回异常");
                        resolve(false);
                    }
                },
                onerror: function () {
                    alert("删除失败:网络错误");
                    resolve(false);
                }
            });
        });
    }

    // ====================== 【核心】使用你抓包的收藏接口 ======================
    async function addFavorite(tid) {
        const formhash = getFormhash();
        if (!formhash || !tid) {
            alert("收藏失败:formhash 或 帖子ID 缺失");
            return false;
        }

        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://www.52pojie.cn/home.php?mod=spacecp&ac=favorite&type=thread&id=${tid}&formhash=${formhash}&infloat=yes&handlekey=k_favorite&inajax=1&ajaxtarget=fwin_content_k_favorite`,
                headers: {
                    "Referer": "https://www.52pojie.cn/home.php?mod=space&do=favorite&view=me"
                },
                onload: function (res) {
                    if (res.status === 200 && res.responseText.includes("收藏成功")) {
                        resolve(true);
                    } else {
                        alert("收藏失败:服务器异常");
                        resolve(false);
                    }
                },
                onerror: function () {
                    alert("收藏失败:网络错误");
                    resolve(false);
                }
            });
        });
    }

    // 单独采集第一页,获取最新收藏数据
    async function fetchFirstPage() {
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://www.52pojie.cn/home.php?mod=space&do=favorite&type=thread&page=1`,
                onload: res => {
                    const doc = new DOMParser().parseFromString(res.responseText, "text/html");
                    const items = doc.querySelectorAll('li[id^="fav_"]');
                    const list = [];
                    items.forEach(it => {
                        const obj = parseFavItem(it, 1);
                        if (obj) list.push(obj);
                    });
                    resolve(list);
                },
                onerror: () => resolve([])
            });
        });
    }

    // ====================== 【更新收藏时间】删除+重新收藏+置顶+改页数=1 ======================
    async function updateFavorite(item, itemEl) {
        if (!item.tid || !item.favid) {
            alert("帖子信息不完整,无法更新");
            return;
        }

        if (!confirm("确定要【更新收藏时间】吗?\n操作:删除旧收藏 → 重新收藏")) {
            return;
        }

        // 1. 删除旧数据
        const delOk = await deleteFavorite(item.favid, null);
        if (!delOk) return;
        await delay(800);

        // 2. 重新收藏
        const addOk = await addFavorite(item.tid);
        if (!addOk) return;

        alert("✅ 收藏时间更新成功!正在置顶本条...");

        // 3. 采集最新第一页,找到本条
        const newList = await fetchFirstPage();
        const newItem = newList.find(x => x.tid === item.tid);

        if (newItem) {
            // 4. 强制设置为第1页
            newItem.page = 1;
            newItem.update = false;

            // 5. 从缓存删除旧的 → 加到最前面
            favCacheList = favCacheList.filter(x => x.tid !== item.tid);
            favCacheList.unshift(newItem);

            // 保存缓存
            GM_setValue("favCacheData", JSON.stringify(favCacheList));
        }

        // 6. 刷新列表,本条自动置顶
        renderFavList();
    }

    function delay(ms) { return new Promise(r => setTimeout(r, ms)); }

    function resetCheckState() {
        stopCheck = false;
        isLoading = false;
        hideProgress();
        menuItems.forEach(item => {
            if (item.dataset.action === "checkSingle") item.textContent = "检测更新";
        });
    }

    function resetBatchBtn() {
        stopCheck = false;
        isLoading = false;
        checkAllBtn.innerText = "批量检测更新";
        checkAllBtn.classList.remove("stop-bg");
    }

    async function checkSingleUpdate() {
        closeMenu();
        if (!isFavoritePage()) {
            alert("请进入【我的-收藏】页面再使用");
            return;
        }
        const singleItem = [...menuItems].find(i => i.dataset.action === "checkSingle");
        if (singleItem.textContent === "停止检测") {
            stopCheck = true;
            updateProgress(0, 0, "已手动停止");
            setTimeout(resetCheckState, 600);
            return;
        }
        stopCheck = false;
        isLoading = true;
        singleItem.textContent = "停止检测";
        showProgress();
        try {
            const items = document.querySelectorAll('li[id^="fav_"]');
            const total = items.length;
            let cur = 0, upd = 0;
            for (const item of items) {
                if (stopCheck) break;
                cur++;
                const dDom = item.querySelector(".xg1");
                if (!dDom) continue;
                const favDate = parseChineseDate(dDom.textContent.trim());
                const link = item.querySelector('a[href*="thread-"]');
                if (!link || !favDate) continue;
                const url = link.href.startsWith("http") ? link.href : "https://www.52pojie.cn/" + link.href;
                updateProgress(cur, total, `检测:${cur}/${total}`);
                const editDate = await getLastEditDate(url);
                if (editDate && editDate > favDate) {
                    if (!dDom.nextElementSibling?.classList.contains("update-indicator")) {
                        const span = document.createElement("span");
                        span.className = "update-indicator";
                        span.innerText = "有更新";
                        dDom.after(span);
                    }
                    upd++;
                }
                await delay(700);
            }
            updateProgress(total, total, `完成:${upd}个有更新`);
            await delay(1500);
        } catch (e) { console.error(e); } finally {
            resetCheckState();
        }
    }

    const mask = document.createElement('div'); mask.className = "mask-layer"; document.body.appendChild(mask);
    const favModal = document.createElement('div');
    favModal.id = "favModal";
    favModal.innerHTML = `
        <div class="modal-head">
            <span class="modal-title">全部收藏列表</span>
            <div style="display:flex;gap:6px;">
                <button class="head-btn" id="refreshFavBtn">重新采集</button>
                <button class="head-btn" id="checkAllFavBtn">批量检测更新</button>
            </div>
            <span class="modal-close">×</span>
        </div>
        <input id="favSearchInput" placeholder="🔍 搜索全部收藏标题...">
        <div class="modal-body"></div>
    `;
    document.body.appendChild(favModal);

    const searchInput = favModal.querySelector("#favSearchInput");
    const refreshBtn = favModal.querySelector("#refreshFavBtn");
    const checkAllBtn = favModal.querySelector("#checkAllFavBtn");
    const closeBtn = favModal.querySelector(".modal-close");
    const modalBody = favModal.querySelector(".modal-body");
    const modalTitle = favModal.querySelector(".modal-title");

    function closeModal() { favModal.style.display = "none"; mask.style.display = "none"; }
    closeBtn.onclick = closeModal;
    mask.onclick = closeModal;

    async function goPage(page) {
        return new Promise(resolve => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://www.52pojie.cn/home.php?mod=space&do=favorite&type=thread&page=${page}`,
                onload: res => {
                    const doc = new DOMParser().parseFromString(res.responseText, "text/html");
                    resolve(doc);
                },
                onerror: () => resolve(null)
            });
        });
    }

    async function collectAllFav(forceRefresh = false) {
        if (isLoading) return favCacheList;
        if (favCacheList && !forceRefresh) return favCacheList;
        isLoading = true;
        let allList = [], page = 1;
        const maxPage = getMaxPage();
        modalBody.innerHTML = `<div class="load-all-tip">正在采集收藏...</div>`;
        while (true) {
            if (page > maxPage) break;
            const pageDoc = await goPage(page);
            if (!pageDoc) break;
            const items = pageDoc.querySelectorAll('li[id^="fav_"]');
            if (items.length === 0) break;
            modalBody.innerHTML = `<div class="load-all-tip">采集第 ${page} 页 / 上限${maxPage}页</div>`;
            items.forEach(item => {
                const res = parseFavItem(item, page);
                res && allList.push(res);
            });
            page++;
            await delay(400);
        }
        favCacheList = allList;
        GM_setValue("favCacheData", JSON.stringify(allList));
        isLoading = false;
        return favCacheList;
    }

    async function checkBatchUpdate() {
        closeMenu();
        if (checkAllBtn.innerText === "停止") {
            stopCheck = true;
            return;
        }
        if (isLoading || !favCacheList) {
            alert("请先加载收藏列表");
            return;
        }
        stopCheck = false;
        isLoading = true;
        checkAllBtn.innerText = "停止";
        checkAllBtn.classList.add("stop-bg");

        let upd = 0;
        for (let i = 0; i < favCacheList.length; i++) {
            if (stopCheck) break;
            const item = favCacheList[i];
            modalBody.innerHTML = `<div class="load-all-tip">批量检测:${i+1}/${favCacheList.length}</div>`;
            const editDate = await getLastEditDate(item.url);
            if (editDate && item.favDate && editDate > item.favDate) {
                item.update = true;
                upd++;
            }
            await delay(450);
        }

        if (!stopCheck) {
            renderFavList();
            alert(`批量检测完成:共 ${upd} 个有更新`);
        } else {
            modalBody.innerHTML = `<div class="load-all-tip">批量检测已手动停止</div>`;
        }
        resetBatchBtn();
    }

    checkAllBtn.onclick = checkBatchUpdate;

    // 渲染列表
    function renderFavList() {
        if (!favCacheList) return;
        const key = searchInput.value.trim().toLowerCase();
        const filterList = favCacheList.filter(item => item.title.toLowerCase().includes(key));

        const maxPage = getMaxPage();
        modalTitle.textContent = `全部收藏:${favCacheList.length} 条 / 上限:${maxPage} 页`;

        modalBody.innerHTML = "";
        if (filterList.length === 0) {
            modalBody.innerHTML = '<div class="empty-tip">无匹配收藏</div>';
            return;
        }
        filterList.forEach(item => {
            const div = document.createElement("div");
            div.className = "fav-item";

            div.innerHTML = `
                <div class="fav-title">
                    ${item.title}${item.update ? '<span class="fav-update">●有更新</span>' : ""}
                </div>
                <span class="fav-page-tag">第${item.page}页</span>
                <button class="fav-btn fav-update-btn">更新收藏</button>
                <button class="fav-btn fav-del-btn">删除</button>
            `;

            div.querySelector('.fav-title').onclick = () => window.open(item.url, "_blank");

            const updateBtn = div.querySelector('.fav-update-btn');
            updateBtn.onclick = (e) => {
                e.stopPropagation();
                updateFavorite(item, div);
            };

            const delBtn = div.querySelector('.fav-del-btn');
            delBtn.onclick = (e) => {
                e.stopPropagation();
                if (confirm('确定要删除该收藏吗?')) {
                    deleteFavorite(item.favid, div);
                }
            };

            modalBody.appendChild(div);
        });
    }

    async function openFavModal() {
        closeMenu();
        getFormhash();
        favModal.style.display = "block";
        mask.style.display = "block";
        searchInput.value = "";
        if (!favCacheList) await collectAllFav(false);
        renderFavList();
    }

    async function refreshCache() {
        closeMenu();
        if (isLoading) return;
        favCacheList = null;
        GM_setValue("favCacheData", "");
        await collectAllFav(true);
        renderFavList();
        alert("缓存已强制刷新");
    }

    menuItems.forEach(item => {
        item.onclick = function () {
            const act = this.dataset.action;
            switch (act) {
                case "checkSingle": checkSingleUpdate(); break;
                case "openFav": openFavModal(); break;
                case "setPageLimit": setPageLimit(); break;
            }
        };
    });

    refreshBtn.onclick = async function () {
        if (isLoading) return;
        await collectAllFav(true);
        renderFavList();
    };
    searchInput.addEventListener("input", renderFavList);

})();

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
wuai3456 + 1 + 1 谢谢@Thanks!
969510853 + 1 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 981930674 发表于 2026-4-27 15:54
风来也999 发表于 2026-4-27 15:52
// @match        *://*.www.52pojie.cn/home.php?*   
这条是不改下   或者说明下得进我的收藏页面才能看 ...

就是收藏页面才能看到按钮
风来也999 发表于 2026-4-27 15:56
风来也999 发表于 2026-4-27 15:52
// @match        *://*.www.52pojie.cn/home.php?*   
这条是不改下   或者说明下得进我的收藏页面才能看 ...

还有只能看到最近两百条   可我的收藏已经有120+页了
风来也999 发表于 2026-4-27 15:52
// @match        *://*.www.52pojie.cn/home.php?*   
这条是不改下   或者说明下得进我的收藏页面才能看到按钮
linglong2013 发表于 2026-4-27 15:51
这个能搜集排行榜吗
 楼主| 981930674 发表于 2026-4-27 15:53
linglong2013 发表于 2026-4-27 15:51
这个能搜集排行榜吗

哪个页面,截个图和还有网址
linglong2013 发表于 2026-4-27 15:55
就是这个网站的技术贴收藏数量排行榜试试看?
风来也999 发表于 2026-4-27 16:00
981930674 发表于 2026-4-27 15:54
就是收藏页面才能看到按钮

论坛限制只能看到最近50页的收藏
 楼主| 981930674 发表于 2026-4-27 16:04
风来也999 发表于 2026-4-27 15:56
还有只能看到最近两百条   可我的收藏已经有120+页了

已经修改了,你试试
lijinfeng 发表于 2026-4-27 17:03
坏了,才知道有收藏功能
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-4-29 07:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表