自动转换 ed2k 和磁力链接并一键复制

自动检测网页中的所有 ed2k 和磁力链接并将其转换为可点击的超链接,同时提供一键复制所有链接的功能,并支持排除指定网址。

  1. // ==UserScript==
  2. // @name 自动转换 ed2k 和磁力链接并一键复制
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.1
  5. // @description 自动检测网页中的所有 ed2k 和磁力链接并将其转换为可点击的超链接,同时提供一键复制所有链接的功能,并支持排除指定网址。
  6. // @author 98-liu**
  7. // @match *://*/*
  8. // @grant GM_setClipboard
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. const EXCLUDE_KEY = 'excludedUrls';
  17. const COPY_BUTTON_POSITION_KEY = 'copyButtonPosition';
  18. const CONFIG_BUTTON_POSITION_KEY = 'configButtonPosition';
  19. const TOGGLE_BUTTONS_POSITION_KEY = 'toggleButtonsPosition';
  20. const COPY_BUTTON_VISIBLE_KEY = 'copyButtonVisible';
  21. const CONFIG_BUTTON_VISIBLE_KEY = 'configButtonVisible';
  22.  
  23. // 检查当前页面是否在排除列表中
  24. function isExcluded(url) {
  25. const excludedUrls = GM_getValue(EXCLUDE_KEY, []);
  26. for (const pattern of excludedUrls) {
  27. if (new RegExp(pattern).test(url)) {
  28. return true;
  29. }
  30. }
  31. return false;
  32. }
  33.  
  34. // 自动检测并转换 ed2k 和磁力链接为超链接
  35. function convertLinks() {
  36. if (isExcluded(window.location.href)) return;
  37.  
  38. // 正则表达式匹配 ed2k 链接
  39. const ed2kRegex = /ed2k:\/\/\|file\|[^\|]+\|\d+\|[a-fA-F0-9]+\|\//g;
  40. // 正则表达式匹配磁力链接
  41. const magnetRegex = /magnet:\?xt=urn:[a-zA-Z0-9:.&=]+/g;
  42.  
  43. // 遍历网页中的所有文本节点
  44. const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
  45. let node;
  46.  
  47. while (node = walker.nextNode()) {
  48. if (node.nodeValue.match(ed2kRegex) || node.nodeValue.match(magnetRegex)) {
  49. const parent = node.parentNode;
  50.  
  51. // 避免重复处理(如果已经是超链接则跳过)
  52. if (parent.tagName !== 'A') {
  53. let newHTML = node.nodeValue;
  54. newHTML = newHTML.replace(ed2kRegex, '<a href="$&" target="_blank">$&</a>');
  55. newHTML = newHTML.replace(magnetRegex, '<a href="$&" target="_blank">$&</a>');
  56.  
  57. const temp = document.createElement('div');
  58. temp.innerHTML = newHTML;
  59.  
  60. // 将新内容插入到 DOM 中
  61. while (temp.firstChild) {
  62. parent.insertBefore(temp.firstChild, node);
  63. }
  64.  
  65. // 移除原始文本节点
  66. parent.removeChild(node);
  67. }
  68. }
  69. }
  70. }
  71.  
  72. // 获取网页中的所有 ed2k 和磁力链接,包括现有的超链接
  73. function getAllLinks() {
  74. const ed2kRegex = /ed2k:\/\/\|file\|[^\|]+\|\d+\|[a-fA-F0-9]+\|\//g;
  75. const magnetRegex = /magnet:\?xt=urn:[a-zA-Z0-9:.&=]+/g;
  76.  
  77. const links = [];
  78.  
  79. // 查找所有现有的 ed2k 和磁力超链接
  80. document.querySelectorAll('a').forEach(anchor => {
  81. if (anchor.href.match(ed2kRegex) || anchor.href.match(magnetRegex)) {
  82. links.push(anchor.href);
  83. }
  84. });
  85.  
  86. // 查找所有文本节点中的 ed2k 和磁力链接
  87. const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, null, false);
  88. let node;
  89.  
  90. while (node = walker.nextNode()) {
  91. const matches = node.nodeValue.match(ed2kRegex) || [];
  92. links.push(...matches);
  93.  
  94. const magnetMatches = node.nodeValue.match(magnetRegex) || [];
  95. links.push(...magnetMatches);
  96. }
  97.  
  98. return links.join('\n');
  99. }
  100.  
  101. // 创建一键复制按钮
  102. function createCopyButton() {
  103. const button = document.createElement('button');
  104. button.id = 'copyAllLinksButton';
  105. button.textContent = '复制所有链接';
  106. button.style.position = 'fixed';
  107. button.style.zIndex = '10000';
  108. button.style.padding = '10px';
  109. button.style.backgroundColor = '#007bff';
  110. button.style.color = '#fff';
  111. button.style.border = 'none';
  112. button.style.borderRadius = '5px';
  113. button.style.cursor = 'pointer';
  114. button.style.width = '120px'; // 固定宽度
  115. button.style.height = '40px'; // 固定高度
  116.  
  117. // 设置初始位置或恢复保存的位置
  118. const position = GM_getValue(COPY_BUTTON_POSITION_KEY, { top: '10px', left: '10px' });
  119. button.style.top = position.top;
  120. button.style.left = position.left;
  121.  
  122. // 点击按钮时复制所有链接
  123. button.addEventListener('click', () => {
  124. const links = getAllLinks();
  125. if (links) {
  126. GM_setClipboard(links);
  127. alert('已复制所有链接到剪贴板!');
  128. } else {
  129. alert('未找到链接!');
  130. }
  131. });
  132.  
  133. makeDraggable(button);
  134.  
  135. document.body.appendChild(button);
  136. }
  137.  
  138. // 保存排除网址列表
  139. function saveExcludedUrls(urls) {
  140. GM_setValue(EXCLUDE_KEY, urls);
  141. }
  142.  
  143. // 打开配置面板
  144. function openConfigPanel() {
  145. const panel = document.createElement('div');
  146. panel.style.position = 'fixed';
  147. panel.style.top = '10px';
  148. panel.style.left = '10px';
  149. panel.style.width = '300px';
  150. panel.style.height = '200px';
  151. panel.style.backgroundColor = '#fff';
  152. panel.style.border = '1px solid #ccc';
  153. panel.style.padding = '10px';
  154. panel.style.zIndex = '1001';
  155. panel.style.boxShadow = '2px 2px 10px rgba(0, 0, 0, 0.1)';
  156. document.body.appendChild(panel);
  157.  
  158. const title = document.createElement('h3');
  159. title.textContent = '排除网址配置';
  160. panel.appendChild(title);
  161.  
  162. const list = document.createElement('ul');
  163. list.style.maxHeight = '100px';
  164. list.style.overflowY = 'auto';
  165. panel.appendChild(list);
  166.  
  167. const input = document.createElement('input');
  168. input.type = 'text';
  169. input.placeholder = '输入网址模式(正则表达式)';
  170. input.style.width = 'calc(100% - 22px)';
  171. input.style.marginBottom = '5px';
  172. panel.appendChild(input);
  173.  
  174. const addButton = document.createElement('button');
  175. addButton.textContent = '添加';
  176. addButton.addEventListener('click', () => {
  177. const value = input.value.trim();
  178. if (value) {
  179. const excludedUrls = GM_getValue(EXCLUDE_KEY, []);
  180. excludedUrls.push(value);
  181. saveExcludedUrls(excludedUrls);
  182. updateList();
  183. input.value = '';
  184. }
  185. });
  186. panel.appendChild(addButton);
  187.  
  188. const closeButton = document.createElement('button');
  189. closeButton.textContent = '关闭';
  190. closeButton.style.float = 'right';
  191. closeButton.addEventListener('click', () => {
  192. document.body.removeChild(panel);
  193. });
  194. panel.appendChild(closeButton);
  195.  
  196. updateList();
  197.  
  198. function updateList() {
  199. list.innerHTML = '';
  200. const excludedUrls = GM_getValue(EXCLUDE_KEY, []);
  201. excludedUrls.forEach((pattern, index) => {
  202. const li = document.createElement('li');
  203. li.textContent = pattern;
  204. const removeButton = document.createElement('button');
  205. removeButton.textContent = '删除';
  206. removeButton.style.float = 'right';
  207. removeButton.addEventListener('click', () => {
  208. excludedUrls.splice(index, 1);
  209. saveExcludedUrls(excludedUrls);
  210. updateList();
  211. });
  212. li.appendChild(removeButton);
  213. list.appendChild(li);
  214. });
  215. }
  216. }
  217.  
  218. // 创建配置按钮
  219. function createConfigButton() {
  220. const button = document.createElement('button');
  221. button.id = 'configExcludeButton';
  222. button.textContent = '配置排除网址';
  223. button.style.position = 'fixed';
  224. button.style.zIndex = '10000';
  225. button.style.padding = '10px';
  226. button.style.backgroundColor = '#ffc107';
  227. button.style.color = '#fff';
  228. button.style.border = 'none';
  229. button.style.borderRadius = '5px';
  230. button.style.cursor = 'pointer';
  231. button.style.width = '120px'; // 固定宽度
  232. button.style.height = '40px'; // 固定高度
  233.  
  234. // 设置初始位置或恢复保存的位置
  235. const position = GM_getValue(CONFIG_BUTTON_POSITION_KEY, { top: '60px', left: '10px' });
  236. button.style.top = position.top;
  237. button.style.left = position.left;
  238.  
  239. // 点击按钮时打开配置面板
  240. button.addEventListener('click', openConfigPanel);
  241.  
  242. makeDraggable(button);
  243.  
  244. document.body.appendChild(button);
  245. }
  246.  
  247. // 创建两个切换按钮
  248. function createToggleButtons() {
  249. const container = document.createElement('div');
  250. container.style.position = 'fixed';
  251. container.style.zIndex = '10000';
  252. container.style.display = 'flex';
  253. container.style.flexDirection = 'row';
  254. container.style.alignItems = 'center';
  255.  
  256. // 设置初始位置或恢复保存的位置
  257. const position = GM_getValue(TOGGLE_BUTTONS_POSITION_KEY, { top: '110px', left: '10px' });
  258. container.style.top = position.top;
  259. container.style.left = position.left;
  260.  
  261. // 切换复制所有链接按钮
  262. const toggleCopyButton = document.createElement('button');
  263. toggleCopyButton.id = 'toggleCopyButton';
  264. toggleCopyButton.textContent = '隐藏复制按钮';
  265. toggleCopyButton.style.padding = '10px';
  266. toggleCopyButton.style.backgroundColor = '#dc3545';
  267. toggleCopyButton.style.color = '#fff';
  268. toggleCopyButton.style.border = 'none';
  269. toggleCopyButton.style.borderRadius = '5px 0 0 5px';
  270. toggleCopyButton.style.cursor = 'pointer';
  271. toggleCopyButton.style.width = '80px'; // 固定宽度
  272. toggleCopyButton.style.height = '50px'; // 固定高度
  273.  
  274. toggleCopyButton.addEventListener('click', () => {
  275. toggleCopyButtonVisibility();
  276. });
  277.  
  278. // 切换配置排除网址按钮
  279. const toggleConfigButton = document.createElement('button');
  280. toggleConfigButton.id = 'toggleConfigButton';
  281. toggleConfigButton.textContent = '隐藏配置网址按钮';
  282. toggleConfigButton.style.padding = '10px';
  283. toggleConfigButton.style.backgroundColor = '#28a745';
  284. toggleConfigButton.style.color = '#fff';
  285. toggleConfigButton.style.border = 'none';
  286. toggleConfigButton.style.borderRadius = '0 5px 5px 0';
  287. toggleConfigButton.style.cursor = 'pointer';
  288. toggleConfigButton.style.width = '80px'; // 固定宽度
  289. toggleConfigButton.style.height = '50px'; // 固定高度
  290.  
  291. toggleConfigButton.addEventListener('click', () => {
  292. toggleConfigButtonVisibility();
  293. });
  294.  
  295. // 使容器可拖动
  296. makeDraggable(container);
  297.  
  298. container.appendChild(toggleCopyButton);
  299. container.appendChild(toggleConfigButton);
  300. document.body.appendChild(container);
  301. }
  302.  
  303. // 使元素可拖动
  304. function makeDraggable(element) {
  305. let offsetX, offsetY;
  306. let isDragging = false;
  307.  
  308. element.addEventListener('mousedown', (e) => {
  309. if (e.target === element || e.target.parentElement === element) {
  310. offsetX = e.clientX - element.offsetLeft;
  311. offsetY = e.clientY - element.offsetTop;
  312. isDragging = true;
  313. document.addEventListener('mousemove', mouseMoveHandler);
  314. document.addEventListener('mouseup', mouseUpHandler);
  315. }
  316. });
  317.  
  318. function mouseMoveHandler(e) {
  319. if (!isDragging) return;
  320.  
  321. const maxX = window.innerWidth - element.offsetWidth;
  322. const maxY = window.innerHeight - element.offsetHeight;
  323. element.style.left = `${Math.min(maxX, Math.max(0, e.clientX - offsetX))}px`;
  324. element.style.top = `${Math.min(maxY, Math.max(0, e.clientY - offsetY))}px`;
  325.  
  326. // 更新位置信息
  327. if (element.id === 'toggleButtonsContainer') {
  328. GM_setValue(TOGGLE_BUTTONS_POSITION_KEY, { top: element.style.top, left: element.style.left });
  329. }
  330. }
  331.  
  332. function mouseUpHandler() {
  333. isDragging = false;
  334. document.removeEventListener('mousemove', mouseMoveHandler);
  335. document.removeEventListener('mouseup', mouseUpHandler);
  336. }
  337. }
  338.  
  339. // 切换复制所有链接按钮的显示状态
  340. function toggleCopyButtonVisibility() {
  341. const copyButton = document.getElementById('copyAllLinksButton');
  342. const toggleCopyButton = document.getElementById('toggleCopyButton');
  343.  
  344. const isVisible = !GM_getValue(COPY_BUTTON_VISIBLE_KEY, true);
  345.  
  346. if (isVisible) {
  347. copyButton.style.display = 'block';
  348. toggleCopyButton.textContent = '隐藏复制按钮';
  349. } else {
  350. copyButton.style.display = 'none';
  351. toggleCopyButton.textContent = '显示复制按钮';
  352. }
  353.  
  354. GM_setValue(COPY_BUTTON_VISIBLE_KEY, isVisible);
  355. }
  356.  
  357. // 切换配置排除网址按钮的显示状态
  358. function toggleConfigButtonVisibility() {
  359. const configButton = document.getElementById('configExcludeButton');
  360. const toggleConfigButton = document.getElementById('toggleConfigButton');
  361.  
  362. const isVisible = !GM_getValue(CONFIG_BUTTON_VISIBLE_KEY, true);
  363.  
  364. if (isVisible) {
  365. configButton.style.display = 'block';
  366. toggleConfigButton.textContent = '隐藏配置按钮';
  367. } else {
  368. configButton.style.display = 'none';
  369. toggleConfigButton.textContent = '显示配置按钮';
  370. }
  371.  
  372. GM_setValue(CONFIG_BUTTON_VISIBLE_KEY, isVisible);
  373. }
  374.  
  375. // 在页面加载完成后执行
  376. window.addEventListener('load', () => {
  377. if (!isExcluded(window.location.href)) {
  378. convertLinks();
  379. createCopyButton();
  380. createConfigButton();
  381. createToggleButtons();
  382.  
  383. // 初始化按钮显示状态
  384. const copyButtonVisible = GM_getValue(COPY_BUTTON_VISIBLE_KEY, true);
  385. const configButtonVisible = GM_getValue(CONFIG_BUTTON_VISIBLE_KEY, true);
  386.  
  387. const copyButton = document.getElementById('copyAllLinksButton');
  388. const configButton = document.getElementById('configExcludeButton');
  389. const toggleCopyButton = document.getElementById('toggleCopyButton');
  390. const toggleConfigButton = document.getElementById('toggleConfigButton');
  391.  
  392. if (!copyButtonVisible) {
  393. copyButton.style.display = 'none';
  394. toggleCopyButton.textContent = '显示复制按钮';
  395. }
  396.  
  397. if (!configButtonVisible) {
  398. configButton.style.display = 'none';
  399. toggleConfigButton.textContent = '显示配置按钮';
  400. }
  401. } else {
  402. // 如果页面在排除列表中,移除可能存在的按钮
  403. const copyButton = document.getElementById('copyAllLinksButton');
  404. if (copyButton) {
  405. document.body.removeChild(copyButton);
  406. }
  407. const configButton = document.getElementById('configExcludeButton');
  408. if (configButton) {
  409. document.body.removeChild(configButton);
  410. }
  411. const toggleButtonsContainer = document.getElementById('toggleButtonsContainer');
  412. if (toggleButtonsContainer) {
  413. document.body.removeChild(toggleButtonsContainer);
  414. }
  415. }
  416. });
  417.  
  418. // 监听动态内容加载(例如 AJAX)
  419. const observer = new MutationObserver(() => {
  420. if (!isExcluded(window.location.href)) {
  421. convertLinks();
  422. }
  423. });
  424. observer.observe(document.body, { childList: true, subtree: true });
  425. })();
  426.  
  427.  
  428.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址