SimpCity Video & Image Link Copier

Collect and copy video/image links across current or all XenForo thread pages, one page at a time, using localStorage clipboard workaround (Cloudflare safe). Supports separate video/image modes with domain matching.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         SimpCity Video & Image Link Copier
// @version      2.0
// @namespace    RiisDevScripts
// @description  Collect and copy video/image links across current or all XenForo thread pages, one page at a time, using localStorage clipboard workaround (Cloudflare safe). Supports separate video/image modes with domain matching.
// @author       https://github.com/RiisDev/
// @match        *https://simpcity.cr/threads/*
// @grant        GM_setClipboard
// @icon         https://simpcity.cr/data/assets/logo/favicon.png
// @run-at       document-end
// @license      MIT
// ==/UserScript==

(function () {
	'use strict';

	function showChoiceDialog(autoCopy) {
		const overlay = document.createElement('div');
		overlay.style.cssText = `
        	position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;
			background: rgba(0,0,0,0.3);
			backdrop-filter: blur(5px);
			-webkit-backdrop-filter: blur(5px);
			display: flex; align-items: center; justify-content: center;
			z-index: 10000;
		`;

		const modal = document.createElement('div');
		modal.className = 'p-body-background p-body-background--main';
		modal.style.cssText = `
			background: var(--xf-palette-pureWhite);
			border-radius: 6px;
			padding: 20px;
			max-width: 320px;
			text-align: center;
			box-shadow: 0 4px 12px rgba(0,0,0,0.15);
		`;

		const title = document.createElement('h3');
		title.textContent = 'Auto Copy Links';
		title.style.marginBottom = '12px';

		const prompt = document.createElement('p');
		prompt.textContent = 'Which links do you want to auto copy across all pages?';
		prompt.style.marginBottom = '20px';

		const btnContainer = document.createElement('div');
		btnContainer.style.display = 'flex';
		btnContainer.style.justifyContent = 'space-between';
		btnContainer.style.gap = '10px';

		function createOptionBtn(text, type) {
			const btn = document.createElement('button');
			btn.className = 'button--primary button rippleButton';
			btn.textContent = text;
			btn.style.flex = '1';
			if (autoCopy){
				btn.addEventListener('click', () => {
					document.body.removeChild(overlay);
					startAutoCopy(type);
				});
			}
			else {
				btn.addEventListener('click', () => {
					const links = extractLinks(type);
					if (links.length > 0) {
						GM_setClipboard(links.join('\n'));
						showXenforoNotice(`✅ Copied ${links.length} ${type} link(s)!`);
					} else {
						showXenforoNotice(`⚠️ No ${type} links found.`);
					}
					document.body.removeChild(overlay);
					
					localStorage.removeItem(XF_LINKS_TYPE);
					localStorage.removeItem(STORAGE_KEY);
				});
			}
			return btn;
		}

		const btnVideo = createOptionBtn('Video', 'video');
		const btnImage = createOptionBtn('Image', 'image');
		const btnBoth = createOptionBtn('Both', 'all');

		const btnCancel = document.createElement('button');
		btnCancel.className = 'button--link button rippleButton';
		btnCancel.textContent = 'Cancel';
		btnCancel.style.flex = '1';
		btnCancel.addEventListener('click', () => {
			document.body.removeChild(overlay);
			if (autoCopy) {
				showXenforoNotice('Auto copy cancelled.', 'warning');
			} else {
				showXenforoNotice('Copy cancelled.', 'warning');
			}
		});

		btnContainer.appendChild(btnVideo);
		btnContainer.appendChild(btnImage);
		btnContainer.appendChild(btnBoth);
		btnContainer.appendChild(btnCancel);

		modal.appendChild(title);
		modal.appendChild(prompt);
		modal.appendChild(btnContainer);
		overlay.appendChild(modal);
		document.body.appendChild(overlay);
	}

	function showXenforoNotice(message, type = 'success', duration = 3000) {
		const notice = document.createElement('div');
		notice.className = `p-notice p-notice--${type}`;
		notice.style.cssText = `
            position: fixed;
            top: 80px;
            right: 30px;
            z-index: 9999;
            max-width: 300px;
            padding: 10px 15px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.3);
            border-radius: 5px;
            font-size: 14px;
            opacity: 0;
            border: 2px solid hsla(var(--xf-textColorEmphasized));
            animation: fadeInOut 0.3s forwards;
        `;
		notice.innerHTML = `<div class="p-notice__content">${message}</div>`;
		document.body.appendChild(notice);

		setTimeout(() => {
			notice.style.animation = 'fadeOut 0.3s forwards';
			setTimeout(() => notice.remove(), 300);
		}, duration);
	}

	const style = document.createElement('style');
	style.textContent = `
        @keyframes fadeInOut {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        @keyframes fadeOut {
            from { opacity: 1; }
            to { opacity: 0; transform: translateY(-10px); }
        }
    `;
	document.head.appendChild(style);

	const XF_LINKS_TYPE = 'xf_links_type';
	const STORAGE_KEY = 'xf_links_collector';
	const imageDomains = ['bunkr', 'cyberdrop', 'pixeldrain', 'ibb.co', 'postimg', 'jpg5.su'];
	const videoDomains = ['gofile', 'pixeldrain', 'mega', 'mediafire', 'xhamster', 'pornhub', 'bunkr', 'cyberdrop', 'cyberfile', 'saint2', 'pvvstream', 'noodlemagazine.com', 'xvideos', 'vk'];

	const isVideoLink = url => { try { return videoDomains.some(domain => new URL(url).hostname.includes(domain)); } catch { return false; } };
	const isImageLink = url => { try { return imageDomains.some(domain => new URL(url).hostname.includes(domain)); } catch { return false; } };
	function GetStorageLinks() { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); }
	function GetType() { return localStorage.getItem(XF_LINKS_TYPE); }

	function extractLinks(type) {
		if (!GetType()) { localStorage.setItem(XF_LINKS_TYPE, type);}

		const links = new Set();

		const anchors = document.querySelectorAll('.message--post a[href]');
		anchors.forEach(a => {
			const href = a.href.trim();
			if (
				href &&
				(
					(type === 'video' && isVideoLink(href)) ||
					(type === 'image' && isImageLink(href)) ||
					(type === 'all' && (isVideoLink(href) || isImageLink(href)))
				)
			) {
				links.add(href);
			}
		});

		const iframes = document.querySelectorAll('iframe.saint-iframe[src]');
		if (type !== 'image') {
			iframes.forEach(iframe => {
				const src = iframe.src.trim();
				if (src && isVideoLink(src)) {
					links.add(src);
				}
			});
		}

		return Array.from(links);
	};

	function getNextPageUrl() {
		const nextBtn = document.querySelector('.pageNav-jump--next');
		return nextBtn ? nextBtn.href : null;
	};

	function saveLinks(newLinks) {
		const existing = GetStorageLinks();
		const merged = [...new Set([...existing, ...newLinks])];
		localStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
		return merged;
	};

	function copyAndFinish(type) {
		const allLinks = GetStorageLinks();
		if (allLinks.length > 0) {
			GM_setClipboard(allLinks.join('\n'));
			showXenforoNotice(`✅ Copied ${allLinks.length} ${type} link(s) from all pages!`);
		} else {
			showXenforoNotice(`⚠️ No ${type} links were found.`);
		}

		localStorage.removeItem(STORAGE_KEY);
		localStorage.removeItem(XF_LINKS_TYPE);
	};

	function startAutoCopy(type) {
		localStorage.removeItem(STORAGE_KEY);
		localStorage.removeItem(XF_LINKS_TYPE);

		const links = extractLinks(type);
		saveLinks(links);

		const nextPage = getNextPageUrl();
		if (nextPage) {
			window.location.href = nextPage;
		} else {
			copyAndFinish(type);
		}
	}
	
	function autoContinueIfNeeded() {
		const linksExist = GetStorageLinks();
		const type = GetType();
		if (!linksExist || !type) return;

		const links = extractLinks(type);
		saveLinks(links);

		const nextPage = getNextPageUrl();
		if (nextPage) {
			setTimeout(() => {
				window.location.href = nextPage;
			}, 1000);
		} else {
			copyAndFinish(type);
		}
	};

	function addButton(label, clickHandler) {
		const buttonGroup = document.querySelector('.buttonGroup');
		if (!buttonGroup) return;

		const btn = document.createElement('a');
		btn.href = '#';
		btn.className = 'button--link button rippleButton';
		btn.innerHTML = `<span class="button-text">${label}</span>`;
		btn.addEventListener('click', e => { e.preventDefault(); clickHandler(); });
		buttonGroup.prepend(btn);
	};

	function addButtons() {
		addButton('📋 Start Auto Copy', () => {
			showChoiceDialog(true);
		});

		addButton('📋 Copy Links', () => {
			showChoiceDialog(false);
		});
	};

	window.addEventListener('load', () => {
		addButtons();
		autoContinueIfNeeded();
	});
})();