Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Archive Links for Images (Throttled, Non-duplicating)
- // @namespace https://perplexity.ai
- // @version 1.0.0
- // @description Adds archive/wayback links under valid images in posting areas, throttled and deduplicated.
- // @author Perplexity QA AI
- // @include */threads/*
- // @grant none
- // @run-at document-end
- // ==/UserScript==
- (function() {
- 'use strict';
- // ---- Configurable: posting area selector ----
- const POSTING_AREA_SELECTOR = 'body'; // Change e.g. '.message-body' as needed
- // ---- Exclusion logic, per spec ----
- function isExcluded(src) {
- if (!src) return true;
- return (
- src.includes('https://static.cdninstagram.com') ||
- src.includes('/data/avatars/') ||
- src.includes('twimg.com/profile_images') ||
- src.includes('twitter.com/profile_images') ||
- src.startsWith('https://www.thecoli.com/styles/') ||
- src.startsWith('data:image/') ||
- src.startsWith('https://cdn.jsdelivr.net') ||
- src.toLowerCase().includes('unicode') ||
- src.toLowerCase().includes('favicon')
- );
- }
- // ---- Marking processed images for deduplication ----
- const ARCHIVE_MARK = 'data-archive-links';
- // ---- Archive Link Generation ----
- function createArchiveLinks(url) {
- const archiveUrl = 'https://web.archive.org/save/' + encodeURIComponent(url);
- const archiveIsUrl = 'https://archive.is/?run=1&url=' + encodeURIComponent(url);
- const viewArchiveUrl = 'https://web.archive.org/web/*/' + encodeURIComponent(url);
- // Container
- const div = document.createElement('div');
- div.style.textAlign = 'center';
- div.style.fontSize = '12px';
- div.style.marginTop = '2px';
- // Archive (Wayback Save) link
- const aArchive = document.createElement('a');
- aArchive.href = archiveUrl;
- aArchive.textContent = 'archive';
- aArchive.target = '_blank';
- aArchive.rel = 'noopener noreferrer';
- aArchive.style.textDecoration = 'underline';
- // Archive.is link
- const aArchiveIs = document.createElement('a');
- aArchiveIs.href = archiveIsUrl;
- aArchiveIs.textContent = 'archive.is';
- aArchiveIs.target = '_blank';
- aArchiveIs.rel = 'noopener noreferrer';
- aArchiveIs.style.textDecoration = 'underline';
- aArchiveIs.style.margin = '0 5px';
- // View archive (Wayback Index)
- const aView = document.createElement('a');
- aView.href = viewArchiveUrl;
- aView.textContent = 'view archive';
- aView.target = '_blank';
- aView.rel = 'noopener noreferrer';
- aView.style.textDecoration = 'underline';
- div.appendChild(aArchive);
- div.appendChild(document.createTextNode(' | '));
- div.appendChild(aArchiveIs);
- div.appendChild(document.createTextNode(' | '));
- div.appendChild(aView);
- return div;
- }
- // ---- Main processing logic ----
- function processImages() {
- const area = document.querySelector(POSTING_AREA_SELECTOR);
- if (!area) return;
- area.querySelectorAll('img').forEach(img => {
- if (img.hasAttribute(ARCHIVE_MARK)) return; // Already processed
- const src = img.getAttribute('src') || '';
- if (isExcluded(src)) return;
- img.setAttribute(ARCHIVE_MARK, '1');
- const links = createArchiveLinks(src);
- // Insert after image, in DOM order
- if (img.nextSibling) {
- img.parentNode.insertBefore(links, img.nextSibling);
- } else {
- img.parentNode.appendChild(links);
- }
- });
- }
- // ---- Throttled mutation observer logic ----
- let throttleTimeout = null;
- function throttledProcess() {
- if (throttleTimeout) return;
- throttleTimeout = setTimeout(() => {
- throttleTimeout = null;
- processImages();
- }, 300); // 300ms throttle; adjust if needed
- }
- // Observe entire posting area for changes
- const observer = new MutationObserver(throttledProcess);
- observer.observe(document.querySelector(POSTING_AREA_SELECTOR), {
- childList: true,
- subtree: true
- });
- // Process initially on DOM ready
- processImages();
- // Self-check mechanism to prevent excessive CPU usage on complex pages
- let cycleCount = 0;
- const MAX_CYCLES = 10;
- const cycleMonitor = setInterval(() => {
- if (++cycleCount > MAX_CYCLES) {
- // If we're still active after many cycles, reduce observation sensitivity
- observer.disconnect();
- // Reconnect with more conservative settings
- observer.observe(document.querySelector(POSTING_AREA_SELECTOR), {
- childList: true,
- subtree: true,
- attributes: false,
- characterData: false
- });
- clearInterval(cycleMonitor);
- }
- }, 5000);
- // Cleanup function - explicit disconnect when page unloads
- window.addEventListener('beforeunload', () => {
- observer.disconnect();
- clearInterval(cycleMonitor);
- if (throttleTimeout) {
- clearTimeout(throttleTimeout);
- throttleTimeout = null;
- }
- }, { once: true });
- // Handle potential errors to prevent script failure
- window.addEventListener('error', (e) => {
- if (e.error && e.error.message && e.error.message.includes('observer')) {
- console.error('Image Archive Links: Observer error detected, reconnecting');
- try {
- observer.disconnect();
- setTimeout(() => {
- observer.observe(document.querySelector(POSTING_AREA_SELECTOR), {
- childList: true,
- subtree: true
- });
- }, 1000);
- } catch (err) {
- console.error('Image Archive Links: Recovery failed');
- }
- }
- });
- })();
Advertisement
Add Comment
Please, Sign In to add comment