Greasy Fork

来自缓存

Greasy Fork is available in English.

Perplexity to Local/Notion

Save Perplexity library/search pages to local files and Notion

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Perplexity to Local/Notion
// @namespace    http://tampermonkey.net/
// @version      1.2.6
// @description  Save Perplexity library/search pages to local files and Notion
// @author       sandleft
// @match        https://www.perplexity.ai/*
// @run-at       document-end
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @connect      api.notion.com
// ==/UserScript==
!function(){"use strict";let e=!1,t=null,n=!1,i=null,r=new Set(Array.isArray(GM_getValue("hiyori_selected_keys",[]))?GM_getValue("hiyori_selected_keys",[]):[]);const o={get token(){return(GM_getValue("notion_token","")||"").trim()},get dbId(){return(GM_getValue("db_id","")||"").trim()},get count(){const e=parseInt(GM_getValue("save_count","12"));return isNaN(e)||e<=0?12:e},get fileType(){const e=GM_getValue("file_type","md");return"md"===e||"txt"===e?e:"md"},get propTitle(){return(GM_getValue("prop_title","Title")||"Title").trim()},get propUrl(){return(GM_getValue("prop_url","URL")||"URL").trim()},get propTags(){return(GM_getValue("prop_tags","Tags")||"Tags").trim()},get propTime(){return(GM_getValue("prop_time","Time")||"Time").trim()},get autoRun(){return GM_getValue("auto_run",!1)},get autoRunDelay(){const e=parseInt(GM_getValue("auto_run_delay","5"));return isNaN(e)||e<0?5:e}};function a(e){return(e||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function l(){return/^\/library(?:\/|$)/.test(window.location.pathname)}function c(){return/^\/search(?:\/|$)/.test(window.location.pathname)}function s(e){if(!e)return"";const t=String(e).trim().replace(/&amp;/g,"&"),n=u(t);if(n)return n;const i=t.match(/https?:\/\/www\.perplexity\.ai\/search(?:\/[^\s"'<>]*)?(?:\?[^\s"'<>]*)?|(?:^|[\s"'=])((?:\/search(?:\/[^\s"'<>]*)?(?:\?[^\s"'<>]*)?))/);return i?u((i[1]||i[0]).trim()):""}function u(e){if(!e)return"";try{const t=new URL(e,window.location.origin);return t.origin!==window.location.origin?"":/^\/search(?:\/|$)/.test(t.pathname)?(t.hash="",t.href):""}catch(e){return""}}function d(e){if(!e)return"";const t=["href","data-href","data-url","data-link","to"];for(const n of t){const t=s(e.getAttribute&&e.getAttribute(n));if(t)return t}const n=e.querySelector&&e.querySelector('a[href*="/search/"], [href*="/search/"], [data-href*="/search/"], [data-url*="/search/"], [data-link*="/search/"]');if(n)for(const e of t){const t=s(n.getAttribute&&n.getAttribute(e));if(t)return t}if(e.outerHTML&&e.outerHTML.length<6e3){const t=s(e.outerHTML);if(t)return t}return""}function h(e){return(e&&e.innerText||"").replace(/\s+/g," ").trim()}function p(e){return!(!e||!e.matches)&&e.matches(["input","button","select","textarea",'[role="checkbox"]',"[aria-checked]",'[data-state="checked"]','[data-state="unchecked"]'].join(", "))}function y(e,t){let n=e;for(;n&&n!==t&&n!==document.body;){if(p(n))return!0;n=n.parentElement}return!1}function f(e){const t=(e||"").replace(/\s+/g," ").trim();return!t||(!!/^\d+\s+selected\b/i.test(t)||(!(!/\b(Delete|Cancel)\b/i.test(t)||!/\bselected\b/i.test(t))||(!!/^(类型|临时问题|排序|显示|筛选|Filter|Sort)\b/i.test(t)||!!/^(Delete|Cancel|删除|取消)$/.test(t))))}function m(e){return e.url||e.titleKey||`idx:${e.originalIndex}`}function g(){document.querySelectorAll(".hiyori-checkbox").forEach(e=>{e.closest("#hiyori-panel")||e.remove()}),document.querySelectorAll(".hiyori-library-item").forEach(e=>{e.classList.remove("hiyori-library-item")})}function b(){const e=(document.body&&document.body.innerText||"").replace(/\s+/g," ");if(!/\d+\s+selected\b/i.test(e))return;const t=Array.from(document.querySelectorAll('button, [role="button"]')).find(e=>/^(Cancel|取消)$/.test(h(e)));t&&t.click?t.click():document.dispatchEvent(new KeyboardEvent("keydown",{key:"Escape",code:"Escape",bubbles:!0,cancelable:!0}))}function w(e,t,n,i,r){const o=function(e,t=!1){const n=document.querySelector("main")||document.body;let i=e;for(;i&&i!==n&&i!==document.body;){const e=(i.tagName||"").toLowerCase(),n=i.getAttribute&&i.getAttribute("role"),r=i.getBoundingClientRect?i.getBoundingClientRect():null,o=(i.innerText||"").trim().length>0;if(("row"===n||("li"===e||"article"===e)&&o||("listitem"===n||"link"===n||"button"===n)&&o||!n&&r&&r.width>=260&&r.height>=24&&r.height<=180&&o)&&(t||d(i)))return i;i=i.parentElement}return e.parentElement||e}(r,!0),a=d(o)||d(r),l=h(o),c=a?`url:${a}`:`row:${l}`;(function(e,t,n){if(!e||e===document.body||e===t)return!1;if(e.closest&&e.closest("#hiyori-panel, .hiyori-float-btn"))return!1;if(y(e,t))return!1;if(p(e))return!1;const i=h(e);if(f(i))return!1;if(n)return!0;const r=e.getBoundingClientRect?e.getBoundingClientRect():null,o=/(搜索|search|小时前|天前|分钟前|刚刚|hour|day|minute|ago)/i.test(i),a=/Mode:|Search the past|You are my|report in Chinese|Cover:/i.test(i);return!(i.length<24&&!o&&!a||r&&(r.width<260||r.height<24||r.height>180))})(o,i,a)&&(t.has(c)||n.has(o)||(t.add(c),n.add(o),e.push({url:a,originalIndex:e.length,item:o,titleKey:Array.from(l).slice(0,180).join("")})))}function x(){const e=document.querySelector("main")||document.body,t=Array.from(e.querySelectorAll(["a[href]",'[role="link"]','[role="row"]',"li","article",'[data-testid*="thread"]','[data-testid*="search"]','[data-testid*="library"]','[href*="/search/"]','[data-href*="/search/"]','[data-url*="/search/"]','[data-link*="/search/"]'].join(", "))),n=[],i=new Set,r=new Set;return t.forEach(t=>w(n,i,r,e,t)),n.length<2&&Array.from(e.querySelectorAll('div, [tabindex]:not([role="checkbox"]), [aria-label]:not(button):not(input)')).forEach(t=>w(n,i,r,e,t)),n}async function _(e=15e3){const t=Date.now()+e;let n=x();for(;0===n.length&&Date.now()<t;)await B(350),n=x();return n}function M(e=x()){g();const t=document.getElementById("hiyori-pick-list"),n=document.getElementById("hiyori-pick-meta");if(!t)return;if(!l())return t.innerHTML='<div class="hiyori-pick-row"><span class="hiyori-pick-title">请先打开 Library/历史页面</span></div>',void(n&&(n.textContent="可选列表仅在 Library 页面显示"));if(!e.length)return t.innerHTML='<div class="hiyori-pick-row"><span class="hiyori-pick-title">暂未识别到历史文章,请等待页面加载后点刷新</span></div>',void(n&&(n.textContent="识别到 0 条"));const i=e.slice(0,Math.max(o.count,30));t.innerHTML=i.map(e=>{const t=m(e),n=r.has(t)?"checked":"";return`\n                <label class="hiyori-pick-row">\n                    <input type="checkbox" class="hiyori-panel-checkbox"\n                           data-hiyori-key="${a(t)}" ${n}>\n                    <span class="hiyori-pick-title">${a(function(e){const t=(e&&e.titleKey||"").replace(/\s+/g," ").trim(),n=t.replace(/^搜索\s*/i,"").trim();return Array.from(n||t||`Library 第 ${e.originalIndex+1} 条`).slice(0,90).join("")}(e))}</span>\n                </label>\n            `}).join(""),t.querySelectorAll(".hiyori-panel-checkbox").forEach(t=>{t.addEventListener("change",()=>{t.checked?r.add(t.dataset.hiyoriKey):r.delete(t.dataset.hiyoriKey),GM_setValue("hiyori_selected_keys",Array.from(r).slice(0,200)),n&&(n.textContent=`识别到 ${e.length} 条,已勾选 ${r.size} 条`)})}),n&&(n.textContent=`识别到 ${e.length} 条,已勾选 ${r.size} 条`)}function k(e){return{url:e.url||"",libraryIndex:e.originalIndex,titleKey:e.titleKey||"",originalIndex:e.originalIndex}}function v(e,t,n={}){const i=e.getBoundingClientRect?e.getBoundingClientRect():null,r=i?i.left+Math.min(Math.max(i.width/2,8),Math.max(i.width-8,8)):0,o=i?i.top+Math.min(Math.max(i.height/2,8),Math.max(i.height-8,8)):0,a=Object.assign({bubbles:!0,cancelable:!0,view:window,button:0,buttons:t.includes("down")?1:0,clientX:r,clientY:o},n),l=t.startsWith("pointer")&&window.PointerEvent?window.PointerEvent:window.MouseEvent;e.dispatchEvent(new l(t,a))}async function G(e){const t=function(e){if(!e||!e.item)return null;const t=e.item,n=t.querySelector&&t.querySelector('a[href*="/search/"], [data-href*="/search/"], [data-url*="/search/"], [data-link*="/search/"]');if(n&&!p(n))return n;return Array.from(t.querySelectorAll("span, p, div")).find(e=>{const n=h(e),i=e.getBoundingClientRect?e.getBoundingClientRect():null;return n.length>=12&&!f(n)&&!y(e,t)&&i&&i.width>=80&&i.height>=10})||(t.matches&&t.matches('a[href], [role="link"], [role="button"]')&&!p(t)?t:t.querySelector&&t.querySelector('a[href], [role="link"], [role="button"], [tabindex]:not([role="checkbox"])')||t)}(e);if(!t)return!1;const n=window.location.href;t.scrollIntoView({block:"center",inline:"nearest"}),await B(150);const i=function(e){if(!e||!e.getBoundingClientRect||!document.elementFromPoint)return e;const t=e.getBoundingClientRect(),n=t.left+Math.min(Math.max(.35*t.width,120),Math.max(t.width-12,12)),i=t.top+Math.min(Math.max(t.height/2,8),Math.max(t.height-8,8)),r=document.elementFromPoint(n,i);return!r||r!==e&&!e.contains(r)||p(r)?e:r}(t);try{v(i,"pointerdown",{pointerId:1,pointerType:"mouse",isPrimary:!0}),v(i,"mousedown"),v(i,"pointerup",{pointerId:1,pointerType:"mouse",isPrimary:!0}),v(i,"mouseup"),v(i,"click")}catch(e){console.warn("[妃爱] 模拟鼠标事件失败,改用原生 click:",e),t.click&&t.click()}for(let e=0;e<20;e++){if(window.location.href!==n||c())return!0;await B(200)}t.click&&t.click();for(let e=0;e<15;e++){if(window.location.href!==n||c())return!0;await B(200)}return!!e.url&&(window.location.href=e.url,!0)}async function T(e){b(),await B(200);const t=await _();M(t);const n=function(e,t){if(!Array.isArray(e)||!t)return null;if(t.url){const n=e.find(e=>e.url===t.url);if(n)return n}if(t.titleKey){const n=e.find(e=>e.titleKey===t.titleKey);if(n)return n}return Number.isInteger(t.libraryIndex)&&e[t.libraryIndex]?e[t.libraryIndex]:null}(t,e);return!!n&&G(n)}function V(){if(!l()||!o.autoRun||GM_getValue("hiyori_running",!1))return;if(t)return;const e=o.autoRunDelay;console.log(`[妃爱] 已开启自动抓取,${e} 秒后启动...`),t=setTimeout(()=>{t=null,l()&&!GM_getValue("hiyori_running",!1)&&(console.log("[妃爱] 自动抓取已启动!"),N(!1))},1e3*e)}function I(){t&&(clearTimeout(t),t=null)}function E(){if(document.getElementById("hiyori-panel"))return;const e=document.createElement("div");e.id="hiyori-panel",e.style.display=GM_getValue("panel_show","none"),e.innerHTML=`\n            <div class="hiyori-title">🌸 P2L/N 🌸</div>\n\n            <label class="hiyori-label">Notion 密钥 (Token)</label>\n            <input id="h-token" class="hiyori-input" type="password"\n                   placeholder="secret_..." value="${a(o.token)}">\n\n            <label class="hiyori-label">数据库 ID</label>\n            <input id="h-dbid" class="hiyori-input"\n                   placeholder="输入数据库ID..." value="${a(o.dbId)}">\n\n            <details style="margin: 6px 0;">\n                <summary>▸ Notion 字段名设置(默认通常不用改)</summary>\n                <label class="hiyori-label">标题字段名(Notion 默认库为 Name)</label>\n                <input id="h-prop-title" class="hiyori-input"\n                       placeholder="Title" value="${a(o.propTitle)}">\n                <label class="hiyori-label">链接字段名</label>\n                <input id="h-prop-url" class="hiyori-input"\n                       placeholder="URL" value="${a(o.propUrl)}">\n                <label class="hiyori-label">标签字段名</label>\n                <input id="h-prop-tags" class="hiyori-input"\n                       placeholder="Tags" value="${a(o.propTags)}">\n                <label class="hiyori-label">日期字段名</label>\n                <input id="h-prop-time" class="hiyori-input"\n                       placeholder="Time" value="${a(o.propTime)}">\n            </details>\n\n            <label class="hiyori-label">自动保存数量</label>\n            <input id="h-count" class="hiyori-input" type="number"\n                   min="1" value="${o.count}">\n\n            <label class="hiyori-label">本地保存格式</label>\n            <select id="h-filetype" class="hiyori-select">\n                <option value="md"  ${"md"===o.fileType?"selected":""}>Markdown (.md)</option>\n                <option value="txt" ${"txt"===o.fileType?"selected":""}>纯文本</option>\n            </select>\n\n            <label class="hiyori-label">\n                <input type="checkbox" id="h-auto-run" ${o.autoRun?"checked":""}\n                       style="margin-right: 5px; vertical-align: middle; accent-color: #ff69b4;">\n                进入 Library 自动开始抓取\n            </label>\n            <div style="display: flex; align-items: center; gap: 8px; margin-top: 5px;">\n                <label class="hiyori-label" style="margin: 0;">延时</label>\n                <input id="h-auto-delay" class="hiyori-input" type="number" min="0" max="300"\n                       value="${o.autoRunDelay}" style="width: 70px;">\n                <span style="font-size: 12px; color: #ff69b4;">秒后启动</span>\n            </div>\n\n            <button id="h-save-config"    class="hiyori-btn">保存设置 ✨</button>\n            <hr style="border: 0.5px solid #ffb6c1; margin: 10px 0;">\n            <button id="h-batch-auto"     class="hiyori-btn">自动倒序抓取最新文章 🚀</button>\n            <button id="h-refresh-list"   class="hiyori-btn">刷新面板可选列表 🔄</button>\n            <div id="hiyori-pick-meta" class="hiyori-pick-meta">可选列表加载中...</div>\n            <div id="hiyori-pick-list"></div>\n            <button id="h-batch-selected" class="hiyori-btn">保存面板勾选文章 🎯</button>\n            <button id="h-stop" class="hiyori-btn"\n                    style="background:#ff9999; display:none;">紧急停止 🛑</button>\n        `,document.body.appendChild(e);const t=document.createElement("div");t.className="hiyori-float-btn",t.innerHTML="🌸",t.onclick=()=>{const t="none"===e.style.display?"block":"none";e.style.display=t,GM_setValue("panel_show",t)},document.body.appendChild(t),document.getElementById("h-save-config").onclick=()=>{const e=parseInt(document.getElementById("h-count").value),t=parseInt(document.getElementById("h-auto-delay").value);GM_setValue("notion_token",document.getElementById("h-token").value.trim()),GM_setValue("db_id",document.getElementById("h-dbid").value.trim()),GM_setValue("prop_title",document.getElementById("h-prop-title").value.trim()||"Title"),GM_setValue("prop_url",document.getElementById("h-prop-url").value.trim()||"URL"),GM_setValue("prop_tags",document.getElementById("h-prop-tags").value.trim()||"Tags"),GM_setValue("prop_time",document.getElementById("h-prop-time").value.trim()||"Time"),GM_setValue("save_count",String(isNaN(e)||e<=0?12:e)),GM_setValue("file_type",document.getElementById("h-filetype").value),GM_setValue("auto_run",document.getElementById("h-auto-run").checked),GM_setValue("auto_run_delay",String(isNaN(t)||t<0?5:t)),i=null,alert("宝宝!设置已经牢牢记住辣!"),I(),V()},document.getElementById("h-batch-auto").onclick=()=>N(!1),document.getElementById("h-batch-selected").onclick=()=>N(!0),document.getElementById("h-refresh-list").onclick=()=>M(),document.getElementById("h-stop").onclick=()=>A(!0),GM_getValue("hiyori_running",!1)&&(document.getElementById("h-stop").style.display="block"),$(),M(),V()}function $(){if(!c())return;if(document.getElementById("hiyori-single-btn"))return;const e=document.createElement("button");e.id="hiyori-single-btn",e.className="hiyori-btn",e.innerHTML="🌸 保存当前页",e.onclick=()=>q(!0).catch(e=>console.error("[妃爱] 单篇保存出错:",e)),document.body.appendChild(e)}GM_addStyle("\n        #hiyori-panel {\n            position: fixed; top: 5%; right: 20px; width: 290px;\n            max-height: 88vh; overflow-y: auto;\n            background: #fff0f5; border: 2px solid #ffb6c1;\n            border-radius: 15px; z-index: 10000; padding: 15px;\n            font-family: 'Microsoft YaHei', sans-serif;\n            box-shadow: 0 4px 15px rgba(255,182,193,0.4);\n            scrollbar-width: thin; scrollbar-color: #ffb6c1 #fff0f5;\n        }\n        .hiyori-title { color: #ff69b4; font-weight: bold; text-align: center; margin-bottom: 10px; font-size: 16px; }\n        .hiyori-label { font-size: 12px; color: #ff69b4; font-weight: bold; margin-top: 5px; display: block; }\n        .hiyori-input, .hiyori-select {\n            width: 100%; padding: 6px; margin: 3px 0 8px 0;\n            border: 1px solid #ffb6c1; border-radius: 5px;\n            font-size: 12px; box-sizing: border-box;\n        }\n        .hiyori-btn {\n            width: 100%; padding: 8px; margin-top: 5px;\n            background: #ffb6c1; color: white; border: none;\n            border-radius: 20px; cursor: pointer; font-weight: bold; transition: 0.3s;\n        }\n        .hiyori-btn:hover { background: #ff69b4; transform: scale(1.02); }\n        .hiyori-float-btn {\n            position: fixed; bottom: 30px; right: 30px;\n            width: 60px; height: 60px; background: #ffb6c1; color: white;\n            border-radius: 50%; border: 4px solid #fff; cursor: pointer;\n            z-index: 9999; font-size: 30px;\n            display: flex; align-items: center; justify-content: center;\n            box-shadow: 0 4px 10px rgba(0,0,0,0.2);\n        }\n        #hiyori-pick-list { max-height: 180px; overflow-y: auto; margin-top: 6px; border: 1px solid #ffb6c1; border-radius: 8px; background: rgba(255,255,255,0.55); }\n        .hiyori-pick-row { display: flex; gap: 6px; align-items: flex-start; padding: 6px; border-bottom: 1px solid rgba(255,182,193,0.45); font-size: 11px; color: #7a2b52; cursor: pointer; }\n        .hiyori-pick-row:last-child { border-bottom: none; }\n        .hiyori-pick-row input { margin-top: 2px; accent-color: #ff69b4; flex: 0 0 auto; }\n        .hiyori-pick-title { line-height: 1.35; overflow-wrap: anywhere; }\n        .hiyori-pick-meta { font-size: 11px; color: #b5477a; margin-top: 6px; }\n        #hiyori-single-btn {\n            position: fixed; bottom: 100px; right: 30px; width: 120px;\n            z-index: 9998; box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n        }\n        #hiyori-panel details > summary {\n            color: #ff69b4; font-size: 12px; cursor: pointer; font-weight: bold;\n        }\n    ");let S=location.href;function A(e=!1){if(GM_setValue("hiyori_running",!1),GM_setValue("hiyori_queue",[]),e){const e=GM_getValue("hiyori_failures",[]);Array.isArray(e)&&e.length>0?alert(`🛑 已停止!以下文章处理失败或写入 Notion 失败:\n${e.join("\n")}`):alert("🛑 已经乖乖停下啦!")}GM_setValue("hiyori_failures",[]);const t=document.getElementById("h-stop");t&&(t.style.display="none")}async function N(e){if(!l())return void alert("宝宝,请先打开 Perplexity 的 Library/历史页面再批量抓取哦!");if(b(),await B(200),GM_getValue("hiyori_running",!1)&&!confirm("检测到已有批量任务运行中,是否强制覆盖?"))return;const t=await _();M(t);const n=[],i=new Set;if(e?t.forEach(e=>{const t=m(e);r.has(t)&&!i.has(t)&&(i.add(t),n.push(k(e)))}):t.slice(0,o.count).forEach(e=>{const t=m(e);i.has(t)||(i.add(t),n.push(k(e)))}),0===n.length)return void alert(e?"宝宝,面板列表里还没有勾选文章哦!请点“刷新面板可选列表”后在面板内勾选。":"宝宝,没有找到文章呀,请确认 Library/历史页面已经加载完毕哦!");n.reverse(),GM_setValue("hiyori_queue",n),GM_setValue("hiyori_running",!0),GM_setValue("hiyori_failures",[]);const a=document.getElementById("h-stop");a&&(a.style.display="block"),L()}async function L(){if(!GM_getValue("hiyori_running",!1))return;if(n)return;const e=GM_getValue("hiyori_queue",[]);if(!Array.isArray(e))return console.error("[妃爱] 队列数据损坏,已静默重置"),A(!1),void alert("⚠️ 队列数据异常,已自动重置,请重新开始。");if(e.length>0){const t=e.shift();if(GM_setValue("hiyori_current_tag_index",t.originalIndex),GM_setValue("hiyori_queue",e),Number.isInteger(t.libraryIndex)){if(!l())return e.unshift(t),GM_setValue("hiyori_queue",e),void(window.location.href="https://www.perplexity.ai/library");n=!0;const i=await T(t);return n=!1,void(i||(console.error("[妃爱] 无法通过模拟点按打开 Library 条目:",t),R(t.titleKey||`Library 第 ${t.originalIndex+1} 条`),setTimeout(()=>L(),700)))}t.url&&t.url===window.location.href?window.location.reload():t.url?window.location.href=t.url:(console.error("[妃爱] 队列条目缺少可打开的信息,已跳过:",t),R(t.titleKey||`Library 第 ${t.originalIndex+1} 条`),setTimeout(()=>L(),700))}else{GM_setValue("hiyori_running",!1);const e=GM_getValue("hiyori_failures",[]);GM_setValue("hiyori_failures",[]),Array.isArray(e)&&e.length>0?alert(`🌸 批量完成!但以下文章处理失败或写入 Notion 失败:\n${e.join("\n")}`):alert("🌸 宝宝!所有的素材都已经完美归档啦!"),window.location.href="https://www.perplexity.ai/library"}}function B(e){return new Promise(t=>setTimeout(t,e))}function C(e,t){return Array.from(e).slice(0,t).join("")}function R(e,t=""){const n=GM_getValue("hiyori_failures",[]);Array.isArray(n)&&(n.push(t?`${e}(${t})`:e),GM_setValue("hiyori_failures",n))}async function q(t=!1){if(e)console.warn("[妃爱] 已在处理中,跳过重复调用");else{e=!0;try{const e=await async function(e=15e3,t=1500){const n=Date.now()+e;let i=-1,r=0;for(;Date.now()<n;){const e=document.querySelector("main")||document.body,n=(e.innerText||"").length;if(n>200)if(n===i){if(0===r&&(r=Date.now()),Date.now()-r>=t)return e}else r=0,i=n;await B(400)}return document.querySelector("main")||document.body}();if(!t&&!GM_getValue("hiyori_running",!1))return;const n=await async function(e=8e3){const t=new Set(["","Perplexity","Perplexity AI","New Thread","Ask anything","Untitled","新建对话","新线程","新しいスレッド","新しい質問","새 스레드"]),n=Date.now()+e;for(;Date.now()<n;){const e=document.title.replace(/ - Perplexity/g,"").trim();if(e&&!t.has(e))return e;await B(300)}return document.title.replace(/ - Perplexity/g,"").trim()||"未命名对话"}();if(!t&&!GM_getValue("hiyori_running",!1))return;const i=window.location.href,r=new Set;e.querySelectorAll('a[href^="http"]').forEach(e=>{e.href&&!e.href.includes("perplexity.ai")&&r.add(e.href)});const a=Array.from(e.querySelectorAll("img")).filter(e=>(e.currentSrc||e.dataset.src||e.src||"").startsWith("http")&&(e.naturalWidth>50||!!e.dataset.src)),l=(e.innerText||"").trim();if(!l)return console.warn("[妃爱] 页面内容为空,跳过:",n),t&&alert("宝宝,页面内容还没加载出来哦,请稍后再试!"),void(t||L());const c=r.size>0?"\n\n---\n### 🌸 引用来源 (Sources)\n"+Array.from(r).map((e,t)=>`[${t+1}] ${e}`).join("\n"):"",s=l+c+(a.length>0?"\n\n---\n### 🌸 提取的附图资源\n"+a.map(e=>{const t=e.currentSrc||e.dataset.src||e.src;return"md"===o.fileType?`![图片](<${t}>)`:`[图片链接]: ${t}`}).join("\n\n"):"");let u="综合";if(!t){const e=GM_getValue("hiyori_current_tag_index",0);u=e<4?"经济":e<10?"政治军事":"综合"}!function(e,t,n){const i=C(e.replace(/[/\\:*?"<>|]/g,"_"),40),r="md"===n?"text/markdown":"text/plain",o=URL.createObjectURL(new Blob([t],{type:r+";charset=utf-8"})),a=document.createElement("a");a.href=o,a.download=`${i}.${n}`,document.body.appendChild(a),a.click(),document.body.removeChild(a),setTimeout(()=>URL.revokeObjectURL(o),1e4)}(n,s,o.fileType),o.token&&o.dbId?await U(n,i,s,u,t):t?alert("宝宝,只帮您保存了本地文件哦!Notion 配置未填写。"):L()}catch(e){console.error("[妃爱] processCurrentPage 出错:",e),!t&&GM_getValue("hiyori_running",!1)&&L()}finally{e=!1}}}function j(){return{Authorization:`Bearer ${o.token}`,"Content-Type":"application/json","Notion-Version":"2022-06-28"}}function D(e){if(!e)return"";try{const t=JSON.parse(e);return t.message||t.code||e.slice(0,160)}catch(t){return e.slice(0,160)}}function P(e,t,n,i=[]){if(e[t]&&e[t].type===n)return t;for(const t of i)if(e[t]&&e[t].type===n)return t;const r=t.toLowerCase(),o=Object.keys(e).find(t=>t.toLowerCase()===r&&e[t].type===n);return o||(Object.keys(e).find(t=>e[t].type===n)||"")}async function z(){if(i)return i;const e=await(t="GET",n=`https://api.notion.com/v1/databases/${encodeURIComponent(o.dbId)}`,new Promise((e,i)=>{GM_xmlhttpRequest({method:t,url:n,timeout:2e4,headers:j(),data:r?JSON.stringify(r):void 0,onload:e,onerror:i,ontimeout:()=>i(new Error("Notion 请求超时"))})}));var t,n,r;if(e.status<200||e.status>=300)throw new Error(D(e.responseText)||`读取数据库失败 ${e.status}`);const a=JSON.parse(e.responseText||"{}").properties||{},l=P(a,o.propTitle,"title",["Name","Title","标题","名称"]);if(!l)throw new Error("数据库里没有 title 类型字段");return i={title:l,url:P(a,o.propUrl,"url",["URL","Url","Link","链接","来源"]),tags:P(a,o.propTags,"multi_select",["Tags","Tag","标签","分类"]),time:P(a,o.propTime,"date",["Time","Date","日期","时间"])},i}function U(e,t,n,i,r,a=0){return new Promise(l=>{const c=Array.from(n).length>188100,s=function(e,t){const n=Array.from(e),i=[];for(let e=0;e<n.length;e+=t)i.push(n.slice(e,e+t).join(""));return i}(n,1900).slice(0,99).map(e=>({object:"block",type:"paragraph",paragraph:{rich_text:[{type:"text",text:{content:e}}]}})),u=new Date,d=[u.getFullYear(),String(u.getMonth()+1).padStart(2,"0"),String(u.getDate()).padStart(2,"0")].join("-");z().then(u=>{const h=function(e,t,n,i,r){const o={};return o[e.title]={title:[{type:"text",text:{content:C(t,100)}}]},e.url&&(o[e.url]={url:n}),e.tags&&(o[e.tags]={multi_select:[{name:i}]}),e.time&&(o[e.time]={date:{start:r}}),o}(u,e,t,i,d);GM_xmlhttpRequest({method:"POST",url:"https://api.notion.com/v1/pages",timeout:2e4,headers:j(),data:JSON.stringify({parent:{database_id:o.dbId},properties:h,children:s}),onload(o){if(429===o.status&&a<3){const o=8e3*(a+1);return console.warn(`[妃爱] Notion 限流,${o/1e3}s 后重试(第 ${a+1} 次)`),void setTimeout(()=>U(e,t,n,i,r,a+1).then(l),o)}o.status<200||o.status>=300?(console.error("[妃爱] Notion 写入失败:",o.status,o.responseText),R(e,D(o.responseText)||`Notion ${o.status}`)):c&&console.warn("[妃爱] 内容超限,已截断至约 18.6 万字符:",e),l(),!r&&GM_getValue("hiyori_running",!1)&&L()},onerror(t){console.error("[妃爱] Notion 网络错误:",t),R(e,"Notion 网络错误"),l(),!r&&GM_getValue("hiyori_running",!1)&&L()},ontimeout(){console.error("[妃爱] Notion 请求超时,已跳过:",e),R(e,"Notion 请求超时"),l(),!r&&GM_getValue("hiyori_running",!1)&&L()}})}).catch(t=>{console.error("[妃爱] Notion 字段识别失败:",t),R(e,t&&t.message?t.message:"Notion 字段识别失败"),l(),!r&&GM_getValue("hiyori_running",!1)&&L()})})}new MutationObserver(()=>{if(location.href===S)return;S=location.href,n=!1;const e=document.getElementById("hiyori-single-btn");e&&e.remove(),$(),l()?(M(),V(),GM_getValue("hiyori_running",!1)&&setTimeout(()=>L(),700)):I(),c()&&GM_getValue("hiyori_running",!1)&&q().catch(e=>{console.error("[妃爱] 自动处理入口出错:",e),GM_getValue("hiyori_running",!1)&&L()})}).observe(document.body,{childList:!0,subtree:!0}),"complete"===document.readyState?E():window.addEventListener("load",E),setInterval(()=>{g(),l()&&M()},4e3),l()&&(V(),GM_getValue("hiyori_running",!1)&&setTimeout(()=>L(),700)),c()&&GM_getValue("hiyori_running",!1)&&q().catch(e=>{console.error("[妃爱] 自动处理入口出错:",e),GM_getValue("hiyori_running",!1)&&L()})}();