Greasy Fork 还支持 简体中文。

Universal Link Extractor

Estrae link con filtri di inclusione specifici per ogni sito, con GUI e toggle.

  1. // ==UserScript==
  2. // @name Universal Link Extractor
  3. // @namespace http://tampermonkey.net/
  4. // @version 3.0
  5. // @description Estrae link con filtri di inclusione specifici per ogni sito, con GUI e toggle.
  6. // @author ChatGPT (modificato)
  7. // @match *://*/*
  8. // @grant GM_registerMenuCommand
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @license MIT
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. const currentHost = location.host;
  18. const enabledSites = JSON.parse(GM_getValue('enabledSites', '[]'));
  19.  
  20. // Struttura per salvare i filtri specifici per ogni sito
  21. // { "example.com": ["pattern1", "pattern2"], "another.com": ["pattern3"] }
  22. const siteFilters = JSON.parse(GM_getValue('siteFilters', '{}'));
  23.  
  24. // Menu per abilitare/disabilitare lo script su questo sito
  25. GM_registerMenuCommand(
  26. enabledSites.includes(currentHost)
  27. ? 'Disabilita Universal Link Extractor su questo sito'
  28. : 'Abilita Universal Link Extractor su questo sito',
  29. () => {
  30. let sites = JSON.parse(GM_getValue('enabledSites', '[]'));
  31. if (sites.includes(currentHost)) {
  32. sites = sites.filter(s => s !== currentHost);
  33. GM_setValue('enabledSites', JSON.stringify(sites));
  34. alert('Script disabilitato su: ' + currentHost);
  35. } else {
  36. sites.push(currentHost);
  37. GM_setValue('enabledSites', JSON.stringify(sites));
  38.  
  39. // Se non esistono filtri per questo sito, crea un default
  40. if (!siteFilters[currentHost]) {
  41. siteFilters[currentHost] = [`^https://${currentHost}/.*`];
  42. GM_setValue('siteFilters', JSON.stringify(siteFilters));
  43. }
  44.  
  45. alert('Script abilitato su: ' + currentHost);
  46. }
  47. }
  48. );
  49.  
  50. // Se non abilitato, esci
  51. if (!enabledSites.includes(currentHost)) return;
  52.  
  53. // GUI principale
  54. const panel = document.createElement('div');
  55. Object.assign(panel.style, {
  56. position: 'fixed', top: '50px', right: '10px',
  57. width: '360px', maxHeight: '300px', overflowY: 'auto', overflowX: 'hidden',
  58. backgroundColor: 'rgba(0,0,0,0.85)', color: '#fff',
  59. border: '1px solid #444', borderRadius: '4px',
  60. boxShadow: '0 2px 8px rgba(0,0,0,0.6)', zIndex: '999999',
  61. fontFamily: 'Arial, sans-serif', fontSize: '14px', lineHeight: '1.4',
  62. transition: 'max-height 0.3s ease, opacity 0.3s ease', opacity: '1'
  63. });
  64.  
  65. // Ottieni i filtri del sito corrente
  66. const currentSiteFilters = siteFilters[currentHost] || [`^https://${currentHost}/.*`];
  67.  
  68. panel.innerHTML = `
  69. <div style="padding:12px;">
  70. <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
  71. <strong>Universal Link Extractor</strong>
  72. <button id="extractBtn" style="background:#28a745; border:none; color:#fff; padding:4px 8px; border-radius:3px; cursor:pointer;">Estrai link</button>
  73. </div>
  74. <div style="font-weight:bold; margin-bottom:8px;">Come usare:</div>
  75. <ul style="padding-left:20px; margin:0 0 8px 0;">
  76. <li>Clicca "Estrai link" per copiare quelli che matchano i filtri.</li>
  77. <li>Configura filtri di <strong>inclusione</strong> specifici per ${currentHost}.</li>
  78. </ul>
  79. <div style="font-weight:bold; margin-bottom:4px;">Filtri attivi per ${currentHost}:</div>
  80. <pre style="background:#f4f4f4; color:#000; padding:10px; border-radius:4px; font-family:monospace; font-size:13px; max-height:80px; overflow-y:auto;">
  81. ${currentSiteFilters.join('\n')}
  82. </pre>
  83. <div style="font-size:12px; color:#ccc; margin-top:8px;">Apri/chiudi con la freccia blu in alto a destra.</div>
  84. </div>
  85. `;
  86. document.body.appendChild(panel);
  87.  
  88. // Listener Estrai
  89. document.getElementById('extractBtn').addEventListener('click', extractLinks);
  90.  
  91. // Toggle sempre visibile
  92. const toggle = document.createElement('div');
  93. Object.assign(toggle.style, {
  94. position: 'fixed', top: '10px', right: '10px',
  95. width: '32px', height: '32px', cursor: 'pointer',
  96. backgroundColor: '#007bff', color: '#fff', display: 'flex',
  97. alignItems: 'center', justifyContent: 'center',
  98. borderRadius: '4px', fontSize: '18px', fontWeight: 'bold',
  99. boxShadow: '0 2px 4px rgba(0,0,0,0.5)', zIndex: '1000000',
  100. userSelect: 'none', transition: 'opacity 0.3s ease', opacity: '1'
  101. });
  102. toggle.textContent = '▼';
  103. document.body.appendChild(toggle);
  104.  
  105. let expanded = true;
  106. toggle.addEventListener('click', () => {
  107. expanded = !expanded;
  108. if (expanded) {
  109. panel.style.maxHeight = '300px'; panel.style.opacity = '1'; toggle.textContent = '▼'; toggle.style.opacity = '1';
  110. } else {
  111. panel.style.maxHeight = '0'; panel.style.opacity = '0'; toggle.textContent = '▲'; toggle.style.opacity = '0';
  112. }
  113. });
  114.  
  115. // Menu comando per configurare i filtri specifici per il sito
  116. GM_registerMenuCommand(`Configura filtri di inclusione per ${currentHost}`, () => {
  117. const defaultVal = currentSiteFilters.join(',');
  118. const inp = prompt(`Regex da includere per ${currentHost} (separa con virgola):`, defaultVal);
  119.  
  120. if (inp !== null) {
  121. const newFilters = inp.split(',').map(s => s.trim()).filter(s => s);
  122.  
  123. // Aggiorna i filtri specifici per questo sito
  124. siteFilters[currentHost] = newFilters;
  125. GM_setValue('siteFilters', JSON.stringify(siteFilters));
  126.  
  127. alert(`Filtri di inclusione aggiornati per ${currentHost}`);
  128. // Refresh per aggiornare l'interfaccia
  129. location.reload();
  130. }
  131. });
  132.  
  133. // Estrai link basandosi solo sui filtri del sito corrente
  134. function matchesAny(link, patterns) {
  135. return patterns.some(pat => {
  136. try {
  137. return new RegExp(pat).test(link);
  138. } catch {
  139. return false;
  140. }
  141. });
  142. }
  143.  
  144. function extractLinks() {
  145. const currentFilters = siteFilters[currentHost] || [`^https://${currentHost}/.*`];
  146.  
  147. const links = Array.from(document.querySelectorAll('a[href]'))
  148. .map(a => a.href)
  149. .filter(l => matchesAny(l, currentFilters));
  150.  
  151. if (!links.length) return alert('Nessun link trovato');
  152.  
  153. const count = links.length;
  154. navigator.clipboard.writeText(links.join('\n')).then(() => {
  155. console.log('Link estratti:', links);
  156. alert(`Ho copiato ${count} link negli appunti`);
  157. });
  158. }
  159. })();