From ca7937e94ea36040b3a579f4b88ef429231100f9 Mon Sep 17 00:00:00 2001 From: JoeanAmier Date: Mon, 30 Dec 2024 22:56:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(XHS-Downloader.js):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=89=93=E5=8C=85=E4=B8=8B=E8=BD=BD=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/Release_Notes.md | 3 +- static/XHS-Downloader.js | 142 +++++++++++++++++++-------------------- 2 files changed, 71 insertions(+), 74 deletions(-) diff --git a/static/Release_Notes.md b/static/Release_Notes.md index b73c79f..f4913e1 100644 --- a/static/Release_Notes.md +++ b/static/Release_Notes.md @@ -19,6 +19,7 @@ 1. 重构自动滚动页面功能 2. 新增文件名称长度限制 3. 优化文件名称过滤规则 -4. 优化文件下载功能 +4. 新增文件打包下载功能 +5. 优化文件下载功能

注意:自动滚动页面功能默认关闭!启用该功能可能会被小红书检测为自动化操作,从而导致账号受到风控或封禁!该功能在使用过程中遇到任何问题请及时向开发者反馈!

diff --git a/static/XHS-Downloader.js b/static/XHS-Downloader.js index 4b6f2b4..fe27365 100644 --- a/static/XHS-Downloader.js +++ b/static/XHS-Downloader.js @@ -1,7 +1,7 @@ // ==UserScript== // @name XHS-Downloader // @namespace https://github.com/JoeanAmier/XHS-Downloader -// @version 1.8.2 +// @version 1.8.3 // @description 提取小红书作品/用户链接,下载小红书无水印图文/视频作品文件 // @author JoeanAmier // @match http*://xhslink.com/* @@ -19,7 +19,7 @@ // @run-at document-end // @updateURL https://raw.githubusercontent.com/JoeanAmier/XHS-Downloader/master/static/XHS-Downloader.js // @downloadURL https://raw.githubusercontent.com/JoeanAmier/XHS-Downloader/master/static/XHS-Downloader.js -// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js +// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js // ==/UserScript== @@ -165,7 +165,7 @@ XHS-Downloader 用户脚本 详细说明: const download = async (urls, type_) => { const name = extractName(); - console.info(`基础文件名称 ${name}`); + console.info(`文件名称 ${name}`); if (type_ === "video") { await downloadVideo(urls[0], name); } else { @@ -182,7 +182,7 @@ XHS-Downloader 用户脚本 详细说明: links = generateVideoUrl(note); } if (links.length > 0) { - console.info("无水印文件下载链接", links); + console.info("文件下载链接", links); await download(links, note.type); } else { abnormal() @@ -197,7 +197,6 @@ XHS-Downloader 用户脚本 详细说明: const regex = /\/explore\/([^?]+)/; const match = currentUrl.match(regex); if (match) { - // let note = Object.values(unsafeWindow.__INITIAL_STATE__.note.noteDetailMap); return unsafeWindow.__INITIAL_STATE__.note.noteDetailMap[match[1]] } else { console.error("从链接提取作品 ID 失败", currentUrl,); @@ -215,7 +214,27 @@ XHS-Downloader 用户脚本 详细说明: } }; - const downloadFile = async (link, name) => { + const triggerDownload = (name, blob) => { + // 创建 Blob 对象的 URL + const blobUrl = URL.createObjectURL(blob); + + // 创建一个临时链接元素 + const tempLink = document.createElement("a"); + tempLink.href = blobUrl; + tempLink.download = name; + + // 将链接添加到 DOM 并模拟点击 + document.body.appendChild(tempLink); // 避免某些浏览器安全限制 + tempLink.click(); + + // 清理临时链接元素 + document.body.removeChild(tempLink); // 从 DOM 中移除临时链接 + URL.revokeObjectURL(blobUrl); // 释放 URL + + console.info(`文件已成功下载: ${name}`); + } + + const downloadFile = async (link, name, trigger = true,) => { try { // 使用 fetch 获取文件数据 const response = await fetch(link, {method: "GET"}); @@ -228,66 +247,52 @@ XHS-Downloader 用户脚本 详细说明: const blob = await response.blob(); - // 创建 Blob 对象的 URL - const blobUrl = URL.createObjectURL(blob); - - // 创建一个临时链接元素 - const tempLink = document.createElement("a"); - tempLink.href = blobUrl; - tempLink.download = name; - - // 将链接添加到 DOM 并模拟点击 - document.body.appendChild(tempLink); // 避免某些浏览器安全限制 - tempLink.click(); - - // 清理临时链接元素 - document.body.removeChild(tempLink); // 从 DOM 中移除临时链接 - URL.revokeObjectURL(blobUrl); // 释放 URL - - console.info(`文件已成功下载: ${name}`); - return true; + if (trigger) { + triggerDownload(name, blob); + return true; + } else { + return blob; + } } catch (error) { console.error(`下载失败 (${name}),错误信息:`, error); return false; } }; - const downloadFiles = async (urls, name) => { - // TODO: 打包下载功能异常 - const zip = new JSZip(); - const results = await Promise.all(urls.map(async (link, index) => { - try { - const response = await fetch(link, {method: "GET"}); + const downloadFiles = async (urls, name,) => { + const downloadResults = []; // 用于存储下载结果 - if (!response.ok) { - console.error(`下载失败,状态码: ${response.status},URL: ${link}`); - return false; // 这里返回 false - } - - const blob = await response.blob(); - zip.file(`${name}_${index + 1}.png`, blob); - return true; // 成功时返回 true - } catch (error) { - console.error(`下载失败 (${name}),错误信息:`, error); - return false; // 这里返回 false + const downloadPromises = urls.map(async (url, index) => { + const fileName = `${name}_${index + 1}.png`; // 根据索引生成文件名 + const result = await downloadFile(url, fileName, false); // 调用单个文件下载方法 + if (result) { + downloadResults.push({name: fileName, file: result}); + return true; // 成功 + } else { + return false; // 失败 } - })); + }); - // 检查结果,是否有任何一个失败 - const allDownloadedSuccessfully = results.every(result => result === true); + // 等待所有下载操作完成 + const results = await Promise.all(downloadPromises); - if (!allDownloadedSuccessfully) { - return false; // 如果有任何失败,返回 false - } + if (results.every(result => result === true)) { + try { + const zip = new JSZip(); + downloadResults.forEach((item) => { + zip.file(item.name, item.file); + }); - // 生成 ZIP 文件 - try { - const content = await zip.generateAsync({type: "blob"}); - saveAs(content, `${name}.zip`); - return true; // 如果 ZIP 文件生成成功,返回 true - } catch (err) { - console.error('生成 ZIP 文件失败:', err); - return false; // 生成 ZIP 文件失败,返回 false + const content = await zip.generateAsync({type: "blob", compression: "STORE"}); + saveAs(content, `${name}.zip`); + console.info(`文件已成功保存为: ${name}.zip`); + return true; + } catch (error) { + console.error('生成 ZIP 文件或保存失败,错误信息:', error); + return false; + } + } else { + return false; } }; @@ -300,8 +305,8 @@ XHS-Downloader 用户脚本 详细说明: }; const extractName = () => { - let name = document.title.replace(/[^\u4e00-\u9fa5a-zA-Z0-9 ~!@#$%&()_\-+=\[\];"',.!()【】:“”《》?]/g, ""); - name = truncateString(name, 128,); + let name = document.title.replace(/ - 小红书$/, "").replace(/[^\u4e00-\u9fa5a-zA-Z0-9 ~!@#$%&()_\-+=\[\];"',.!()【】:“”《》?]/g, ""); + name = truncateString(name, 64,); let match = currentUrl.match(/\/([^\/]+)$/); let id = match ? match[1] : null; return name === "" ? id : name @@ -314,24 +319,15 @@ XHS-Downloader 用户脚本 详细说明: }; const downloadImage = async (urls, name) => { - let result = []; - for (const [index, url] of urls.entries()) { - result.push(await downloadFile(url, `${name}_${index + 1}.png`)); + let success; + if (urls.length > 1) { + success = await downloadFiles(urls, name,); + } else { + success = await downloadFile(urls[0], `${name}.png`); } - if (!result.every(item => item === true)) { + if (!success) { abnormal(); } - - // TODO: 打包下载功能未实现 - // let success; - // if (urls.length > 1) { - // success = await downloadFiles(urls, name,); - // } else { - // success = await downloadFile(urls[0], `${name}.png`); - // } - // if (!success) { - // abnormal(); - // } }; const window_scrollBy = (x, y,) => { @@ -637,6 +633,6 @@ XHS-Downloader 用户脚本 详细说明: alert("XHS-Downloader 用户脚本依赖库 JSZip 加载失败,下载功能可能无法使用!"); } if (typeof saveAs === 'undefined') { - alert("XHS-Downloader 用户脚本依赖库 FileSaver 加载失败,下载功能可能无法使用!"); + alert("XHS-Downloader 用户脚本依赖库 FileSaver 加载失败,作品文件打包下载功能无法使用!"); } })();