- // ==UserScript==
- // @name Site Filter (Protocol-Independent)
- // @namespace http://tampermonkey.net/
- // @version 2.2
- // @description Manage allowed sites dynamically and reference this in other scripts.
- // @author blvdmd
- // @match *://*/*
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_registerMenuCommand
- // @grant GM_download
- // @run-at document-start
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- const USE_EMOJI_FOR_STATUS = true; // Configurable flag to use emoji for true/false status
- const SHOW_STATUS_ONLY_IF_TRUE = true; // Configurable flag to show status only if any value is true
-
- // ✅ Wait for `SCRIPT_STORAGE_KEY` to be set
- function waitForScriptStorageKey(maxWait = 1000) {
- return new Promise(resolve => {
- const startTime = Date.now();
- const interval = setInterval(() => {
- if (typeof window.SCRIPT_STORAGE_KEY !== 'undefined') {
- clearInterval(interval);
- resolve(window.SCRIPT_STORAGE_KEY);
- } else if (Date.now() - startTime > maxWait) {
- clearInterval(interval);
- console.error("🚨 SCRIPT_STORAGE_KEY is not set! Make sure your script sets it **before** @require.");
- resolve(null);
- }
- }, 50);
- });
- }
-
- (async function initialize() {
- async function waitForDocumentReady() {
- if (document.readyState === "complete") return;
- return new Promise(resolve => {
- window.addEventListener("load", resolve, { once: true });
- });
- }
- // ✅ Wait for the script storage key
- const key = await waitForScriptStorageKey();
- if (!key) return;
-
- // ✅ Ensure the document is fully loaded before setting `shouldRunOnThisSite`
- await waitForDocumentReady();
-
- const STORAGE_KEY = `additionalSites_${key}`;
-
- function getDefaultList() {
- return typeof window.GET_DEFAULT_LIST === "function" ? window.GET_DEFAULT_LIST() : [];
- }
-
- function normalizeUrl(url) {
- if (typeof url !== 'string') {
- url = String(url);
- }
- return url.replace(/^https?:\/\//, '');
- }
-
- let additionalSites = GM_getValue(STORAGE_KEY, []);
-
- let mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(item => {
- if (typeof item === 'string') {
- return { pattern: normalizeUrl(item), preProcessingRequired: false, postProcessingRequired: false };
- }
- return { ...item, pattern: normalizeUrl(item.pattern) };
- });
-
- GM_registerMenuCommand("➕ Add Current Site to Include List", addCurrentSiteMenu);
- GM_registerMenuCommand("📜 View Included Sites", viewIncludedSites);
- GM_registerMenuCommand("🗑️ Delete Specific Entries", deleteEntries);
- GM_registerMenuCommand("✏️ Edit an Entry", editEntry);
- GM_registerMenuCommand("🚨 Clear All Entries", clearAllEntries);
- GM_registerMenuCommand("📤 Export Site List as JSON", exportAdditionalSites);
- GM_registerMenuCommand("📥 Import Site List from JSON", importAdditionalSites);
-
- async function shouldRunOnThisSite() {
- const currentFullPath = normalizeUrl(`${window.location.href}`);
- return mergedSites.some(item => wildcardToRegex(normalizeUrl(item.pattern)).test(currentFullPath));
- }
-
- function wildcardToRegex(pattern) {
- return new RegExp("^" + pattern
- .replace(/[-[\]{}()+^$|#\s]/g, '\\$&')
- .replace(/\./g, '\\.')
- .replace(/\?/g, '\\?')
- .replace(/\*/g, '.*')
- + "$");
- }
-
- function addCurrentSiteMenu() {
- const currentHost = window.location.hostname;
- const currentPath = window.location.pathname;
- const domainParts = currentHost.split('.');
- const baseDomain = domainParts.length > 2 ? domainParts.slice(-2).join('.') : domainParts.join('.');
- const secondLevelDomain = domainParts.length > 2 ? domainParts.slice(-2, -1)[0] : domainParts[0];
-
- const options = [
- { name: `Preferred Domain Match (*${secondLevelDomain}.*)`, pattern: `*${secondLevelDomain}.*` },
- { name: `Base Hostname (*.${baseDomain}*)`, pattern: `*.${baseDomain}*` },
- { name: `Base Domain (*.${secondLevelDomain}.*)`, pattern: `*.${secondLevelDomain}.*` },
- { name: `Host Contains (*${secondLevelDomain}*)`, pattern: `*${secondLevelDomain}*` },
- { name: `Exact Path (${currentHost}${currentPath})`, pattern: normalizeUrl(`${window.location.href}`) },
- { name: "Custom Wildcard Pattern", pattern: normalizeUrl(`${window.location.href}`) }
- ];
-
- const userChoice = prompt(
- "Select an option to add the site:\n" +
- options.map((opt, index) => `${index + 1}. ${opt.name}`).join("\n") +
- "\nEnter a number or cancel."
- );
-
- if (!userChoice) return;
- const selectedIndex = parseInt(userChoice, 10) - 1;
- if (selectedIndex >= 0 && selectedIndex < options.length) {
- let pattern = normalizeUrl(options[selectedIndex].pattern);
- if (options[selectedIndex].name === "Custom Wildcard Pattern") {
- pattern = normalizeUrl(prompt("Edit custom wildcard pattern:", pattern));
- if (!pattern.trim()) return alert("Invalid pattern. Operation canceled.");
- }
-
- const preProcessingRequired = prompt("Is pre-processing required? (y/n)", "n").toLowerCase() === 'y';
- const postProcessingRequired = prompt("Is post-processing required? (y/n)", "n").toLowerCase() === 'y';
-
- const entry = { pattern, preProcessingRequired, postProcessingRequired };
-
- if (!additionalSites.some(item => item.pattern === pattern)) {
- additionalSites.push(entry);
- GM_setValue(STORAGE_KEY, additionalSites);
- mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(item => {
- if (typeof item === 'string') {
- return { pattern: normalizeUrl(item), preProcessingRequired: false, postProcessingRequired: false };
- }
- return { ...item, pattern: normalizeUrl(item.pattern) };
- });
- alert(`✅ Added site with pattern: ${pattern}`);
- } else {
- alert(`⚠️ Pattern "${pattern}" is already in the list.`);
- }
- }
- }
-
- function viewIncludedSites() {
- const siteList = additionalSites.map(item => {
- const status = formatStatus(item.preProcessingRequired, item.postProcessingRequired);
- return `${item.pattern}${status ? ` (${status})` : ''}`;
- }).join("\n");
- alert(`🔍 Included Sites:\n${siteList || "No sites added yet."}`);
- }
-
- function deleteEntries() {
- if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to delete.");
- const userChoice = prompt("Select entries to delete (comma-separated numbers):\n" +
- additionalSites.map((item, index) => `${index + 1}. ${item.pattern}`).join("\n"));
- if (!userChoice) return;
- const indicesToRemove = userChoice.split(',').map(num => parseInt(num.trim(), 10) - 1);
- additionalSites = additionalSites.filter((_, index) => !indicesToRemove.includes(index));
- GM_setValue(STORAGE_KEY, additionalSites);
- mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(item => {
- if (typeof item === 'string') {
- return { pattern: normalizeUrl(item), preProcessingRequired: false, postProcessingRequired: false };
- }
- return { ...item, pattern: normalizeUrl(item.pattern) };
- });
- alert("✅ Selected entries have been deleted.");
- }
-
- function editEntry() {
- if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to edit.");
- const userChoice = prompt("Select an entry to edit:\n" +
- additionalSites.map((item, index) => {
- const status = formatStatus(item.preProcessingRequired, item.postProcessingRequired);
- return `${index + 1}. ${item.pattern}${status ? ` (${status})` : ''}`;
- }).join("\n"));
- if (!userChoice) return;
- const selectedIndex = parseInt(userChoice, 10) - 1;
- if (selectedIndex < 0 || selectedIndex >= additionalSites.length) return alert("❌ Invalid selection.");
- const entry = additionalSites[selectedIndex];
- const newPattern = normalizeUrl(prompt("Edit the pattern:", entry.pattern));
- if (!newPattern || !newPattern.trim()) return;
-
- const preProcessingRequired = prompt("Is pre-processing required? (y/n)", entry.preProcessingRequired ? "y" : "n").toLowerCase() === 'y';
- const postProcessingRequired = prompt("Is post-processing required? (y/n)", entry.postProcessingRequired ? "y" : "n").toLowerCase() === 'y';
-
- entry.pattern = newPattern.trim();
- entry.preProcessingRequired = preProcessingRequired;
- entry.postProcessingRequired = postProcessingRequired;
- GM_setValue(STORAGE_KEY, additionalSites);
- mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(item => {
- if (typeof item === 'string') {
- return { pattern: normalizeUrl(item), preProcessingRequired: false, postProcessingRequired: false };
- }
- return { ...item, pattern: normalizeUrl(item.pattern) };
- });
- alert("✅ Entry updated.");
- }
-
- function clearAllEntries() {
- if (additionalSites.length === 0) return alert("⚠️ No user-defined entries to clear.");
- if (confirm(`🚨 You have ${additionalSites.length} entries. Clear all?`)) {
- additionalSites = [];
- GM_setValue(STORAGE_KEY, additionalSites);
- mergedSites = [...getDefaultList()].map(item => {
- if (typeof item === 'string') {
- return { pattern: normalizeUrl(item), preProcessingRequired: false, postProcessingRequired: false };
- }
- return { ...item, pattern: normalizeUrl(item.pattern) };
- });
- alert("✅ All user-defined entries cleared.");
- }
- }
-
- // function exportAdditionalSites() {
- // GM_download("data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(additionalSites, null, 2)), "additionalSites_backup.json");
- // alert("📤 Additional sites exported as JSON.");
- // }
-
- function exportAdditionalSites() {
- const data = JSON.stringify(additionalSites, null, 2);
- const blob = new Blob([data], { type: 'application/json' });
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'additionalSites_backup.json';
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- alert("📤 Additional sites exported as JSON.");
- }
-
- // function importAdditionalSites() {
- // const input = document.createElement("input");
- // input.type = "file";
- // input.accept = ".json";
- // input.onchange = event => {
- // const reader = new FileReader();
- // reader.onload = e => {
- // additionalSites = JSON.parse(e.target.result);
- // GM_setValue(STORAGE_KEY, additionalSites);
- // mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(item => {
- // if (typeof item === 'string') {
- // return { pattern: normalizeUrl(item), preProcessingRequired: false, postProcessingRequired: false };
- // }
- // return { ...item, pattern: normalizeUrl(item.pattern) };
- // });
- // alert("📥 Sites imported successfully.");
- // };
- // reader.readAsText(event.target.files[0]);
- // };
- // input.click();
- // }
-
- function importAdditionalSites() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
- input.style.display = 'none';
- input.onchange = event => {
- const reader = new FileReader();
- reader.onload = e => {
- try {
- const importedData = JSON.parse(e.target.result);
- if (Array.isArray(importedData)) {
- additionalSites = importedData.map(item => {
- if (typeof item === 'string') {
- return normalizeUrl(item);
- } else if (typeof item === 'object' && item.pattern) {
- return { ...item, pattern: normalizeUrl(item.pattern) };
- }
- throw new Error('Invalid data format');
- });
- GM_setValue(STORAGE_KEY, additionalSites);
- mergedSites = [...new Set([...getDefaultList(), ...additionalSites])].map(item => {
- if (typeof item === 'string') {
- return normalizeUrl(item);
- }
- return { ...item, pattern: normalizeUrl(item.pattern) };
- });
- alert('📥 Sites imported successfully.');
- } else {
- throw new Error('Invalid data format');
- }
- } catch (error) {
- alert('❌ Failed to import sites: ' + error.message);
- }
- };
- reader.readAsText(event.target.files[0]);
- };
- document.body.appendChild(input);
- input.click();
- document.body.removeChild(input);
- }
-
- function formatStatus(preProcessingRequired, postProcessingRequired) {
- if (SHOW_STATUS_ONLY_IF_TRUE && !preProcessingRequired && !postProcessingRequired) {
- return '';
- }
- const preStatus = USE_EMOJI_FOR_STATUS ? (preProcessingRequired ? '✅' : '✖️') : (preProcessingRequired ? 'true' : 'false');
- const postStatus = USE_EMOJI_FOR_STATUS ? (postProcessingRequired ? '✅' : '✖️') : (postProcessingRequired ? 'true' : 'false');
- return `Pre: ${preStatus}, Post: ${postStatus}`;
- }
-
- window.shouldRunOnThisSite = shouldRunOnThisSite;
- window.isPreProcessingRequired = function() {
- const currentFullPath = normalizeUrl(`${window.location.href}`);
- const entry = mergedSites.find(item => wildcardToRegex(normalizeUrl(item.pattern)).test(currentFullPath));
- return entry ? entry.preProcessingRequired : false;
- };
- window.isPostProcessingRequired = function() {
- const currentFullPath = normalizeUrl(`${window.location.href}`);
- const entry = mergedSites.find(item => wildcardToRegex(normalizeUrl(item.pattern)).test(currentFullPath));
- return entry ? entry.postProcessingRequired : false;
- };
- })();
- })();
-
- //To use this in another script use @require
-
- // // @run-at document-end
- // // ==/UserScript==
-
- // window.SCRIPT_STORAGE_KEY = "magnetLinkHashChecker"; // UNIQUE STORAGE KEY
-
-
- // window.GET_DEFAULT_LIST = function() {
- // return [
- // { pattern: "*1337x.*", preProcessingRequired: false, postProcessingRequired: false },
- // { pattern: "*yts.*", preProcessingRequired: true, postProcessingRequired: true },
- // { pattern: "*torrentgalaxy.*", preProcessingRequired: false, postProcessingRequired: true },
- // { pattern: "*bitsearch.*", preProcessingRequired: false, postProcessingRequired: false },
- // { pattern: "*thepiratebay.*", preProcessingRequired: false, postProcessingRequired: false },
- // { pattern: "*ext.*", preProcessingRequired: false, postProcessingRequired: false }
- // ];
- // };
-
-
- // (async function () {
- // 'use strict';
-
- // // ✅ Wait until `shouldRunOnThisSite` is available
- // while (typeof shouldRunOnThisSite === 'undefined') {
- // await new Promise(resolve => setTimeout(resolve, 50));
- // }
-
- // if (!(await shouldRunOnThisSite())) return;
- // //alert("running");
-
- // console.log("Pre-Customization enabled for this site: " + isPreProcessingRequired() );
- // console.log("Post-Customization enabled for this site: " + isPostProcessingRequired() );
-
- // const OFFCLOUD_CACHE_API_URL = 'https://offcloud.com/api/cache';