// ==UserScript==
// @name 小红书app复制链接转换为直接可访问链接
// @namespace http://tampermonkey.net/
// @version 0.4
// @description 自动将小红书app分享的短链接转换为直接可访问的长链接,支持手动输入、批量转换和导出表格
// @author You
// @match *://*/*
// @license UNLICENSED
// @grant GM_xmlhttpRequest
// @grant GM_setClipboard
// @grant GM_notification
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function() {
'use strict';
// 版本更新日志
const VERSION = '0.4';
const UPDATE_NOTES = `
v0.4 更新内容:
- 添加批量转换功能,支持一次性转换多个链接
- 优化链接提取算法,支持从文本中提取多个链接
- 添加批量结果表格展示和导出功能
- 优化界面布局,添加选项卡切换
v0.3 更新内容:
- 修复了状态码200导致无法获取重定向链接的问题
- 增强了链接提取能力,支持多种重定向方式
- 添加了备用请求方法,提高成功率
- 优化了错误处理和调试信息
v0.2 更新内容:
- 添加了手动输入功能
- 添加了导出表格功能
- 增加了历史记录存储
`;
console.log(`小红书链接转换器 v${VERSION} 已加载`);
console.log(UPDATE_NOTES);
// 存储转换历史记录
let linkHistory = GM_getValue('xhs_link_history', []);
// 保存历史记录
const saveHistory = (shortUrl, longUrl) => {
// 检查是否已存在相同的短链接
const existingIndex = linkHistory.findIndex(item => item.shortUrl === shortUrl);
if (existingIndex !== -1) {
// 更新已存在的记录
linkHistory[existingIndex].longUrl = longUrl;
linkHistory[existingIndex].timestamp = new Date().toISOString();
} else {
// 添加新记录
linkHistory.push({
shortUrl: shortUrl,
longUrl: longUrl,
timestamp: new Date().toISOString()
});
}
// 限制历史记录数量,最多保存100条
if (linkHistory.length > 100) {
linkHistory = linkHistory.slice(-100);
}
// 保存到GM存储
GM_setValue('xhs_link_history', linkHistory);
};
// 导出CSV格式
const exportToCSV = () => {
if (linkHistory.length === 0) {
GM_notification({
text: '没有可导出的链接记录',
title: '导出失败',
timeout: 2000
});
return;
}
// 创建CSV内容
let csvContent = '序号,原始链接,转换后链接,转换时间\n';
linkHistory.forEach((item, index) => {
const formattedDate = new Date(item.timestamp).toLocaleString();
csvContent += `${index + 1},"${item.shortUrl}","${item.longUrl}","${formattedDate}"\n`;
});
// 创建Blob对象
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
// 创建下载链接
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `小红书链接转换记录_${new Date().toISOString().slice(0,10)}.csv`);
link.style.display = 'none';
document.body.appendChild(link);
// 触发下载
link.click();
// 清理
setTimeout(() => {
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}, 100);
};
// 创建浮动窗口
const createFloatingWindow = () => {
const container = document.createElement('div');
container.id = 'xhs-link-converter';
container.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
width: 400px;
background-color: white;
border: 1px solid #f5a9ae;
border-radius: 8px;
padding: 10px;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
font-family: 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
display: none;
`;
const header = document.createElement('div');
header.style.cssText = `
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
border-bottom: 1px solid #f5a9ae;
padding-bottom: 5px;
`;
const title = document.createElement('h3');
title.textContent = '小红书链接转换器';
title.style.cssText = `
margin: 0;
color: #ff2442;
font-size: 16px;
`;
const closeBtn = document.createElement('button');
closeBtn.textContent = '✕';
closeBtn.style.cssText = `
background: none;
border: none;
color: #ff2442;
cursor: pointer;
font-size: 16px;
`;
closeBtn.onclick = () => {
container.style.display = 'none';
};
header.appendChild(title);
header.appendChild(closeBtn);
container.appendChild(header);
// 添加选项卡
const tabContainer = document.createElement('div');
tabContainer.style.cssText = `
display: flex;
border-bottom: 1px solid #ddd;
margin-bottom: 15px;
`;
const singleTab = document.createElement('div');
singleTab.textContent = '单个转换';
singleTab.id = 'xhs-single-tab';
singleTab.style.cssText = `
padding: 8px 15px;
cursor: pointer;
background-color: #ff2442;
color: white;
border-radius: 4px 4px 0 0;
margin-right: 5px;
`;
const batchTab = document.createElement('div');
batchTab.textContent = '批量转换';
batchTab.id = 'xhs-batch-tab';
batchTab.style.cssText = `
padding: 8px 15px;
cursor: pointer;
background-color: #f8f8f8;
border-radius: 4px 4px 0 0;
`;
tabContainer.appendChild(singleTab);
tabContainer.appendChild(batchTab);
container.appendChild(tabContainer);
// 单个转换内容区域
const singleContent = document.createElement('div');
singleContent.id = 'xhs-single-content';
singleContent.style.display = 'block';
// 批量转换内容区域
const batchContent = document.createElement('div');
batchContent.id = 'xhs-batch-content';
batchContent.style.display = 'none';
// 选项卡切换事件
singleTab.onclick = () => {
singleTab.style.backgroundColor = '#ff2442';
singleTab.style.color = 'white';
batchTab.style.backgroundColor = '#f8f8f8';
batchTab.style.color = 'black';
singleContent.style.display = 'block';
batchContent.style.display = 'none';
};
batchTab.onclick = () => {
batchTab.style.backgroundColor = '#ff2442';
batchTab.style.color = 'white';
singleTab.style.backgroundColor = '#f8f8f8';
singleTab.style.color = 'black';
batchContent.style.display = 'block';
singleContent.style.display = 'none';
};
// 单个转换内容
// 添加输入框
const inputSection = document.createElement('div');
inputSection.style.cssText = `
margin-bottom: 15px;
`;
const inputLabel = document.createElement('p');
inputLabel.textContent = '输入包含小红书链接的文本:';
inputLabel.style.cssText = `
margin: 5px 0;
font-size: 14px;
font-weight: bold;
`;
inputSection.appendChild(inputLabel);
const textArea = document.createElement('textarea');
textArea.id = 'xhs-input-text';
textArea.placeholder = '粘贴包含小红书链接的文本...';
textArea.style.cssText = `
width: 100%;
height: 80px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
resize: vertical;
box-sizing: border-box;
margin-bottom: 10px;
`;
inputSection.appendChild(textArea);
const convertBtn = document.createElement('button');
convertBtn.textContent = '转换链接';
convertBtn.style.cssText = `
background-color: #ff2442;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
margin-right: 10px;
`;
convertBtn.onclick = () => {
const inputText = document.getElementById('xhs-input-text').value;
if (inputText && inputText.includes('xhslink.com')) {
const shortUrl = extractUrl(inputText);
if (shortUrl) {
// 更新原始链接展示
const originalLinkElement = document.getElementById('xhs-original-link');
originalLinkElement.textContent = shortUrl;
// 获取并显示重定向链接
getRedirectUrl(shortUrl, function(redirectUrl) {
const convertedLinkElement = document.getElementById('xhs-converted-link');
convertedLinkElement.textContent = redirectUrl;
// 保存到历史记录
saveHistory(shortUrl, redirectUrl);
});
} else {
GM_notification({
text: '未在输入文本中找到小红书链接',
title: '提示',
timeout: 2000
});
}
} else {
GM_notification({
text: '请输入包含小红书链接的文本',
title: '提示',
timeout: 2000
});
}
};
inputSection.appendChild(convertBtn);
const clearBtn = document.createElement('button');
clearBtn.textContent = '清空输入';
clearBtn.style.cssText = `
background-color: #888;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
`;
clearBtn.onclick = () => {
document.getElementById('xhs-input-text').value = '';
};
inputSection.appendChild(clearBtn);
singleContent.appendChild(inputSection);
// 结果显示区域
const contentDiv = document.createElement('div');
contentDiv.style.cssText = `
margin-bottom: 15px;
`;
const originalLinkLabel = document.createElement('p');
originalLinkLabel.textContent = '原始链接:';
originalLinkLabel.style.cssText = `
margin: 5px 0;
font-size: 14px;
font-weight: bold;
`;
contentDiv.appendChild(originalLinkLabel);
const originalLinkText = document.createElement('div');
originalLinkText.id = 'xhs-original-link';
originalLinkText.style.cssText = `
padding: 5px;
background-color: #f8f8f8;
border-radius: 4px;
word-break: break-all;
font-size: 12px;
max-height: 60px;
overflow-y: auto;
margin-bottom: 10px;
`;
contentDiv.appendChild(originalLinkText);
const convertedLinkLabel = document.createElement('p');
convertedLinkLabel.textContent = '转换后链接:';
convertedLinkLabel.style.cssText = `
margin: 5px 0;
font-size: 14px;
font-weight: bold;
`;
contentDiv.appendChild(convertedLinkLabel);
const convertedLinkText = document.createElement('div');
convertedLinkText.id = 'xhs-converted-link';
convertedLinkText.style.cssText = `
padding: 5px;
background-color: #f8f8f8;
border-radius: 4px;
word-break: break-all;
font-size: 12px;
max-height: 60px;
overflow-y: auto;
`;
contentDiv.appendChild(convertedLinkText);
singleContent.appendChild(contentDiv);
// 批量转换内容
const batchInputSection = document.createElement('div');
batchInputSection.style.cssText = `
margin-bottom: 15px;
`;
const batchInputLabel = document.createElement('p');
batchInputLabel.textContent = '批量输入小红书链接(每行一个):';
batchInputLabel.style.cssText = `
margin: 5px 0;
font-size: 14px;
font-weight: bold;
`;
batchInputSection.appendChild(batchInputLabel);
const batchTextArea = document.createElement('textarea');
batchTextArea.id = 'xhs-batch-input';
batchTextArea.placeholder = '粘贴多个小红书链接,每行一个...\n例如:\nhttp://xhslink.com/abcde\nhttp://xhslink.com/fghij';
batchTextArea.style.cssText = `
width: 100%;
height: 120px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
resize: vertical;
box-sizing: border-box;
margin-bottom: 10px;
`;
batchInputSection.appendChild(batchTextArea);
const batchBtnContainer = document.createElement('div');
batchBtnContainer.style.cssText = `
display: flex;
gap: 10px;
margin-bottom: 15px;
`;
const batchConvertBtn = document.createElement('button');
batchConvertBtn.textContent = '批量转换';
batchConvertBtn.style.cssText = `
background-color: #ff2442;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
`;
batchConvertBtn.onclick = () => {
const batchInput = document.getElementById('xhs-batch-input').value;
if (!batchInput) {
GM_notification({
text: '请输入小红书链接',
title: '提示',
timeout: 2000
});
return;
}
// 清空结果表格
const resultTable = document.getElementById('xhs-batch-results');
resultTable.innerHTML = `
<tr>
<th>序号</th>
<th>原始链接</th>
<th>转换后链接</th>
<th>状态</th>
</tr>
`;
// 提取所有链接
const links = [];
// 处理每行文本
const lines = batchInput.split('\n');
lines.forEach(line => {
if (line.trim()) {
// 尝试从每行提取所有链接
const lineLinks = extractMultipleUrls(line);
if (lineLinks.length > 0) {
links.push(...lineLinks);
} else {
// 如果没有找到链接,尝试单个提取
const singleLink = extractUrl(line);
if (singleLink) {
links.push(singleLink);
}
}
}
});
// 去重
const uniqueLinks = [...new Set(links)];
if (uniqueLinks.length === 0) {
GM_notification({
text: '未找到有效的小红书链接',
title: '提示',
timeout: 2000
});
return;
}
// 显示进度信息
const progressInfo = document.getElementById('xhs-batch-progress');
progressInfo.textContent = `正在转换 0/${uniqueLinks.length}`;
progressInfo.style.display = 'block';
// 批量处理链接
let completedCount = 0;
uniqueLinks.forEach((link, index) => {
// 添加一行到表格
const row = resultTable.insertRow(-1);
const cellIndex = row.insertCell(0);
const cellOriginal = row.insertCell(1);
const cellConverted = row.insertCell(2);
const cellStatus = row.insertCell(3);
cellIndex.textContent = index + 1;
cellOriginal.textContent = link;
cellConverted.textContent = '转换中...';
cellStatus.textContent = '处理中';
cellStatus.style.color = '#ff9800';
// 转换链接
getRedirectUrl(link, function(redirectUrl) {
completedCount++;
// 更新进度
progressInfo.textContent = `正在转换 ${completedCount}/${uniqueLinks.length}`;
// 更新表格
cellConverted.textContent = redirectUrl;
if (redirectUrl.startsWith('无法') || redirectUrl.startsWith('请求失败')) {
cellStatus.textContent = '失败';
cellStatus.style.color = '#f44336';
} else {
cellStatus.textContent = '成功';
cellStatus.style.color = '#4caf50';
// 保存到历史记录
saveHistory(link, redirectUrl);
}
// 检查是否全部完成
if (completedCount === uniqueLinks.length) {
progressInfo.textContent = `转换完成,共 ${uniqueLinks.length} 个链接`;
// 显示导出按钮
document.getElementById('xhs-batch-export').style.display = 'block';
}
});
});
};
batchBtnContainer.appendChild(batchConvertBtn);
const batchClearBtn = document.createElement('button');
batchClearBtn.textContent = '清空输入';
batchClearBtn.style.cssText = `
background-color: #888;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
`;
batchClearBtn.onclick = () => {
document.getElementById('xhs-batch-input').value = '';
};
batchBtnContainer.appendChild(batchClearBtn);
batchInputSection.appendChild(batchBtnContainer);
batchContent.appendChild(batchInputSection);
// 批量转换进度
const progressInfo = document.createElement('div');
progressInfo.id = 'xhs-batch-progress';
progressInfo.style.cssText = `
margin: 10px 0;
font-size: 14px;
font-weight: bold;
color: #ff2442;
display: none;
`;
batchContent.appendChild(progressInfo);
// 批量转换结果表格
const resultTableContainer = document.createElement('div');
resultTableContainer.style.cssText = `
max-height: 300px;
overflow-y: auto;
margin-bottom: 15px;
`;
const resultTable = document.createElement('table');
resultTable.id = 'xhs-batch-results';
resultTable.style.cssText = `
width: 100%;
border-collapse: collapse;
font-size: 12px;
`;
resultTable.innerHTML = `
<tr>
<th>序号</th>
<th>原始链接</th>
<th>转换后链接</th>
<th>状态</th>
</tr>
`;
// 设置表格样式
const style = document.createElement('style');
style.textContent = `
#xhs-batch-results th, #xhs-batch-results td {
border: 1px solid #ddd;
padding: 6px;
text-align: left;
}
#xhs-batch-results th {
background-color: #f2f2f2;
position: sticky;
top: 0;
}
#xhs-batch-results tr:nth-child(even) {
background-color: #f9f9f9;
}
#xhs-batch-results tr:hover {
background-color: #f5f5f5;
}
`;
document.head.appendChild(style);
resultTableContainer.appendChild(resultTable);
batchContent.appendChild(resultTableContainer);
// 批量导出按钮
const batchExportBtn = document.createElement('button');
batchExportBtn.id = 'xhs-batch-export';
batchExportBtn.textContent = '导出批量结果';
batchExportBtn.style.cssText = `
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
display: none;
margin-bottom: 10px;
`;
batchExportBtn.onclick = () => {
exportBatchResults();
};
batchContent.appendChild(batchExportBtn);
// 添加内容区域到容器
container.appendChild(singleContent);
container.appendChild(batchContent);
// 按钮容器
const btnContainer = document.createElement('div');
btnContainer.style.cssText = `
display: flex;
justify-content: space-between;
gap: 10px;
`;
// 左侧按钮组
const leftBtnGroup = document.createElement('div');
const exportBtn = document.createElement('button');
exportBtn.textContent = '导出历史记录';
exportBtn.style.cssText = `
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
`;
exportBtn.onclick = exportToCSV;
leftBtnGroup.appendChild(exportBtn);
btnContainer.appendChild(leftBtnGroup);
// 右侧按钮组
const rightBtnGroup = document.createElement('div');
rightBtnGroup.style.cssText = `
display: flex;
gap: 10px;
`;
const copyBtn = document.createElement('button');
copyBtn.textContent = '复制链接';
copyBtn.style.cssText = `
background-color: #ff2442;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
`;
copyBtn.onclick = () => {
const convertedLink = document.getElementById('xhs-converted-link').textContent;
if (convertedLink) {
GM_setClipboard(convertedLink);
GM_notification({
text: '链接已复制到剪贴板',
title: '复制成功',
timeout: 2000
});
}
};
const openBtn = document.createElement('button');
openBtn.textContent = '打开链接';
openBtn.style.cssText = `
background-color: #ff2442;
color: white;
border: none;
border-radius: 4px;
padding: 5px 10px;
cursor: pointer;
font-size: 14px;
`;
openBtn.onclick = () => {
const convertedLink = document.getElementById('xhs-converted-link').textContent;
if (convertedLink) {
window.open(convertedLink, '_blank');
}
};
rightBtnGroup.appendChild(copyBtn);
rightBtnGroup.appendChild(openBtn);
btnContainer.appendChild(rightBtnGroup);
container.appendChild(btnContainer);
document.body.appendChild(container);
return container;
};
// 导出批量转换结果
const exportBatchResults = () => {
const table = document.getElementById('xhs-batch-results');
if (!table || table.rows.length <= 1) {
GM_notification({
text: '没有可导出的批量转换结果',
title: '导出失败',
timeout: 2000
});
return;
}
// 创建CSV内容
let csvContent = '序号,原始链接,转换后链接,状态\n';
// 从第二行开始(跳过表头)
for (let i = 1; i < table.rows.length; i++) {
const row = table.rows[i];
const index = row.cells[0].textContent;
const original = row.cells[1].textContent;
const converted = row.cells[2].textContent;
const status = row.cells[3].textContent;
csvContent += `${index},"${original}","${converted}","${status}"\n`;
}
// 创建Blob对象
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
// 创建下载链接
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `小红书批量链接转换结果_${new Date().toISOString().slice(0,10)}.csv`);
link.style.display = 'none';
document.body.appendChild(link);
// 触发下载
link.click();
// 清理
setTimeout(() => {
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
}, 100);
GM_notification({
text: '批量转换结果已导出为CSV文件',
title: '导出成功',
timeout: 2000
});
};
// 提取URL
const extractUrl = (text) => {
const pattern = /http:\/\/xhslink\.com\/\S+/;
const match = pattern.exec(text);
if (match) {
// 去除链接末尾的中文逗号或其他标点符号
let url = match[0];
if (url.endsWith(',') || url.endsWith('。') || url.endsWith('!') || url.endsWith('!')) {
url = url.slice(0, -1);
}
return url;
}
return null;
};
// 提取多个URL
const extractMultipleUrls = (text) => {
const pattern = /http:\/\/xhslink\.com\/\S+/g;
const matches = text.match(pattern);
if (!matches) return [];
// 处理每个链接,去除末尾的标点符号
return matches.map(url => {
if (url.endsWith(',') || url.endsWith('。') || url.endsWith('!') || url.endsWith('!')) {
return url.slice(0, -1);
}
return url;
});
};
// 获取重定向链接
const getRedirectUrl = (shortUrl, callback) => {
// 第一次尝试 - 标准方法
tryGetRedirectUrl(shortUrl, (result) => {
// 如果结果包含错误信息,尝试备用方法
if (result.startsWith('无法') || result.startsWith('请求失败')) {
console.log('小红书链接转换器 - 第一次尝试失败,使用备用方法');
tryAlternativeMethod(shortUrl, callback);
} else {
callback(result);
}
});
};
// 标准方法获取重定向链接
const tryGetRedirectUrl = (shortUrl, callback) => {
GM_xmlhttpRequest({
method: 'GET',
url: shortUrl,
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.9",
"Connection": "keep-alive",
"Referer": "https://www.xiaohongshu.com/"
},
followRedirect: false,
onload: function(response) {
if (response.status >= 300 && response.status < 400) {
// 标准重定向方式
const redirectUrl = response.finalUrl || response.responseHeaders.match(/Location:\s*(.*)/i)?.[1];
if (redirectUrl) {
callback(redirectUrl);
} else {
callback("无法解析重定向链接");
}
} else if (response.status === 200) {
// 处理状态码200的情况
// 尝试从响应内容中提取重定向URL
// 方法1: 检查meta refresh标签
const metaRefreshMatch = response.responseText.match(/<meta[^>]*?url=(.*?)["'\s>]/i) ||
response.responseText.match(/<meta[^>]*?http-equiv=["']?refresh["']?[^>]*?content=["']?\d+;\s*url=(.*?)["'\s>]/i);
if (metaRefreshMatch && metaRefreshMatch[1]) {
callback(metaRefreshMatch[1]);
return;
}
// 方法2: 检查JavaScript重定向
const jsRedirectPatterns = [
/window\.location\.(?:href|replace)\s*=\s*["'](https?:\/\/[^"']+)["']/i,
/location\.(?:href|replace)\s*=\s*["'](https?:\/\/[^"']+)["']/i,
/window\.location\s*=\s*["'](https?:\/\/[^"']+)["']/i,
/location\s*=\s*["'](https?:\/\/[^"']+)["']/i,
/top\.location\s*=\s*["'](https?:\/\/[^"']+)["']/i,
/parent\.location\s*=\s*["'](https?:\/\/[^"']+)["']/i
];
for (const pattern of jsRedirectPatterns) {
const match = response.responseText.match(pattern);
if (match && match[1]) {
callback(match[1]);
return;
}
}
// 方法3: 尝试查找小红书域名链接
const xhsLinkPatterns = [
/https:\/\/www\.xiaohongshu\.com\/discovery\/item\/[a-zA-Z0-9]+/i,
/https:\/\/www\.xiaohongshu\.com\/explore\/[a-zA-Z0-9]+/i,
/https:\/\/www\.xiaohongshu\.com\/[^"'\s)]+/i
];
for (const pattern of xhsLinkPatterns) {
const match = response.responseText.match(pattern);
if (match) {
callback(match[0]);
return;
}
}
// 方法4: 尝试查找data-url属性
const dataUrlMatch = response.responseText.match(/data-url=["'](https?:\/\/[^"']+)["']/i);
if (dataUrlMatch && dataUrlMatch[1]) {
callback(dataUrlMatch[1]);
return;
}
// 方法5: 尝试查找JSON中的URL
const jsonUrlMatch = response.responseText.match(/"url"\s*:\s*"(https?:\/\/[^"]+)"/i);
if (jsonUrlMatch && jsonUrlMatch[1]) {
callback(jsonUrlMatch[1]);
return;
}
// 如果以上方法都失败,尝试使用完整的响应URL
if (response.finalUrl && response.finalUrl !== shortUrl) {
callback(response.finalUrl);
return;
}
// 在控制台输出调试信息
console.log('小红书链接转换器 - 调试信息:', {
status: response.status,
url: shortUrl,
responseHeaders: response.responseHeaders,
// 仅显示响应前1000个字符,避免过长
responseTextPreview: response.responseText.substring(0, 1000)
});
callback("无法提取重定向链接,请尝试手动打开: " + shortUrl);
} else {
callback("无法获取重定向链接,状态码: " + response.status);
}
},
onerror: function(error) {
callback("请求失败: " + error.message);
}
});
};
// 备用方法 - 使用不同的请求方式
const tryAlternativeMethod = (shortUrl, callback) => {
// 使用移动设备UA和允许重定向
GM_xmlhttpRequest({
method: 'GET',
url: shortUrl,
headers: {
"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh-Hans;q=0.9",
"Connection": "keep-alive",
"Referer": "https://www.xiaohongshu.com/"
},
followRedirect: true, // 允许重定向
onload: function(response) {
// 检查最终URL是否是小红书域名
if (response.finalUrl && response.finalUrl.includes('xiaohongshu.com')) {
callback(response.finalUrl);
return;
}
// 尝试从响应内容中提取URL
const xhsLinkMatch = response.responseText.match(/https:\/\/www\.xiaohongshu\.com\/[^"'\s)]+/i);
if (xhsLinkMatch) {
callback(xhsLinkMatch[0]);
return;
}
// 如果仍然失败,返回原始错误
callback("无法获取重定向链接,请尝试手动打开: " + shortUrl);
},
onerror: function(error) {
callback("备用方法请求失败: " + error.message);
}
});
};
// 添加剪贴板事件监听
let floatingWindow = null;
let lastProcessedText = '';
document.addEventListener('paste', function(e) {
const clipboardText = e.clipboardData.getData('text');
// 检查文本是否包含小红书链接且未处理过相同文本
if (clipboardText && clipboardText !== lastProcessedText && clipboardText.includes('xhslink.com')) {
lastProcessedText = clipboardText;
const shortUrl = extractUrl(clipboardText);
if (shortUrl) {
// 确保浮动窗口已创建
if (!floatingWindow) {
floatingWindow = createFloatingWindow();
}
// 更新原始链接展示
const originalLinkElement = document.getElementById('xhs-original-link');
originalLinkElement.textContent = shortUrl;
// 显示窗口
floatingWindow.style.display = 'block';
// 获取并显示重定向链接
getRedirectUrl(shortUrl, function(redirectUrl) {
const convertedLinkElement = document.getElementById('xhs-converted-link');
convertedLinkElement.textContent = redirectUrl;
// 保存到历史记录
saveHistory(shortUrl, redirectUrl);
});
}
}
});
// 添加剪贴板监听按钮
const addClipboardButton = () => {
const button = document.createElement('button');
button.textContent = '小红书链接转换';
button.style.cssText = `
position: fixed;
bottom: 80px;
right: 20px;
background-color: #ff2442;
color: white;
border: none;
border-radius: 4px;
padding: 8px 12px;
cursor: pointer;
font-size: 14px;
z-index: 9999;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
`;
button.onclick = () => {
// 确保浮动窗口已创建
if (!floatingWindow) {
floatingWindow = createFloatingWindow();
}
// 显示窗口
floatingWindow.style.display = 'block';
// 尝试自动读取剪贴板
try {
navigator.clipboard.readText().then(clipboardText => {
if (clipboardText && clipboardText.includes('xhslink.com')) {
document.getElementById('xhs-input-text').value = clipboardText;
}
}).catch(() => {
// 忽略错误,用户可以手动粘贴
});
} catch (error) {
// 忽略错误,用户可以手动粘贴
}
};
document.body.appendChild(button);
};
// 页面加载完成后添加按钮
window.addEventListener('load', function() {
addClipboardButton();
});
})();