Greasy Fork is available in English.
这是ai编的......
// ==UserScript==
// @name 小红书视频下载极简版
// @namespace http://tampermonkey.net/
// @version 1.0.12
// @description 这是ai编的......
// @match *://www.xiaohongshu.com/*
// @grant GM_download
// @grant GM_addStyle
// @license MIT
// @require https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.9/dayjs.min.js
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// 原版按钮样式(未改动)
const btn = document.createElement('button');
btn.id = 'xhs-video-downloader';
GM_addStyle(`
#xhs-video-downloader {
position: fixed;
bottom: 30px;
right: 80px;
z-index: 9999;
padding: 10px 20px;
background: #ff2442;
color: white;
border: none;
border-radius: 20px;
cursor: pointer;
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
font-family: -apple-system, sans-serif;
}
#xhs-video-downloader:disabled {
background-color: #cccccc;
cursor: not-allowed;
opacity: 0.7;
}
`);
btn.textContent = '↓ 下载视频';
// 初始时不显式设置禁用状态,由检测逻辑控制
document.body.appendChild(btn);
// =============== 增强视频检测 (保持原样) ===============
let currentVideoUrl = null;
let lastVideoCheck = 0;
// =============== 增强作者获取 (保持原样) ===============
const getDynamicAuthor = () => {
try {
return (
document.querySelector('.author-info .name')?.textContent.trim() ||
document.querySelector('[class*="username"]')?.textContent.trim() ||
document.title.split('的小红书笔记')[0]
).replace(/[\\/:*?"<>|.\n\r]/g, '').substring(0, 20) || '未知作者';
} catch(e) {
return '未知作者';
}
};
// =============== 新增:只获取 #detail-title 的内容 ===============
const getSpecificTitle = () => {
try {
const titleElement = document.querySelector('#detail-title'); // 直接使用 ID 选择器
if (titleElement) {
const titleText = titleElement.textContent;
const firstLine = titleText.split('\n')[0].trim();
// 清理并截取前 20 字符
const cleanTitle = firstLine.replace(/[\\/:*?"<>|.\n\r]/g, '').substring(0, 20);
return cleanTitle; // 返回清理后的标题
}
return ''; // 找不到元素则返回空字符串
} catch (e) {
console.error("获取 #detail-title 出错:", e); // 输出错误到控制台
return ''; // 出错也返回空字符串
}
};
// =============== 点击事件(只修改文件名) ===============
btn.addEventListener('click', () => {
if(!currentVideoUrl) return alert('请先播放视频');
// 保持原始逻辑,不添加额外的禁用检查
const timestamp = dayjs().format('YYYYMMDD_HH-mm-ss');
const author = getDynamicAuthor();
const title = getSpecificTitle(); // <-- 调用新的、只获取指定ID的函数
// 构建文件名
let filename = `XHS_${timestamp}_${author}`;
if (title) { // 只有标题非空时才添加
filename += `_${title}`;
}
filename += '.mp4';
console.log("生成文件名:", filename); // 保留原始确认日志
// 恢复原始 GM_download
GM_download({
url: currentVideoUrl,
name: filename,
onerror: (e) => alert(`下载失败: ${e.error}`)
// 无 onload, ontimeout
});
});
const origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
if(typeof url === 'string' && /sns-video.*\.mp4/.test(url)) {
let fullUrl = url;
try { if (!url.startsWith('http')) { fullUrl = new URL(url, window.location.href).href; } } catch {}
if (fullUrl && fullUrl.startsWith('http') && fullUrl !== currentVideoUrl) {
currentVideoUrl = fullUrl;
btn.disabled = false; // 启用按钮
console.log('XHR捕获视频:', fullUrl);
}
}
try {
origOpen.apply(this, arguments);
} catch(e) { /* 忽略原始调用错误 */ }
};
})();