您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
关键词 + 排序
// ==UserScript== // @name 豆包AI网盘增强 // @namespace https://www.doubao.com/ // @version 2025-08-07 // @description 关键词 + 排序 // @match https://www.doubao.com/drive/* // @match https://www.doubao.com/chat/drive/* // @grant GM_xmlhttpRequest // @connect doubao.com // @license MIT // ==/UserScript== (function () { "use strict"; // ===================== 样式注入 ===================== function injectCustomStyle() { const style = document.createElement("style"); style.textContent = ` .left-column-t5w32g { min-width: 480px !important; flex-grow: 0 !important; } #mySearchBoxWrapper { position: fixed; top: 80px; right: 10px; z-index: 999999; background: white; padding: 5px 10px; border: 1px solid #ccc; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); cursor: move; /* 鼠标样式改为可拖动 */ } `; document.head.appendChild(style); } // ===================== 工具函数 ===================== function formatSize(size) { if (!size) return "-"; if (size > 1 << 30) return (size / (1 << 30)).toFixed(2) + " GB"; if (size > 1 << 20) return (size / (1 << 20)).toFixed(2) + " MB"; if (size > 1 << 10) return (size / (1 << 10)).toFixed(2) + " KB"; return size + " B"; } function formatDuration(seconds) { if (!seconds) return "-"; const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); return `${h}h${m}m${s}s`; } function getFileExtension(name) { if (!name) return "-"; const parts = name.split("."); return parts.length > 1 ? parts.pop().toUpperCase() : "-"; } function getAiStatus(status) { switch (status) { case 0: return "未处理"; case 1: return "分析中"; case 2: return "完成"; case 3: return "失败"; default: return "未知"; } } function makeDraggable(element) { let isDragging = false; let offsetX = 0; let offsetY = 0; element.addEventListener("mousedown", (e) => { isDragging = true; offsetX = e.clientX - element.getBoundingClientRect().left; offsetY = e.clientY - element.getBoundingClientRect().top; element.style.transition = "none"; // 拖动时禁用动画 document.body.style.userSelect = "none"; // 禁止选中文字 }); document.addEventListener("mousemove", (e) => { if (!isDragging) return; const x = e.clientX - offsetX; const y = e.clientY - offsetY; element.style.left = x + "px"; element.style.top = y + "px"; element.style.right = "auto"; // 移除原来 right 的限制 }); document.addEventListener("mouseup", () => { if (isDragging) { isDragging = false; document.body.style.userSelect = ""; } }); } // ===================== 数据列渲染 ===================== function createRightColumn(text, options = {}) { const colWrapper = document.createElement("div"); colWrapper.className = "right-column-dCUG0O flex items-center"; if (options.extended) colWrapper.dataset.extended = "true"; const inner = document.createElement("div"); inner.className = "update-time-PWmLSY"; inner.textContent = text || "-"; if (options.minWidth) inner.style.minWidth = options.minWidth; colWrapper.appendChild(inner); return colWrapper; } // ===================== 显示隐藏的文件 ===================== function appendMissingRows(missFileList) { // ✅ 清除上一次自己插入的行 removeInjectedRows(); const container = document.querySelector(".children-wrapper-Ck8u3i") || document.querySelector("children-wrapper"); if (!container) return; missFileList.forEach((item) => { const id = item.id; const exists = container.querySelector( `[data-ai-space-file-item="row_${id}"]` ); if (!exists) { const row = createFileRow(item); container.appendChild(row); } }); } function renderFileRows(fileList, isMine) { const missFileList = []; waitForRows(3, 300) .then((rows) => { // 建立 rowMap: id => DOM 行 const rowMap = new Map(); rows.forEach((row) => { const container = row.closest("[data-ai-space-file-item]"); const rowId = container ?.getAttribute("data-ai-space-file-item") ?.replace("row_", ""); if (rowId) rowMap.set(rowId, row); }); fileList.forEach((item) => { if (!item?.id) return; const row = rowMap.get(item.id); if (row && (item.content_review_status === 1 || isMine)) { if (row.dataset.extended) return; row.dataset.extended = "true"; row.appendChild( createRightColumn(formatSize(item.size), { minWidth: "10px" }) ); row.appendChild( createRightColumn(formatDuration(item.content?.duration), { minWidth: "10px", }) ); // row.appendChild(createRightColumn(getFileExtension(item.name), { minWidth: '10px' })); row.appendChild( createRightColumn(getAiStatus(item.content?.ai_skill_status), { minWidth: "10px", }) ); } else { missFileList.push(item); } }); //appendMissingRows(missFileList); }) .catch((err) => { //console.warn('[文件行加载超时]', err); //resetListAsnFbtWithCustomContent(fileList); }); } // ===================== 等待文件行渲染 ===================== function waitForRows(maxRetries = 3, delay = 300) { return new Promise((resolve, reject) => { let attempts = 0; const tryFind = () => { const rows = document.querySelectorAll( ".file-row-wrapper-vFCVDk:not(.header-CvSd1B)" ); if (rows.length > 0) { return resolve(rows); } attempts++; if (attempts >= maxRetries) { return reject("文件行未渲染完成,超出最大重试次数"); } setTimeout(tryFind, delay); }; tryFind(); }); } // 插入搜索框 function insertSearchBox() { const containers = document.querySelectorAll(".container-upw8nU"); if (!containers.length) return; const lastContainer = containers[containers.length - 1]; const existing = document.getElementById("mySearchBoxWrapper"); if (existing) existing.remove(); const sortSelect = document.createElement("select"); sortSelect.id = "mySortSelect"; sortSelect.style.margin = "10px 10px 10px 0"; sortSelect.style.padding = "6px"; const options = [ { value: "name_asc", label: "文件名 升序" }, { value: "name_desc", label: "文件名 降序" }, { value: "time_asc", label: "修改时间 升序" }, { value: "time_desc", label: "修改时间 降序" }, ]; options.forEach((opt) => { const option = document.createElement("option"); option.value = opt.value; option.textContent = opt.label; sortSelect.appendChild(option); }); // 绑定change事件,触发排序和重新渲染 sortSelect.addEventListener("change", () => { const keywordInput = document.getElementById("mySearchBox"); const keyword = keywordInput ? keywordInput.value.trim().toLowerCase() : ""; const sortRule = sortSelect.value; // 重新触发数据过滤与渲染 if (window.__allFileData__) { const filtered = filterByKeyword({ children: window.__allFileData__ }); const sorted = sortFileList(filtered); renderFileRows(sorted, window.__isMine__); } }); const input = document.createElement("input"); input.type = "text"; input.placeholder = "请输入关键词"; input.style.cssText = "margin:10px 10px 10px 0;padding:6px;border:1px solid #ccc;border-radius:4px;"; input.id = "mySearchBox"; const button = document.createElement("button"); button.innerText = "执行"; button.style.cssText = "padding:6px 12px;cursor:pointer;"; button.onclick = () => { const val = input.value.trim(); //console.log("[关键词更新]", val); const allcontainers = document.querySelectorAll(".container-zLcYj3"); if (!allcontainers.length) return; const lastContainers = allcontainers[allcontainers.length - 1]; lastContainers.click(); }; const wrapper = document.createElement("div"); wrapper.id = "mySearchBoxWrapper"; wrapper.style.marginLeft = "auto"; wrapper.appendChild(sortSelect); wrapper.appendChild(input); wrapper.appendChild(button); lastContainer.parentNode.insertBefore(wrapper, lastContainer.nextSibling); } // 关键词过滤逻辑 function filterByKeyword(data) { const inputEl = document.getElementById("mySearchBox"); const keyword = inputEl?.value?.trim().toLowerCase(); if (!keyword) return data?.children || []; if (!data || !Array.isArray(data.children)) return []; return data.children.filter((item) => item?.name?.toLowerCase()?.includes(keyword) ); } /** * 根据指定字段对文件数组排序 * @param {Array} data - 文件数组 * @returns {Array} 排序后的数组(原地排序) */ function sortFileList(data) { if (!Array.isArray(data)) return data; const select = document.getElementById("mySortSelect"); if (!select) return data; const value = select.value; // 如 "name_asc" 或 "time_desc" const [field, order] = value.split("_"); // 分割为 "name" 和 "asc" const isAsc = order === "asc"; data.sort((a, b) => { let valA, valB; if (field === "name") { valA = a.name?.toLowerCase() || ""; valB = b.name?.toLowerCase() || ""; return isAsc ? valA.localeCompare(valB) : valB.localeCompare(valA); } if (field === "time") { valA = a.update_time || 0; valB = b.update_time || 0; return isAsc ? valA - valB : valB - valA; } return 0; }); return data; } // XHR 拦截 const originalOpen = XMLHttpRequest.prototype.open; const originalSend = XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open = function (...args) { this._method = args[0]; this._url = args[1]; this._async = args[2] !== false; return originalOpen.apply(this, args); }; XMLHttpRequest.prototype.send = function (body) { const xhr = this; const shouldIntercept = xhr._url && (xhr._url.includes("/samantha/aispace/share/node_info") || xhr._url.includes("/samantha/aispace/node_info")); if (!shouldIntercept) return originalSend.call(xhr, body); try { const requestData = JSON.parse(body || "{}"); const allChildren = []; let nextCursor = null; async function fetchAllPages() { let hasMore = true; let first = true; while (hasMore) { const reqPayload = { ...requestData }; if (!first && nextCursor) { reqPayload.cursor = nextCursor; } first = false; const res = await sendGMRequest(reqPayload); const json = JSON.parse(res.responseText); const children = json?.data?.children || []; const cursor = json?.data?.next_cursor; const more = json?.data?.has_more; allChildren.push(...children); //console.log(`📦 拉取了 ${allChildren.length} 条, next_cursor=${cursor}, has_more=${more}`); hasMore = more === true; nextCursor = cursor; } // ✅ 构造完整响应结构 const fakeResponse = { code: 0, msg: "", data: { node_info: {}, children: allChildren, next_cursor: null, has_more: false, }, }; const filtered = filterByKeyword(fakeResponse.data); if (filtered) { const sorted = sortFileList(filtered); fakeResponse.data.children = sorted; } renderFileRows(fakeResponse.data.children, true); // isMineFlag 根据请求类型传入 const jsonText = JSON.stringify(fakeResponse); Object.defineProperty(xhr, "responseText", { get: () => jsonText, }); Object.defineProperty(xhr, "status", { get: () => 200 }); Object.defineProperty(xhr, "readyState", { get: () => 4 }); if (typeof xhr.onreadystatechange === "function") xhr.onreadystatechange(); xhr.dispatchEvent(new Event("readystatechange")); xhr.dispatchEvent(new Event("load")); xhr.dispatchEvent(new Event("loadend")); } function sendGMRequest(payload) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: "POST", url: xhr._url, headers: { "Content-Type": "application/json", }, data: JSON.stringify(payload), onload: resolve, onerror: reject, }); }); } fetchAllPages().catch((err) => { //console.error("[分页拉取失败]", err); }); return; // 阻止原始 XHR 请求 } catch (e) { //console.warn("[XHR 拦截失败]", e); } return originalSend.call(xhr, body); // fallback }; // ===================== 页面加载完成后执行 ===================== function waitForElement(selector, callback) { const targetNode = document.body; const observer = new MutationObserver((mutations, obs) => { const el = document.querySelector(selector); if (el) { obs.disconnect(); // 停止观察 callback(el); } }); observer.observe(targetNode, { childList: true, subtree: true, }); } // ===================== 启动脚本 ===================== injectCustomStyle(); // 等待 .container-upw8nU 出现后插入搜索框 waitForElement(".container-upw8nU", () => { if (!document.getElementById("mySearchBoxWrapper")) { insertSearchBox(); makeDraggable(document.getElementById("mySearchBoxWrapper")); } }); })();