// ==UserScript== // @name 小红书转发 // @namespace https://mundane.ink/redbook // @version 2.1 // @description 在浏览小红书收藏时将数据转发到https://mundane.ink/redbook/index.html,方便收藏的管理和导出 // @match https://www.xiaohongshu.com/user/profile/* // @grant GM_xmlhttpRequest // @license MIT // @downloadURL none // ==/UserScript== (function () { "use strict"; console.log("小红书脚本生效了"); const baseUrl = "https://mundane.ink"; document.body.addEventListener("click", (e) => { if ( e.target.tagName === "A" && e.target.classList.value.includes("cover ld mask") ) { setTimeout(() => { const href = e.target.href; const noteId = href.match(/\/([^/]+)$/)[1]; if (noteId) { createDownloadMdButton(noteId); createMediaButton(noteId); } }, 1000); } }); // 创建下载md按钮 function createDownloadMdButton(noteId) { const mask = document.querySelector("div.note-detail-mask"); const button = document.createElement("button"); button.textContent = "下载md文件"; button.style.position = "fixed"; button.style.bottom = "65px"; button.style.right = "20px"; button.style.padding = "10px 20px"; button.style.border = "none"; button.style.backgroundColor = "#056b00"; button.style.color = "#fff"; button.style.fontFamily = "Arial, sans-serif"; button.style.fontSize = "16px"; button.style.fontWeight = "bold"; button.style.cursor = "pointer"; button.addEventListener("click", function () { exportMd(noteId); }); mask.appendChild(button); } // 创建下载图片和视频按钮 function createMediaButton(noteId) { const mask = document.querySelector("div.note-detail-mask"); const button = document.createElement("button"); button.textContent = "下载图片和视频"; button.style.position = "fixed"; button.style.bottom = "20px"; button.style.right = "20px"; button.style.padding = "10px 20px"; button.style.border = "none"; button.style.backgroundColor = "#056b00"; button.style.color = "#fff"; button.style.fontFamily = "Arial, sans-serif"; button.style.fontSize = "16px"; button.style.fontWeight = "bold"; button.style.cursor = "pointer"; button.addEventListener("click", function () { getMedia(noteId); }); mask.appendChild(button); } function getMedia(noteId) { fetch(`${baseUrl}/mail/redbook/note/getMediaInfo`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ noteId }), }) .then((response) => { if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } // 解析响应数据为 JSON 格式 return response.json(); }) .then((resp) => { if (resp.code === 200) { exportMedia(resp.data); } }) .catch((error) => console.error(error)); } function exportMedia(data) { const { title, videoUrl, imageUrls } = data; exportImages(title, imageUrls); if (videoUrl) { exportVideo(title, videoUrl); } } function exportVideo(title, videoUrl) { fetch(videoUrl) .then((response) => { return response.blob(); }) .then((blob) => { // 创建一个下载链接 const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = title + ".mp4"; document.body.appendChild(a); // 模拟点击下载链接 a.click(); // 清理对象 URL URL.revokeObjectURL(url); }) .catch((error) => console.error(error)); } function exportImages(title, imageUrls) { for (let i = 0; i < imageUrls.length; i++) { const imageUrl = imageUrls[i]; fetch(imageUrl) .then((response) => response.blob()) .then((blob) => { const imageURL = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = imageURL; a.download = title + "-" + (i + 1) + ".png"; a.click(); URL.revokeObjectURL(imageURL); }); } } function exportMd(noteId) { fetch(`${baseUrl}/mail/redbook/note/exportNoteMd`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ noteId }), }) .then((response) => { const contentDisposition = response.headers.get("Content-Disposition"); const filenameMatch = decodeURIComponent( contentDisposition.match(/filename\=(.*)/)[1] ); const filename = filenameMatch || "filename.md"; response.blob().then((blob) => { const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); }); }) .catch((error) => console.error(error)); } const userId = window.location.href.match(/\/user\/profile\/(\w+)/)[1]; console.log(userId); // 创建按钮元素 const btnScroll = document.createElement("button"); btnScroll.innerHTML = "自动滚动"; const btnJump = document.createElement("button"); btnJump.innerHTML = "去下载"; const btnTest = document.createElement("button"); btnTest.innerHTML = "测试"; // 设置按钮样式 btnScroll.style.position = "fixed"; btnScroll.style.top = "160px"; btnScroll.style.right = "20px"; btnScroll.style.backgroundColor = "#056b00"; btnScroll.style.color = "#fff"; btnScroll.style.padding = "8px"; btnScroll.style.borderRadius = "6px"; btnScroll.style.zIndex = "1000"; btnJump.style.position = "fixed"; btnJump.style.top = "210px"; btnJump.style.right = "20px"; btnJump.style.backgroundColor = "#056b00"; btnJump.style.color = "#fff"; btnJump.style.padding = "8px"; btnJump.style.borderRadius = "6px"; btnJump.style.zIndex = "1000"; btnTest.style.position = "fixed"; btnTest.style.top = "260px"; btnTest.style.right = "20px"; btnTest.style.backgroundColor = "#056b00"; btnTest.style.color = "#fff"; btnTest.style.padding = "8px"; btnTest.style.borderRadius = "6px"; btnTest.style.zIndex = "1000"; // 添加按钮到页面中 document.body.appendChild(btnScroll); document.body.appendChild(btnJump); // document.body.appendChild(btnTest); let isScrolling = false; let timerId; function simulateScroll() { window.scrollBy(0, 200); } function startScroll() { if (isScrolling) { return; } isScrolling = true; btnScroll.innerHTML = "停止滚动"; btnScroll.style.backgroundColor = "#ff2442"; timerId = setInterval(simulateScroll, 200); } function cancelScroll() { if (!isScrolling) { return; } isScrolling = false; btnScroll.style.backgroundColor = "#056b00"; btnScroll.innerHTML = "自动滚动"; if (timerId) { clearInterval(timerId); } } // 给按钮添加点击事件 btnScroll.addEventListener("click", function () { if (isScrolling) { cancelScroll(); } else { startScroll(); } }); btnJump.addEventListener("click", function () { window.open( `https://mundane.ink/redbook/index.html?userId=${userId}`, "_blank" ); }); btnTest.addEventListener("click", function () { let tab = document.querySelectorAll(".tab-content-item")[1]; const elements = tab.querySelectorAll("a.cover.ld.mask"); elements[0].click(); let timeId = setInterval(function () { let closeButton = document.querySelector("div.close-circle div.close"); if (closeButton) { closeButton.click(); clearInterval(timeId); } }, 500); }); const originOpen = XMLHttpRequest.prototype.open; const collectUrl = "//edith.xiaohongshu.com/api/sns/web/v2/note/collect"; const feedUrl = "//edith.xiaohongshu.com/api/sns/web/v1/feed"; XMLHttpRequest.prototype.open = function (_, url) { const xhr = this; if (url.startsWith(collectUrl) || url.startsWith(feedUrl)) { const getter = Object.getOwnPropertyDescriptor( XMLHttpRequest.prototype, "response" ).get; Object.defineProperty(xhr, "responseText", { get: () => { let result = getter.call(xhr); let myUrl = ""; if (url.startsWith(collectUrl)) { myUrl = `${baseUrl}/mail/redbook/collect/save`; } else if (url.startsWith(feedUrl)) { myUrl = `${baseUrl}/mail/redbook/note/save`; } try { // 将result发送到服务器 GM_xmlhttpRequest({ method: "POST", url: myUrl, headers: { "Content-Type": "application/json", }, data: JSON.stringify({ result: result, userId: userId }), onload: function (response) { console.log("Result sent to server successfully!"); }, }); if (url.startsWith(collectUrl)) { const obj = JSON.parse(result); if (!obj.data.has_more) { console.log("没有更多了!!!"); cancelScroll(); } } } catch (e) { console.error(e); } return result; }, }); } originOpen.apply(this, arguments); }; })();