/** * 🚀 XIAOMI NOTES ULTIMATE EXPORTER (GEEK EDITION) * ------------------------------------------------ * @version 代码版本持续优化中 * @author Gemini Assistant * @features ConsoleUI, DarkMode, Search, Glassmorphism, SafeStop */ (async function() { // ========================================== // 1. 配置与工具 (CONFIG & UTILS) // ========================================== const CONFIG = { fileName: `小米笔记_极客版_${new Date().toISOString().slice(0,10)}.html`, delay: 350, // 请求间隔(ms),防止封控 batchLimit: 200 // 单次请求条数 }; // 全局状态 const STATE = { stopSignal: false, total: 0, current: 0, startTime: Date.now() }; // 极客风控制台工具 const UI = { banner: () => { console.clear(); console.log(`%c ███ ███ ██ ██ ███ ██ ██████ ████████ ███████ ████ ████ ██ ██ ████ ██ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██████ ██ ███████ `, "color: #FF6900; font-weight: bold;"); console.log(`%c :: GEEK EDITION :: %c 代码版本持续优化中 `, "background:#333;color:#fff;border-radius:3px;", "color:#888"); }, log: (msg, type = 'info') => { const colors = { info: '#00B0FF', success: '#00E676', warn: '#FFEA00', error: '#FF1744' }; const icon = { info: 'ℹ️', success: '✅', warn: '⚠️', error: '❌' }; console.log(`%c${icon[type]} [${new Date().toLocaleTimeString()}] %c${msg}`, `font-size:12px`, `color:${colors[type]}; font-family: monospace;`); }, progress: (current, total, detail = "") => { const percent = total > 0 ? Math.floor((current / total) * 100) : 0; const barLength = 20; const filled = Math.floor((percent / 100) * barLength); const bar = "█".repeat(filled) + "░".repeat(barLength - filled); console.log(`%c[${bar}] ${percent}% %c ${detail}`, "color: #FF6900", "color: #999"); } }; // 挂载屏幕上的“紧急停止”按钮 const createStopButton = () => { const btn = document.createElement("button"); Object.assign(btn.style, { position: "fixed", top: "15px", right: "15px", zIndex: "999999", background: "rgba(255, 59, 48, 0.9)", color: "white", backdropFilter: "blur(10px)", border: "none", borderRadius: "8px", padding: "10px 20px", fontFamily: "system-ui", fontWeight: "600", boxShadow: "0 4px 15px rgba(255,59,48,0.4)", cursor: "pointer", transition: "all 0.2s" }); btn.innerHTML = `🛑 停止并保存 (0)`; btn.onclick = () => { STATE.stopSignal = true; btn.innerHTML = "⏳ 正在打包数据..."; btn.style.background = "#8E8E93"; UI.log("用户触发停止信号,正在终止任务...", "warn"); }; document.body.appendChild(btn); return btn; }; const updateCounter = (num) => { const el = document.getElementById("xm_counter"); if(el) el.innerText = `(${num})`; }; const sleep = (ms) => new Promise(r => setTimeout(r, ms)); // ========================================== // 2. 核心逻辑 (CORE LOGIC) // ========================================== // 获取列表(支持分页与中断) async function fetchList() { let entries = []; let syncTag = ""; let more = true; let page = 1; UI.log("开始扫描笔记列表...", "info"); while (more && !STATE.stopSignal) { try { const url = `https://i.mi.com/note/full/page/?ts=${Date.now()}&limit=${CONFIG.batchLimit}${syncTag ? '&syncTag='+syncTag : ''}`; const res = await fetch(url).then(r => r.json()); if (res.data && res.data.entries) { const batch = res.data.entries; if (batch.length === 0) break; // 防死循环 entries = entries.concat(batch); syncTag = res.data.syncTag; UI.log(`Page ${page}: 扫描到 ${batch.length} 条索引`, "info"); updateCounter(entries.length); if (!syncTag) more = false; page++; await sleep(CONFIG.delay); } else { more = false; } } catch (e) { UI.log("列表获取中断: " + e.message, "error"); more = false; } } return entries; } // 获取详情(带进度条) async function fetchDetails(list) { const results = []; STATE.total = list.length; UI.log(`索引扫描完成,准备下载 ${STATE.total} 条笔记详情...`, "success"); for (let i = 0; i < list.length; i++) { if (STATE.stopSignal) break; try { const res = await fetch(`https://i.mi.com/note/note/${list[i].id}/?ts=${Date.now()}`).then(r => r.json()); const entry = res.data.entry; let title = "无标题笔记"; try { const extra = JSON.parse(entry.extraInfo); if(extra.title) title = extra.title; } catch (e) {} // 数据清洗 const content = entry.content || ""; // 简单的预览摘要 (取前50字) const snippet = content.replace(/<[^>]+>/g, "").slice(0, 30).replace(/\n/g, " ") + "..."; results.push({ id: list[i].id, title: title, date: list[i].createDate, content: content, folderId: entry.folderId }); STATE.current++; updateCounter(`${STATE.current}/${STATE.total}`); // 极客流式日志:每5条打印一次,或者最后一条 if (i % 5 === 0 || i === list.length - 1) { UI.progress(i + 1, list.length, `正在获取: ${title.slice(0,10)}`); } if (i % 20 === 0) await sleep(CONFIG.delay * 1.5); // 动态延时 } catch (e) { UI.log(`跳过损坏笔记 ID: ${list[i].id}`, "warn"); } } return results; } // ========================================== // 3. 生成器 (HTML GENERATOR) // ========================================== const generateHTML = (data) => { // 按时间倒序 data.sort((a, b) => b.date - a.date); const count = data.length; const dateStr = new Date().toLocaleDateString(); return `