StashDB + Pornbox + WhoresHub Search

Search your favorite video/actress on multiple sites

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         StashDB + Pornbox + WhoresHub Search
// @namespace    http://tampermonkey.net/
// @version      1.2
// @license      MIT
// @description  Search your favorite video/actress on multiple sites
// @author       NoOne
// @icon         https://pornbox.com/favicon.ico
// @match        https://stashdb.org/*
// @match        https://stashdb.org/scenes/*
// @match        https://pornbox.com/application/watch-page/*
// @match        https://www.whoreshub.com/videos/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function encodeWhoresHubTitle(str) {
        const encoded = encodeURIComponent(str);
        return encoded.replace(/%2F/g, '%252F');
    }

    const sites = [
        { name: "StashDB", formatTitle: t => t, url: "https://stashdb.org/search/TITRE" },
        { name: "Simpcity", formatTitle: t => t.trim(), url: "https://simpcity.cr/search/?q=TITRE" },
        { name: "Sxyprn", formatTitle: t => t.trim().replace(/\s+/g, '-').replace(/[^\w\-]/g,''), url: "https://sxyprn.com/TITRE.html" },
        { name: "XMovies", formatTitle: t => t, url: "https://xmoviesforyou.com/?s=TITRE" },
        { name: "WhoresHub", formatTitle: t => t.trim().replace(/\s+/g, '-'), url: "https://www.whoreshub.com/search/TITRE/", customEncode: true }
    ];

    function getSceneTitle() {
        if (location.hostname.includes('stashdb.org')) {
            const span = document.querySelector('.card-header h3 span');
            return span ? span.textContent.trim() : '';
        } else if (location.hostname.includes('pornbox.com')) {
            const h1 = document.querySelector('h1.scene-title');
            return h1 ? h1.textContent.trim() : '';
        } else if (location.hostname.includes('whoreshub.com')) {
            const h1 = document.querySelector('h1.title');
            return h1 ? h1.textContent.trim() : '';
        }
        return '';
    }

    function createPanelButton(name, onClick) {
        const btn = document.createElement('button');
        btn.textContent = name;
        Object.assign(btn.style, {
            flex: '1',
            padding: '6px',
            border: 'none',
            borderRadius: '6px',
            cursor: 'pointer',
            background: '#30404d',
            color: 'white',
            fontWeight: 'bold',
            transition: 'background 0.2s'
        });
        btn.addEventListener('mouseenter', () => btn.style.background = '#3a4c5e');
        btn.addEventListener('mouseleave', () => btn.style.background = '#30404d');
        btn.addEventListener('click', onClick);
        return btn;
    }

    const panelBtn = document.createElement('button');
    panelBtn.textContent = '🔍';
    Object.assign(panelBtn.style, {
        position: 'fixed',
        bottom: '10px',
        left: '10px',
        zIndex: 9999,
        background: '#202b33',
        border: '2px solid white',
        color: 'white',
        borderRadius: '8px',
        width: '40px',
        height: '40px',
        cursor: 'pointer',
        transition: 'background 0.2s'
    });
    panelBtn.addEventListener('mouseenter', () => panelBtn.style.background = '#2a3a4d');
    panelBtn.addEventListener('mouseleave', () => panelBtn.style.background = '#202b33');
    document.body.appendChild(panelBtn);

    const panelContainer = document.createElement('div');
    Object.assign(panelContainer.style, {
        position: 'fixed',
        bottom: '60px',
        left: '10px',
        background: '#202b33',
        border: '2px solid white',
        color: 'white',
        padding: '10px',
        borderRadius: '8px',
        opacity: '0',
        transform: 'translateY(10px)',
        transition: 'opacity 0.25s ease, transform 0.25s ease',
        pointerEvents: 'none',
        zIndex: 9999,
        width: '300px',
        fontSize: '14px'
    });
    document.body.appendChild(panelContainer);

    const inputWrapper = document.createElement('div');
    inputWrapper.style.display = 'flex';
    inputWrapper.style.gap = '4px';
    panelContainer.appendChild(inputWrapper);

    const clearBtn = document.createElement('button');
    clearBtn.textContent = '❌';
    Object.assign(clearBtn.style, {
        cursor: 'pointer',
        border: 'none',
        borderRadius: '4px',
        background: '#ee8aa9',
        color: 'black',
        width: '30px'
    });
    inputWrapper.appendChild(clearBtn);

    const pasteBtn = document.createElement('button');
    pasteBtn.textContent = '🧷';
    Object.assign(pasteBtn.style, {
        cursor: 'pointer',
        border: 'none',
        borderRadius: '4px',
        background: '#8aeebb',
        color: 'black',
        width: '30px'
    });
    inputWrapper.appendChild(pasteBtn);

    const input = document.createElement('input');
    Object.assign(input.style, {
        flex: '1',
        padding: '6px',
        borderRadius: '4px',
        border: 'none',
        background: '#1a1a1a',
        color: 'white'
    });
    input.setAttribute('placeholder', 'Search term...');
    inputWrapper.appendChild(input);

    let userEdited = false;
    clearBtn.addEventListener('click', () => { input.value = ''; userEdited = true; });
    input.addEventListener('input', () => { userEdited = true; });

    pasteBtn.addEventListener('mousedown', e => e.preventDefault()); // Prevent context menu
    pasteBtn.addEventListener('click', async (e) => {
        e.preventDefault();
        try {
            const text = await navigator.clipboard.readText();
            if (text) {
                input.value = text.trim();
                userEdited = true;
            } else {
                alert('Presse-papiers vide.');
            }
        } catch (err) {
            alert('Impossible d’accéder au presse-papiers : ' + err);
        }
    });

    const btnContainer = document.createElement('div');
    Object.assign(btnContainer.style, {
        display: 'flex',
        gap: '6px',
        marginTop: '6px',
        flexWrap: 'wrap'
    });
    panelContainer.appendChild(btnContainer);

    sites.forEach(site => {
        const btn = createPanelButton(site.name, () => {
            const term = input.value.trim();
            if (!term) return alert('Please enter a search term!');
            const formatted = site.formatTitle(term);
            const encoded = site.customEncode ? encodeWhoresHubTitle(formatted) : encodeURIComponent(formatted);
            const url = site.url.replace('TITRE', encoded);
            window.open(url, '_blank');
        });
        btnContainer.appendChild(btn);
    });

    let lastURL = location.href;
    function checkURLChange() {
        if (location.href !== lastURL) {
            lastURL = location.href;
            userEdited = false;
            input.value = getSceneTitle();
        }
    }
    setInterval(checkURLChange, 500);

    let isOpen = false;
    panelBtn.addEventListener('click', () => {
        isOpen = !isOpen;
        if (isOpen) {
            panelContainer.style.pointerEvents = 'auto';
            panelContainer.style.opacity = '1';
            panelContainer.style.transform = 'translateY(0)';
            if (!userEdited) input.value = getSceneTitle();
        } else {
            panelContainer.style.opacity = '0';
            panelContainer.style.transform = 'translateY(10px)';
            panelContainer.style.pointerEvents = 'none';
        }
    });

})();