Adds a dropdown on many porn sites to search various sites found in the html.
// ==UserScript==
// @name Quick Porn Search
// @namespace https://github.com/goawaylovestrike
// @description Adds a dropdown on many porn sites to search various sites found in the html.
// @author GoAwayLoveStrike
// @version 5.3
// @icon https://raw.githubusercontent.com/goawaylovestrike/Userscripts/refs/heads/main/quick-porn-search/quick-porn-search.png
// @include https://www.wifey.com/*
// @include https://tour.swallowed.com/*
// @include https://myfamilypies.com/*
// @include https://cum4k.com/*
// @include https://www.brazzers.com/*
// @include https://*.dirtyauditions.com/*
// @include https://fansdb.cc/*
// @include https://www.lethalhardcore.com/*
// @include https://realitysis.com/*
// @include https://nubileset.com/*
// @include https://www.milfed.com/*
// @include https://bangbros.com/*
// @include https://www.bangadventures.com/*
// @include https://MomsTight.com/*
// @include https://store.fapnado.com/*
// @include https://brattysis.com/*
// @include https://www.tushyraw.com/*
// @include https://deeplush.com/*
// @include https://*.iafd.com/*
// @include https://*.data18.com/*
// @include https://www.mylf.com/*
// @include https://nubilesunscripted.com/*
// @include https://www.myfreecams.com/*
// @include https://*.teamskeet.com/*
// @include https://tour.allanal.com/*
// @include https://momlover.com/*
// @include https://www.lookathernow.com/*
// @include https://petitehdporn.com/*
// @include https://*.stephousexxx.com/*
// @include https://girlsonlyporn.com/*
// @include https://MomSwapped.com/*
// @include https://petiteballerinasfucked.com/*
// @include https://*.jonathanjordanxxx.com/*
// @include https://nympho.com/*
// @include https://www.bang.com/*
// @include https://*.westcoastproductions.com/*
// @include https://pervcity.com/*
// @include https://www.propertysex.com/*
// @include https://hotcrazymess.com/*
// @include https://www.empirestores.co/*
// @include https://brattymilf.com/*
// @include https://www.blackedraw.com/*
// @include https://*.allanal.com/*
// @include https://imnotyourmommy.com/*
// @include https://www.deeper.com/*
// @include https://www.bangpremium.com/*
// @include https://tour.trueanal.com/*
// @include https://*.manyvids.com/Video/*
// @include https://nubiles-casting.com/*
// @include https://lubed.com/*
// @include https://www.pervmom.com/*
// @include https://caughtmycoach.com/*
// @include https://exotic4k.com/*
// @include https://youngermommy.com/*
// @include https://www.dadcrush.com/*
// @include https://theporndb.net/*
// @include https://tour.nympho.com/*
// @include https://www.vixen.com/*
// @include https://*.18lust.com/*
// @include https://www.pornstarempire.com/*
// @include https://www.mypervyfamily.com/*
// @include https://*.lewood.com/*
// @include https://momsfamilysecrets.com/*
// @include https://MomsFamilySecrets.com/*
// @include https://momsteachsex.com/*
// @include https://nubiles-porn.com/*
// @include https://momswapped.com/*
// @include https://driverxxx.com/*
// @include https://pornpros.com/*
// @include https://bountyhunterporn.com/*
// @include https://princesscum.com/*
// @include https://*.joannaangel.com/*
// @include https://momwantscreampie.com/*
// @include https://ImNotYourMommy.com/*
// @include https://trueanal.com/*
// @include https://www.sislovesme.com/*
// @include https://www.adultdvdempire.com/*
// @include https://*.concoxxxion.com/*
// @include https://www.mrluckypov.com/*
// @include https://www.mofos.com/*
// @include https://anilos.com/*
// @include https://*.inserted.com/*
// @include https://girlcum.com/*
// @include https://familyswap.xxx/*
// @include https://vrbangers.com/*
// @include https://MomsBoyToy.com/*
// @include https://www.daughterswap.com/*
// @include https://*.adameveplus.com/*
// @include https://*.analonly.com/*
// @include https://momsboytoy.com/*
// @include https://badteenspunished.com/*
// @include https://www.wankzvr.com/*
// @include https://smashed.xxx/*
// @include https://teacherfucksteens.com/*
// @include https://stashdb.org/*
// @include https://www.naughtyamerica.com/*
// @include https://*.bruthasinc.com/*
// @include https://*.gloryholeswallow.com/*
// @include https://cumswappingsis.com/*
// @include https://MomWantsCreampie.com/*
// @include https://www.gloryholesecrets.com/*
// @include https://holed.com/*
// @include https://www.realitykings.com/*
// @include https://chaturbate.com/*
// @include https://anal4k.com/*
// @include https://nubiles.net/*
// @include https://MomWantsToBreed.com/*
// @include https://cheatingsis.com/*
// @include https://detentiongirls.com/*
// @include https://nfbusty.com/*
// @include https://thepovgod.com/*
// @include https://thatsitcomshow.com/*
// @include https://nubilefilms.com/*
// @include https://www.tushy.com/*
// @include https://www.mormongirlz.com/*
// @include https://*.fetishmovies.com/*
// @include https://*.kaiiaeve.com/*
// @include https://www.lethalhardcorevr.com/*
// @include https://thePOVGod.com/*
// @include https://baeb.com/*
// @include https://momwantstobreed.com/*
// @include https://fansly.com/*
// @include https://daddyslilangel.com/*
// @include https://tiny4k.com/*
// @include https://*.spankmonster.com/*
// @include https://tour.analonly.com/*
// @include https://www.blacked.com/*
// @include https://stepsiblingscaught.com/*
// @include https://momstight.com/*
// @include https://xhamsterlive.com/*
// @include https://povd.com/*
// @include https://www.slayed.com/*
// @include https://swallowed.com/*
// @include https://*.jayspov.net/*
// @license MIT
// ==/UserScript==
// Search button configuration - Edit these to add/remove/modify search options
const searchButtons = [
{
name: "1337x",
url: "https://1337x.to/sort-search/{query}/time/desc/1/",
},
{
name: "PornoLabs",
url: "https://pornolab.net/forum/tracker.php?max=1&nm={query}",
},
{
name: "Yandex",
url: "https://yandex.com/search/?text={query}",
},
{
name: "Google",
url: "https://www.google.com/search?q={query}",
},
{
name: "Full Porn",
url: "https://www.fullporn.xxx/search/{query}/",
queryTransform: (query) => query.replace(/\s+/g, "-"), //This replaces all spaces with dashes in the url which is required for some sites.
},
{
name: "WatchPorn.to",
url: "https://watchporn.to/search/{query}/",
queryTransform: (query) => query.replace(/\s+/g, "-"),
},
{
name: "Hdporn92",
url: "https://hdporn92.com/?s={query}",
},
{
name: "Porndish",
url: "https://www.porndish.com/?s={query}",
},
{
name: "Sexuria",
url: "https://sexuria.net/?do=search&subaction=search&search_start=0&full_search=0&story={query}",
},
{
name: "Porn Horder",
url: "https://w15.pornhoarder.tv/search/?search={query}&sort=0&date=0&servers%5B%5D=47&servers%5B%5D=21&servers%5B%5D=40&servers%5B%5D=45&servers%5B%5D=12&servers%5B%5D=35&servers%5B%5D=25&servers%5B%5D=41&servers%5B%5D=44&servers%5B%5D=42&servers%5B%5D=43&servers%5B%5D=29&author=0&page=1",
},
];
function isMobileDevice() {
return (
/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent
) || window.innerWidth <= 768
);
}
(function () {
"use strict";
var searchterm = null;
let initAttempts = 0;
const MAX_ATTEMPTS = 5;
let includeTitle = true;
let includePerformers = true;
let includeSite = true;
let excludeMalePerformers = true;
// Initialize with retry mechanism
console.log("DOM ready state:", document.readyState);
console.log("Current URL:", window.location.href);
console.log("Attempt number:", initAttempts + 1, "of", MAX_ATTEMPTS);
function initialize() {
if (initAttempts >= MAX_ATTEMPTS) {
console.log("Failed to initialize after maximum attempts");
return;
}
initAttempts++;
// First detect the pattern
const pattern = detectSitePattern();
if (!pattern) {
// Remove buttons
document
.querySelectorAll(".search-button-container")
.forEach((el) => el.remove());
// Retry after delay if we haven't hit max attempts
if (initAttempts < MAX_ATTEMPTS) {
setTimeout(initialize, 1000);
}
return;
}
// Set the current pattern for use by other functions
window._currentPattern = pattern;
updateSearchButtons();
}
function getHostnameSite() {
const hostname = window.location.hostname;
const parts = hostname.split(".");
// Always take the part before the TLD (second-to-last part)
let site = parts[parts.length - 2].toLowerCase();
// Normalize site names for specific platforms
if (site.includes("onlyfans")) {
return "OnlyFans";
}
if (site.includes("manyvids")) {
return "ManyVids";
}
return site;
}
function getsearchterm() {
const hostname = window.location.hostname;
const path = window.location.pathname;
if (
!path ||
path === "/" ||
path.includes("/categories") ||
path.includes("/search") ||
path.includes("/models") ||
path.includes("/channels") ||
path.includes("/tags")
) {
return;
}
let title = null;
let performers = [];
let site = null;
searchterm = null;
if (window._currentPattern) {
const { selectors } = window._currentPattern;
const titleElement = document.querySelector(selectors.title);
if (titleElement) {
title = window._currentPattern.titleTransform
? window._currentPattern.titleTransform(titleElement)
: titleElement.textContent.trim();
}
// Special handling for ThePornDB
if (window._currentPattern.name === "THEPORNDB") {
const performerElements = Array.from(
document.querySelectorAll(selectors.performers)
);
if (performerElements.length > 0) {
performers = performerElements
.filter((el) => {
// Skip male performers if excludeMalePerformers is enabled
if (excludeMalePerformers) {
// Check if this performer has the male icon
const parentCard =
el.closest(".relative.group");
if (parentCard) {
// Look for the male icon (SVG with Mars symbol)
const maleIcon =
parentCard.querySelector(
"svg.text-blue-400"
);
if (maleIcon) {
console.log(
`Filtering out male performer: ${el.textContent.trim()}`
);
return false;
}
}
}
return true;
})
.map((el) => el.textContent.trim())
.filter((name) => name && name !== "");
}
} else if (window._currentPattern.name === "STASHBOX") {
const performerElements = Array.from(
document.querySelectorAll(selectors.performers)
);
if (performerElements.length > 0) {
performers = performerElements
.filter((el) => {
// Skip male performers if excludeMalePerformers is enabled
if (excludeMalePerformers) {
// Check if this performer has the male icon (Mars symbol SVG)
const parentElement =
el.closest(".scene-performer");
if (parentElement) {
// Look for the Mars symbol SVG with title "Male"
const maleIcon =
parentElement.querySelector(
'svg[data-icon="mars"], svg.fa-mars'
);
if (maleIcon) {
console.log(
`Filtering out male performer: ${el.textContent.trim()}`
);
return false;
}
}
}
return true;
})
.map((el) => el.textContent.trim())
.filter((name) => name && name !== "");
}
} else {
// Original performer extraction for other sites
const performerElements = Array.from(
document.querySelectorAll(selectors.performers)
);
if (performerElements.length > 0) {
if (
window._currentPattern &&
window._currentPattern.name === "GLORY"
) {
performers = performerElements
.map((el) => {
const text = el.textContent.trim();
const match = text.match(/\((.*?)\)/);
return match
? match[1].trim().replace(/[@\s]/g, "")
: "";
})
.filter((name) => name !== "");
} else {
performers = performerElements
.map((el) => el.textContent.trim())
.filter((name) => name && name !== "");
}
}
}
const siteElement = document.querySelector(selectors.site);
if (siteElement) {
site = window._currentPattern.siteTransform
? window._currentPattern.siteTransform(siteElement)
: siteElement.textContent.trim();
}
if (!site) {
site = getHostnameSite();
}
// Normalize site names for specific platforms
if (site && site.toLowerCase().includes("onlyfans")) {
site = "OnlyFans";
}
if (site && site.toLowerCase().includes("manyvids")) {
site = "ManyVids";
}
}
// Build searchterm based on checkbox preferences
const parts = [];
if (includePerformers && performers.length > 0) {
parts.push(performers.join(" "));
}
if (includeTitle && title) {
parts.push(title);
}
if (includeSite && site) {
parts.push(site.replace(/\((\w+)\)/g, "$1").replace(/[\s']+/g, "")); // Remove parentheses, spaces, and apostrophes
}
searchterm = parts
.join(" ")
.trim()
.replace(/[^\w\s-.!&]/g, "") // Remove special characters without replacing with spaces
.replace(/\s+/g, " "); // Normalize spaces
console.log(`Final searchterm for ${hostname}:`, searchterm);
}
function updateSearchLinks() {
// Get all existing links in the dropdown
const links = document.querySelectorAll(".search-button-container a");
// Update each link's href with the new searchterm
links.forEach((link) => {
const buttonName = link.textContent.trim();
const button = searchButtons.find((b) => b.name === buttonName);
if (button) {
const query = button.queryTransform
? button.queryTransform(searchterm)
: encodeURIComponent(searchterm);
link.href = button.url.replace("{query}", query);
}
});
}
function updateSearchButtons() {
// Remove the pattern check since it's already done in initialize()
getsearchterm();
// Only create dropdown if we have a valid searchterm
if (searchterm && searchterm.length > 0) {
const dropdownContainer = document.createElement("div");
dropdownContainer.className = "search-button-container";
// Adjust container position based on device
const isMobile = isMobileDevice();
dropdownContainer.style.cssText = `
position: fixed !important;
${
isMobile
? "left: 5px !important; top: 10px !important;"
: "left: 15px !important; top: 80px !important;"
}
z-index: 2147483647 !important;
pointer-events: auto !important;
visibility: visible !important;
opacity: 1 !important;
`;
// Create the dropdown button
const dropdownButton = document.createElement("button");
if (isMobile) {
dropdownButton.textContent = "🔍";
dropdownButton.style.cssText = `
background: #ffffff !important;
color: #000000 !important;
font-family: Arial, sans-serif !important;
font-weight: bold !important;
padding: 8px !important;
border: 2px solid #000000 !important;
border-radius: 50% !important;
font-size: 16px !important;
cursor: pointer !important;
width: 40px !important;
height: 40px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
`;
} else {
dropdownButton.textContent = "🔍 Search";
dropdownButton.style.cssText = `
background: #ffffff !important;
color: #000000 !important;
font-family: Arial, sans-serif !important;
font-weight: bold !important;
padding: 10px 15px !important;
border: 2px solid #000000 !important;
border-radius: 7px !important;
font-size: 18px !important;
cursor: pointer !important;
min-width: 140px !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
`;
}
// Create the dropdown content
const dropdownContent = document.createElement("div");
dropdownContent.style.cssText = `
display: none;
position: absolute !important;
background-color: #ffffff !important;
min-width: ${isMobile ? "200px" : "140px"} !important;
box-shadow: 0 4px 8px rgba(0,0,0,0.3) !important;
border-radius: 7px !important;
margin-top: 5px !important;
border: 2px solid #000000 !important;
overflow: hidden !important;
${isMobile ? "left: 0 !important;" : ""}
`;
searchButtons.forEach((button) => {
const link = document.createElement("a");
link.id = button.name;
// Create favicon image
const favicon = document.createElement("img");
const urlObj = new URL(button.url);
const domain = urlObj.hostname.split(".").slice(-2).join(".");
favicon.src = `${urlObj.protocol}//${domain}/favicon.ico`;
favicon.style.cssText = `
width: 16px !important;
height: 16px !important;
margin-right: 8px !important;
vertical-align: middle !important;
filter: drop-shadow(0 1px 1px rgba(0,0,0,0.2)) !important;
`;
const textSpan = document.createElement("span");
textSpan.textContent = button.name;
textSpan.style.cssText = `
vertical-align: middle !important;
font-weight: bold !important;
color: #000000 !important;
font-size: 14px !important;
font-family: Arial, sans-serif !important;
line-height: normal !important;
`;
const query = button.queryTransform
? button.queryTransform(searchterm)
: encodeURIComponent(searchterm);
link.href = button.url.replace("{query}", query);
link.target = "_blank";
link.style.cssText = `
color: #000000 !important;
background: white !important;
padding: 10px 10px !important;
text-decoration: none !important;
display: flex !important;
align-items: center !important;
text-align: left !important;
font-size: 14px !important;
cursor: pointer !important;
border-bottom: 1px solid #eee !important;
font-family: Arial, sans-serif !important;
line-height: normal !important;
width: auto !important;
height: auto !important;
`;
textSpan.style.cssText = `
vertical-align: middle !important;
font-weight: bold !important;
color: #000000 !important;
font-size: 15px !important;
line-height: normal !important;
`;
const container = document.createElement("div");
container.className = "search-button-container";
container.style.cssText = `
position: fixed !important;
top: 10px !important;
right: 10px !important;
background: white !important;
border: 1px solid #ddd !important;
border-radius: 4px !important;
box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
z-index: 9999999 !important;
width: auto !important;
min-width: 200px !important;
`;
link.onmouseover = function () {
this.style.backgroundColor = "#f1f1f1 !important";
};
link.onmouseout = function () {
this.style.backgroundColor = "#ffffff !important";
};
link.appendChild(favicon);
link.appendChild(textSpan);
dropdownContent.appendChild(link);
});
const separator = document.createElement("div");
separator.style.cssText = `
border-top: 2px solid #eee !important;
margin-top: 5px !important;
`;
dropdownContent.appendChild(separator);
const controlsDiv = document.createElement("div");
controlsDiv.style.borderBottom = "1px solid #ddd";
controlsDiv.style.marginBottom = "10px";
controlsDiv.style.paddingBottom = "10px";
const options = [
{
id: "includePerformers",
label: "Performers",
ref: () => includePerformers,
},
{ id: "includeTitle", label: "Title", ref: () => includeTitle },
{ id: "includeSite", label: "Site", ref: () => includeSite },
{
id: "excludeMalePerformers",
label: "No Male Performers",
ref: () => excludeMalePerformers,
},
];
options.forEach((option) => {
const div = document.createElement("div");
div.style.marginBottom = "5px";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = option.id;
checkbox.checked = option.ref();
checkbox.style.cssText = `
margin-right: 5px !important;
cursor: pointer !important;
`;
// Single consolidated event listener
checkbox.addEventListener("change", (e) => {
e.stopPropagation();
console.log(
`Checkbox ${option.id} changed to ${checkbox.checked}`
); // Debug log
switch (option.id) {
case "includePerformers":
includePerformers = checkbox.checked;
break;
case "includeTitle":
includeTitle = checkbox.checked;
break;
case "includeSite":
includeSite = checkbox.checked;
break;
case "excludeMalePerformers":
excludeMalePerformers = checkbox.checked;
break;
}
getsearchterm();
updateSearchLinks();
console.log("Updated searchterm:", searchterm); // Debug log
});
const label = document.createElement("label");
label.htmlFor = option.id;
label.textContent = option.label;
label.style.cssText = `
font-family: Arial, sans-serif !important;
font-size: 12px !important;
color: black !important;
cursor: pointer !important;
`;
div.appendChild(checkbox);
div.appendChild(label);
controlsDiv.appendChild(div);
});
dropdownContent.appendChild(controlsDiv);
// Toggle dropdown on click
dropdownButton.onclick = function (e) {
e.stopPropagation();
dropdownContent.style.display =
dropdownContent.style.display === "none" ? "block" : "none";
};
dropdownContainer.appendChild(dropdownButton);
dropdownContainer.appendChild(dropdownContent);
if (document.body) {
document.body.appendChild(dropdownContainer);
console.log("Dropdown created with searchterm:", searchterm);
}
}
}
initialize();
// Watch for URL changes
let lastUrl = location.href;
const observer = new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
// Immediately remove the button
document
.querySelectorAll(".search-button-container")
.forEach((el) => el.remove());
// Reset attempts and try to initialize with new page
initAttempts = 0;
setTimeout(initialize, 1000);
}
});
observer.observe(document.body || document, {
subtree: true,
childList: true,
});
function detectSitePattern() {
const hostname = window.location.hostname;
// Define common selectors to test
const commonPatterns = [
{
name: "TEAMSKEET",
selectors: {
title: ".sceneTitle",
performers: ".contentTitle .model-name-link",
site: ".siteName",
},
},
{
name: "NAUGHTYAMERICA",
selectors: {
title: "h1.scene-title",
performers: ".performer-list",
site: ".site-title",
},
},
{
name: "ADULTEMPIRECASH",
selectors: {
title: ".video-title",
performers: "span.video-performer-name span.overlay-inner",
site: 'div.studio span:not(.font-weight-bold), meta[property="og:site_name"]',
},
siteTransform: (el) =>
(el.tagName.toLowerCase() === "meta"
? el.getAttribute("content")
: el.textContent
).replace(/\s+/g, ""),
},
{
name: "STASHBOX",
selectors: {
title: ".card-header h3 span",
performers: ".scene-performer span",
site: 'a[href^="/studios/"]',
},
},
{
name: "AYLO",
selectors: {
title: "h2.sc-wxt7nk-4, h2.sc-1b6bgon-3, h1.sc-1b6bgon-3",
performers:
'a[href^="/pornstar/"][class^="sc-"], a[href^="/model/"][class^="sc-"]',
site: ".sc-vdkjux-5",
},
},
{
name: "PORNPROS",
selectors: {
title: 'div[id="trailer_player"] .scene-info h1',
performers: ".scene-info .link-list-with-commas a",
},
},
{
name: "BANG",
selectors: {
title: '[data-controller="video-entry"] div.flex.mb-6', // More specific selector targeting h1 within the flex container
performers: 'div.w-full .leading-6 a[href^="/pornstar/"]',
},
},
{
name: "VIXEN",
selectors: {
title: '[data-test-component="VideoTitle"]', // More specific selector targeting h1 within the flex container
performers:
'[data-test-component="VideoModels"] a[href^="/performers/"]',
},
},
{
name: "NUBILES",
selectors: {
title: "div.row.content-pane-container .content-pane-title h2", // More specific selector targeting h1 within the flex container
performers: ".content-pane-performer.model",
site: ".row.content-pane-container .site-link",
},
},
{
name: "THEPORNDB",
selectors: {
title: ".justify-between h2.text-3xl", // More specific selector targeting h1 within the flex container
performers: "div.px-3.py-1.text-center h2.text-xl a[title]",
site: 'a[href^="/sites/"]',
},
},
{
name: "MANYVIDS",
selectors: {
title: ".VideoMetaInfo_title__mWRak", // More specific selector targeting h1 within the flex container
performers: ".VideoProfileCard_actions__x_NEr",
site: 'a[href^="/sites/"]',
},
},
{
name: "ADULTDVDEMPIRE",
selectors: {
title: ".movie-page__heading__title", // More specific selector targeting h1 within the flex container
performers: '[label="Performer"]',
site: '[label="Studio"]',
},
},
{
name: "GLORYHOLESWALLOW",
selectors: {
title: "div.memberVideoPics div.objectInfo h1",
performers: "div.content p",
},
},
{
name: "STICKYDOLLARS",
selectors: {
title: "h1.title", // More specific selector targeting h1 within the flex container
performers: 'h2.models a[href^="/models/"]',
},
},
{
name: "GLORYHOLESECRETS",
selectors: {
title: "h1.Title", // More specific selector targeting h1 within the flex container
performers: ".ActorThumb-Name-Link",
},
},
{
name: "DATA18",
selectors: {
title: "#h1div h1 a",
performers: "a.bold.gen",
site: "p b a.bold",
},
},
{
name: "IAFD",
selectors: {
title: "h1",
performers: "div.castbox a",
site: 'p.bioheading a[href*="/studio.rme"], p.biodata a[href*="/studio.rme"]',
},
// Custom transform to remove the year in parentheses and apostrophes from the title
titleTransform: (titleElement) => {
const titleText = titleElement.textContent.trim();
return titleText
.replace(/\s*\(\d{4}\)$/, "") // Remove year in parentheses at the end
.replace(/'/g, ""); // Remove all apostrophes without adding spaces
},
// Custom transform to remove the top-level domain from the site name
siteTransform: (siteElement) => {
const siteText = siteElement.textContent.trim();
return siteText.replace(/\.[a-z]+$/, ""); // Remove the TLD (.com, .net, etc.)
},
},
];
console.log(
`[Pattern Detector] ⚡ Starting pattern detection for: ${hostname}`
);
// Find matching patterns
const matchingPattern = commonPatterns.find((pattern) => {
const { selectors } = pattern;
let matches = 0;
for (const [key, selector] of Object.entries(selectors)) {
const elements = document.querySelectorAll(selector);
if (elements && elements.length > 0) {
matches++;
console.log(
`[Pattern Detector] Found ${elements.length} matches for ${key} using selector: ${selector}`
);
// Log the actual site name if we're checking the site selector
if (key === "site") {
console.log(
`[Pattern Detector] Site name found: "${elements[0].textContent.trim()}"`
);
}
}
}
return matches >= 2;
});
if (matchingPattern) {
console.log(
`[Pattern Detector] Found matching pattern: ${matchingPattern.name}`,
matchingPattern
);
window._currentPattern = matchingPattern;
// Immediately try to extract data using the pattern
const titleElement = document.querySelector(
matchingPattern.selectors.title
);
const performerElements = document.querySelectorAll(
matchingPattern.selectors.performers
);
const siteElement = document.querySelector(
matchingPattern.selectors.site
);
console.log("[Pattern Detector] Extracted data:", {
title: titleElement?.textContent.trim(),
performers: Array.from(performerElements).map((el) =>
el.textContent.trim()
),
site: siteElement?.textContent.trim(),
});
return matchingPattern;
}
console.log(
`[Pattern Detector] No matching patterns found for ${hostname}`
);
return null;
}
})();