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.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==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();
	});
})();