您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Creat and save 123pan instant links.
// ==UserScript== // @name 123FastLink // @namespace http://tampermonkey.net/ // @version 2025.7.25 // @description Creat and save 123pan instant links. // @author Baoqing // @author Chaofan // @author lipkiat // @match *://*.123pan.com/* // @match *://*.123pan.cn/* // @icon https://www.google.com/s2/favicons?sz=64&domain=123pan.com // @grant none // ==/UserScript== (function() { 'use strict'; // ----------------------------------------------------基础环境---------------------------------------------------- // ==================🚀 构建URL函数 ================== const buildURL = (host, path, queryParams) => { const queryString = new URLSearchParams(queryParams).toString(); return `${host}${path}?${queryString}`; }; // ==================🌐 发送请求函数 ================== async function sendRequest(method, path, queryParams, body) { const config = { host: 'https://' + window.location.host, queryParams: { // 🛡️ 预留的签名参数(可选) //'803521858': '1738073884-258518-2032310069' }, // 🔑 获取身份认证信息 authToken: localStorage['authorToken'], loginUuid: localStorage['LoginUuid'], appVersion: '3', referer: document.location.href, }; const headers = { 'Content-Type': 'application/json;charset=UTF-8', 'Authorization': 'Bearer ' + config.authToken, 'platform': 'web', 'App-Version': config.appVersion, 'LoginUuid': config.loginUuid, 'Origin': config.host, 'Referer': config.referer, }; try { const response = await fetch(buildURL(config.host, path, queryParams), { method: method, headers: headers, body: body, credentials: 'include' }); console.log(`[${response.status}] ${response.statusText}`); const data = await response.json(); console.table(data); // ✅ 表格化输出 if (data.code !== 0) { console.error('❗ 业务逻辑错误:', data.message); throw '❗ 业务逻辑错误:' + data.message; } return data; // ✅ 确保 sendRequest 返回 data } catch (error) { console.error('⚠️ 网络请求失败:', error); throw '未知错误'; return null; } } // ----------------------------------------------------生成秒传---------------------------------------------------- // ====================== 📂 获取文件信息 ================ async function getFileInfo(idList) { const transformedList = idList.map(fileId => ({ fileId })); const responseData = await sendRequest( "POST", "/b/api/file/info", {}, JSON.stringify({ // 请求体 fileIdList: transformedList }) ); return responseData; } // ===================== 获取选择的文件id ============= function getSelectFile() { const fileRow = Array.from(document.getElementsByClassName("ant-table-row ant-table-row-level-0 editable-row")); const selectFile = fileRow.map(function(element, index, array) { if (element.getElementsByTagName("input")[0].checked) { return element.getAttribute('data-row-key'); // 返回修改后的元素 } }).filter(item => item != null); return selectFile; } // ====================🔗 生成秒传链接 =================== async function creatShareLink() { const fileSelect = getSelectFile(); //console.log("fileS", fileSelect); if (!fileSelect.length) { return "" } const fileInfo = Array.from((await getFileInfo(fileSelect))['data']['infoList']); var hasFloder = 0; const shareLink = fileInfo.map(function(info, infoIndex, infoArray) { //.filter(item => item != null) if (info.Type == 0) { return [info.Etag, info.Size, info.FileName.replace("#", "").replace("$", "")].join('#'); } else { console.log("忽略文件夹", info.FileName); hasFloder = 1; } }).filter(item => item != null).join('\n'); if (hasFloder) { alert("文件夹无法秒传,将被忽略"); } return shareLink; } // ----------------------------------------------------接受秒传---------------------------------------------------- // ==================📥 参数解析 ==================== function getShareFileInfo(shareLink) { const shareLinkList = Array.from(shareLink.replace(/\r?\n/g, '$').split('$')); const shareFileInfoList = shareLinkList.map(function(singleShareLink, linkIndex, linkArray) { const singleFileInfoList = singleShareLink.split('#'); if (singleFileInfoList.length < 3) { return null; } const singleFileInfo = { etag: singleFileInfoList[0], size: singleFileInfoList[1], fileName: singleFileInfoList[2] }; return singleFileInfo; }); return shareFileInfoList; //JSON.parse(decodeURIComponent(atob(shareLink))); } // ================== 获取单一文件 =================== async function getSingleFile(shareFileInfo) { // --------------------- 文件信息 --------------------- const fileInfo = { driveId: 0, etag: shareFileInfo.etag, fileName: shareFileInfo.fileName, parentFileId: JSON.parse(sessionStorage['filePath'])['homeFilePath'][0] || 0, size: shareFileInfo.size, type: 0, duplicate: 1 }; // --------------------- 发送请求 --------------------- const responseData = await sendRequest('POST', '/b/api/file/upload_request', {}, JSON.stringify({...fileInfo, RequestSource: null })); return responseData; } // ================== 获取全部文件 =================== async function getFiles(shareLink) { try { const shareFileList = getShareFileInfo(shareLink); for (var i = 0; i < shareFileList.length; i++) { const fileInfo = shareFileList[i]; if (fileInfo === null) { continue; // 跳过空行 } getSingleFile(fileInfo); } return 1 } catch { return 0 } } // =================✨ 弹出操作框 ================ function showCopyModal(defaultText = "", buttonText = "复制", buttonFunction = copyToClipboard) { // 这个样式会遮挡,清除掉 const floatTable = document.getElementsByClassName('ant-table-header ant-table-sticky-holder'); if (floatTable.length > 0) { floatTable[0].className = "ant-table-header"; } // 检查是否已有样式,防止重复添加 if (!document.getElementById("modal-style")) { let style = document.createElement("style"); style.id = "modal-style"; style.innerHTML = ` .modal-overlay {display: flex;position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, 0.5);justify-content: center;align-items: center;z-index: 2; } .modal {background: white;padding: 20px;border-radius: 8px;box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);text-align: center;width: 300px;} .close-btn {background: #f44336;color: white;border: none;padding: 5px 10px;cursor: pointer;float: right;} .modal textarea {width: 90%;padding: 8px;margin: 10px 0;border: 1px solid #ccc;border-radius: 4px;resize: vertical;min-height: 100px;} .copy-btn {background: #4CAF50;color: white;border: none;padding: 8px 12px;cursor: pointer;border-radius: 4px;} `; document.head.appendChild(style); } // 如果已有弹窗,则删除它 let existingModal = document.getElementById('modal'); if (existingModal) existingModal.remove(); // 创建遮罩层 let modalOverlay = document.createElement('div'); modalOverlay.className = 'modal-overlay'; modalOverlay.id = 'modal'; // 创建弹窗 modalOverlay.innerHTML = ` <div class="modal"> <button class="close-btn" onclick="document.getElementById('modal').remove()">×</button> <h3>秒传链接</h3> <textarea id="copyText">${defaultText}</textarea> <button class="copy-btn" id="massageboxButton" onclick="${buttonFunction}()">${buttonText}</button> </div> `; // 绑定接受直链按钮事件 modalOverlay.querySelector('#massageboxButton').addEventListener('click', () => { buttonFunction(); }); // 添加到 body document.body.appendChild(modalOverlay); } // ===================📋 写入剪贴板 ==================== async function copyToClipboard() { try { const inputField = document.getElementById('copyText'); if (!inputField) { throw new Error('找不到输入框元素'); } // 使用现代 Clipboard API await navigator.clipboard.writeText(inputField.value); // 更新提示信息 alert('已成功复制到剪贴板 📋'); } catch (err) { console.error('复制失败:', err); alert(`复制失败: ${err.message || '请手动复制内容'}`); } } // ================== 获取文件 ==================== function startGetFile() { const shareLink = document.getElementById("copyText").value; if (getFiles(shareLink)) { //alert("获取成功,请刷新目录查看,如没有请检查根目录。"); // 如果已有弹窗,则删除它 let existingModal = document.getElementById('modal'); if (existingModal) existingModal.remove(); //模拟点击class="layout-operate-icon mfy-tooltip"的div下的svg元素 const element = document.querySelector('.layout-operate-icon.mfy-tooltip svg'); // 创建鼠标点击事件 const clickEvent = new MouseEvent('click', { bubbles: true, // 事件是否冒泡 cancelable: true, // 事件能否被取消 view: window // 关联的视图(通常是 window) }); // 分发事件到元素 element.dispatchEvent(clickEvent); } else { alert("获取失败"); } } // ================== 添加秒传按钮的主方法 ================== function addButton() { //判断是否已经存在按钮了 const buttonExist = document.querySelector('.mfy-button-container'); if (buttonExist) return; //判断是否在文件列表页面 const isFilePage = window.location.pathname == "/" && (window.location.search == "" || window.location.search.includes("homeFilePath")); if (!isFilePage) return; // 查找目标容器 const container = document.querySelector('.home-operator-button-group'); if (!container) return; // 创建按钮容器 const btnContainer = document.createElement('div'); btnContainer.className = 'mfy-button-container'; btnContainer.style.position = 'relative'; btnContainer.style.display = 'inline-block'; // 创建按钮 const btn = document.createElement('button'); btn.className = 'ant-btn css-dev-only-do-not-override-168k93g ant-btn-default ant-btn-color-default ant-btn-variant-outlined mfy-button create-button'; btn.style.background = "#4CAF50"; btn.style.color = "#fff"; btn.style.border = "none"; btn.innerHTML = ` <svg t="1753345987410" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2781" width="16" height="16"> <path d="M395.765333 586.570667h-171.733333c-22.421333 0-37.888-22.442667-29.909333-43.381334L364.768 95.274667A32 32 0 0 1 394.666667 74.666667h287.957333c22.72 0 38.208 23.018667 29.632 44.064l-99.36 243.882666h187.050667c27.509333 0 42.186667 32.426667 24.042666 53.098667l-458.602666 522.56c-22.293333 25.408-63.626667 3.392-54.976-29.28l85.354666-322.421333z" fill="#ffffff" p-id="2782"></path> </svg> <span>秒传</span> `; // 创建下拉菜单 const dropdown = document.createElement('div'); dropdown.className = 'mfy-dropdown'; dropdown.style.display = 'none'; dropdown.style.position = 'absolute'; dropdown.style.top = 'calc(100% + 5px)'; // 增加5px间距 dropdown.style.left = '0'; dropdown.style.backgroundColor = '#fff'; dropdown.style.border = '1px solid #d9d9d9'; dropdown.style.borderRadius = '10px'; // 圆角调整为10px dropdown.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)'; dropdown.style.zIndex = '1000'; dropdown.style.minWidth = '120px'; dropdown.style.overflow = 'hidden'; // 确保圆角效果 dropdown.innerHTML = ` <div class="mfy-dropdown-item" data-action="generate">生成秒传连接</div> <div class="mfy-dropdown-item" data-action="save">保存秒传连接</div> `; // 添加样式 const style = document.createElement('style'); style.textContent = ` .mfy-button-container:hover .mfy-dropdown { display: block !important; } .mfy-dropdown-item { padding: 8px 12px; cursor: pointer; transition: background 0.3s; font-size: 14px; } .mfy-dropdown-item:hover { background-color: #f5f5f5; } /* 添加下拉菜单顶部间距 */ .mfy-dropdown::before { content: ''; position: absolute; top: -5px; /* 填补间距 */ left: 0; width: 100%; height: 5px; background: transparent; } `; document.head.appendChild(style); // 组装元素 btnContainer.appendChild(btn); btnContainer.appendChild(dropdown); container.insertBefore(btnContainer, container.firstChild); // 添加下拉菜单点击事件 dropdown.querySelectorAll('.mfy-dropdown-item').forEach(item => { item.addEventListener('click', async function() { const action = this.dataset.action; if (action === 'generate') { // 这里添加生成秒传连接的具体逻辑 const shareLink = await creatShareLink(); if (shareLink == '') { alert("没有选择文件"); return } showCopyModal(shareLink); } else if (action === 'save') { // 这里添加保存秒传连接的具体逻辑 showCopyModal("", "获取", startGetFile); } dropdown.style.display = 'none'; }); }); // 鼠标事件处理 btnContainer.addEventListener('mouseenter', function() { dropdown.style.display = 'block'; }); btnContainer.addEventListener('mouseleave', function() { let timer; clearTimeout(timer); // 清除已有定时器避免冲突 timer = setTimeout(() => { dropdown.style.display = 'none'; }, 300); // 10毫秒延迟 }); } // 等待页面加载完成创建秒传按钮 window.addEventListener('load', addButton); // 下面的是为了监测地址栏发生变化时,重新添加秒传按钮,主要是为了解决当跳转到其他页面(如分享页)再跳转回全部文件页面时,秒传按钮不再加载的BUG // 虽然只要地址栏发生变化就会触发添加秒传按钮,但是在添加按钮的方法中判断了当前的页面是否是全部文件页面,只有在全部文件页面上才会继续添加 const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function () { originalPushState.apply(this, arguments); triggerUrlChange(); }; history.replaceState = function () { originalReplaceState.apply(this, arguments); triggerUrlChange(); }; // 监听浏览器前进/后退 window.addEventListener('popstate', triggerUrlChange); // 统一触发函数 function triggerUrlChange() { //此处必须有延迟,否则秒传按钮不会显示 setTimeout(addButton, 10); } })();