// ==UserScript== // @name 123Pan CV // @name:zh-CN 123云盘 CV // @namespace https://github.com/Chumor/123PanCV // @version 1.1.250829.1 // @description A lightweight Tampermonkey userscript for 123Pan. Automatically saves and manages extraction codes, displays them in a table, supports one-click copy, import/export, and Tampermonkey cloud sync. File/folder names are clickable to open share links. // @description:zh-CN 一个适用于 123云盘 的轻量级 Tampermonkey 用户脚本:自动保存提取码,表格化管理,一键复制,支持导入/导出与 Tampermonkey 云同步,文件/文件夹名可点击跳转到分享链接。 // @author Chumor // @license MIT // @homepage https://github.com/Chumor/123PanCV // @supportURL https://scriptcat.org/zh-CN/script-show-page/4094/issue // @match *://*.123pan.com/* // @match *://123pan.com/* // @match *://*.123684.com/* // @match *://123684.com/* // @grant GM_setClipboard // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue // @grant GM_deleteValue // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const STORAGE_KEY = 'PanCV_Data'; /** ========== 数据操作 ========== **/ function loadData() { const raw = GM_getValue(STORAGE_KEY, "[]"); return JSON.parse(raw); } function saveData(data) { GM_setValue(STORAGE_KEY, JSON.stringify(data)); } function addRecord(name, code) { code = code.replace(/^["']|["']$/g, ''); const shareKey = localStorage.getItem("shareKey")?.replace(/^"|"$/g, ""); const domain = location.hostname; const shareUrl = (shareKey && shareKey !== "undefined") ? `https://${domain}/s/${shareKey}` : location.href; let data = loadData(); // ⚡ 确保同一个 URL 只保存一个提取码 const existing = data.find(d => d.url === shareUrl); if (existing) { existing.name = name; existing.code = code; } else { data.push({ name, code, url: shareUrl }); } saveData(data); console.log('[PanCV] ✅ 保存:', name, code, shareUrl); } /** ========== 提取码 & 文件名查找 ========== **/ function findCodes() { const out = new Set(); ["SharePwd", "sharePwd", "share_code", "pwd"].forEach(k => { const v = localStorage.getItem(k) || sessionStorage.getItem(k); if (v) out.add(v.replace(/^["']|["']$/g, '')); }); document.cookie.split(';').forEach(c => { const kv = c.trim().split('='); if (kv.length === 2 && /^[A-Za-z0-9]{4}$/.test(kv[1])) { out.add(kv[1]); } }); document.querySelectorAll('input').forEach(el => { const v = (el.value || '').trim(); if (/^[A-Za-z0-9]{4}$/.test(v)) out.add(v); }); return [...out]; } function findFileNames() { const names = new Set(); document.querySelectorAll('a,div,span,li,p').forEach(el => { const t = (el.innerText || '').trim(); if (t && !/^分享文件/.test(t) && t.length < 200) { if ( /\.(txt|zip|rar|7z|mp4|mkv|avi|mp3|flac|jpg|jpeg|png|gif|pdf|docx?|xlsx?|pptx?)$/i.test(t) || el.style.whiteSpace === "nowrap" ) { names.add(t); } } }); return [...names]; } /** ========== 保存逻辑 ========== **/ function trySave() { const codes = findCodes(); let files = findFileNames(); if (!files.length && codes.length) files = ["未知文件"]; if (codes.length && files.length) { files.forEach(f => codes.forEach(c => addRecord(f, c))); } } /** ========== UI ========== **/ function showTable() { const data = loadData(); const overlay = document.createElement('div'); overlay.style.cssText = ` position: fixed; inset: 10% 5% auto 5%; background: #fff; z-index: 999999; border-radius: 12px; border: 1px solid #ccc; box-shadow: 0 4px 16px rgba(0,0,0,.2); padding: 14px 18px; max-height: 80%; overflow: auto; font-size: 14px; `; overlay.innerHTML = `
文件/文件夹名 | 提取码 |
---|---|
${d.name} | ${d.code} |
暂无记录 |