F95Zone, Ryuugames, and DLsite Search Buttons for Steam

Adds buttons to search for the current game and developer on F95Zone and its forums, and search for the game on Ryuugames, OtomiGames, and DLsite. Enhanced layout for better user-friendliness, visual appeal, and eye comfort on Steam.

Versión del día 07/05/2025. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name         F95Zone, Ryuugames, and DLsite Search Buttons for Steam
// @namespace    http://tampermonkey.net/
// @version      6.6.0
// @description  Adds buttons to search for the current game and developer on F95Zone and its forums, and search for the game on Ryuugames, OtomiGames, and DLsite. Enhanced layout for better user-friendliness, visual appeal, and eye comfort on Steam.
// @author       FunkyJustin
// @match        https://store.steampowered.com/app/*
// @grant        none
// @license      MIT
// @supportURL   https://greasyfork.org/en/scripts/490884-f95zone-ryuugames-and-dlsite-search-buttons-for-steam/feedback
// ==/UserScript==

(function() {
    'use strict';

    function createF95ZoneButton() {
        const titleElement = document.querySelector('.apphub_AppName');
        if (!titleElement) return;

        const gameTitle = titleElement.innerText.trim() || 'Unknown Game';
        const developerElement = document.querySelector('#developers_list a');
        const developerName = developerElement ? developerElement.innerText.trim() : null;

        // Add margin below the game title for spacing
        titleElement.style.marginBottom = '25px';

        // Container for game search buttons
        const buttonContainer = createSectionContainer('Game Searches', true, 'game-searches');
        const buttonWrapper = buttonContainer.querySelector('.button-wrapper');
        titleElement.parentElement.appendChild(buttonContainer);

        // Configuration for game search buttons
        const gameSearchSites = [
            { name: 'F95Zone', url: `https://f95zone.to/sam/latest_alpha/#/cat=games/page=1/search=${encodeURIComponent(gameTitle)}` },
            { name: 'F95Zone Forums', url: `https://f95zone.to/search/?q=${encodeURIComponent(gameTitle)}&t=post&c[child_nodes]=1&c[nodes][0]=2&c[title_only]=1&o=relevance` },
            { name: 'Ryuugames', url: `https://www.ryuugames.com/?s=${encodeURIComponent(gameTitle)}` },
            { name: 'OtomiGames', url: `https://otomi-games.com/?s=${encodeURIComponent(gameTitle)}` },
            { name: 'DLsite (Pro)', url: `https://www.dlsite.com/pro/fsr/=/language/jp/sex_category%5B0%5D/male/keyword/${encodeURIComponent(gameTitle).replace(/%20/g, '+')}/work_category%5B0%5D/pc/order%5B0%5D/trend/options_and_or/and/per_page/30/page/1/from/fs.header` },
            { name: 'DLsite (Maniax)', url: `https://www.dlsite.com/maniax/fsr/=/language/jp/sex_category%5B0%5D/male/keyword/${encodeURIComponent(gameTitle).replace(/%20/g, '+')}/work_category%5B0%5D/doujin/work_category%5B1%5D/books/work_category%5B2%5D/pc/work_category%5B3%5D/app/order%5B0%5D/trend/options_and_or/and/per_page/30/page/1/from/fs.header` }
        ];

        // Create game search buttons
        gameSearchSites.forEach(site => {
            const button = createButton(site.name, site.url, '#d0d8e0', 'linear-gradient(135deg, #2a3e4f 0%, #1b2838 100%)', '#66c0f4');
            button.setAttribute('aria-label', `Search ${site.name} for ${gameTitle}`);
            button.setAttribute('title', `Search for ${gameTitle} on ${site.name}`);
            buttonWrapper.appendChild(button);
        });

        // Container for developer search buttons
        if (developerName) {
            const devContainer = createSectionContainer('Developer Searches', true, 'dev-searches'); // Changed to true for default expansion
            const devWrapper = devContainer.querySelector('.button-wrapper');
            titleElement.parentElement.appendChild(devContainer);

            const devSearchSites = [
                { name: 'F95Zone', url: `https://f95zone.to/sam/latest_alpha/#/cat=games/page=1/creator=${encodeURIComponent(developerName)}` },
                { name: 'DLsite', url: `https://www.dlsite.com/pro/fsr/=/language/jp/sex_category%5B0%5D/male/keyword/${encodeURIComponent(developerName).replace(/%20/g, '+')}/order%5B0%5D/trend/options_and_or/and/per_page/30/page/1/from/fs.header` }
            ];

            devSearchSites.forEach(site => {
                const button = createButton(`Search Developer on ${site.name}`, site.url, '#d0d8e0', 'linear-gradient(135deg, #2a3e4f 0%, #1b2838 100%)', '#66c0f4');
                button.setAttribute('aria-label', `Search ${site.name} for developer ${developerName}`);
                button.setAttribute('title', `Search for developer ${developerName} on ${site.name}`);
                devWrapper.appendChild(button);
            });
        }
    }

    function createSectionContainer(labelText, isExpandedByDefault, sectionId) {
        const container = document.createElement('div');
        container.style.margin = '15px 0';
        container.style.background = 'linear-gradient(135deg, #1b2838 0%, #2a3e4f 100%)';
        container.style.padding = '12px';
        container.style.borderRadius = '8px';
        container.style.border = '1px solid rgba(102, 192, 244, 0.15)';
        container.style.boxShadow = '0 6px 15px rgba(0, 0, 0, 0.25), inset 0 1px 3px rgba(255, 255, 255, 0.05)';
        container.style.backdropFilter = 'blur(6px)';
        container.style.webkitBackdropFilter = 'blur(6px)';
        container.style.opacity = '0';
        container.style.transition = 'opacity 0.5s ease-in-out, box-shadow 0.3s ease';

        const header = document.createElement('div');
        header.style.display = 'flex';
        header.style.alignItems = 'center';
        header.style.padding = '10px';
        header.style.cursor = 'pointer';
        header.style.borderRadius = '6px';
        header.style.transition = 'background 0.3s ease, opacity 0.3s ease';
        header.setAttribute('role', 'button');
        header.setAttribute('tabindex', '0');
        header.setAttribute('aria-expanded', isExpandedByDefault.toString());
        header.setAttribute('aria-controls', sectionId);

        const toggleIcon = document.createElement('span');
        toggleIcon.innerHTML = isExpandedByDefault ? '▼' : '▶';
        toggleIcon.style.marginRight = '10px';
        toggleIcon.style.background = 'linear-gradient(90deg, #66c0f4, #a7d7f9)';
        toggleIcon.style.webkitBackgroundClip = 'text';
        toggleIcon.style.backgroundClip = 'text';
        toggleIcon.style.color = 'transparent';
        toggleIcon.style.transition = 'transform 0.2s ease';
        header.appendChild(toggleIcon);

        const label = document.createElement('span');
        label.innerText = labelText;
        label.style.fontWeight = '700';
        label.style.color = '#e0e8f0';
        label.style.fontFamily = '"Motiva Sans", Arial, sans-serif';
        label.style.fontSize = '16px';
        label.style.letterSpacing = '0.5px';
        label.style.background = 'linear-gradient(90deg, #a7d7f9, #e0e8f0)';
        label.style.webkitBackgroundClip = 'text';
        label.style.backgroundClip = 'text';
        label.style.color = 'transparent';
        label.style.textShadow = '0 1px 2px rgba(0, 0, 0, 0.3)';
        header.appendChild(label);

        const buttonWrapper = document.createElement('div');
        buttonWrapper.className = 'button-wrapper';
        buttonWrapper.id = sectionId;
        buttonWrapper.style.display = isExpandedByDefault ? 'flex' : 'none';
        buttonWrapper.style.flexWrap = 'wrap';
        buttonWrapper.style.gap = '12px';
        buttonWrapper.style.padding = '12px';
        buttonWrapper.style.justifyContent = 'flex-start';

        container.appendChild(header);
        container.appendChild(buttonWrapper);

        header.addEventListener('click', () => {
            const isExpanded = buttonWrapper.style.display === 'flex';
            buttonWrapper.style.display = isExpanded ? 'none' : 'flex';
            toggleIcon.innerHTML = isExpanded ? '▶' : '▼';
            header.setAttribute('aria-expanded', (!isExpanded).toString());
            header.style.opacity = isExpanded ? '0.9' : '1';
        });

        header.addEventListener('keydown', (event) => {
            if (event.key === 'Enter' || event.key === ' ') {
                event.preventDefault();
                header.click();
            }
        });

        header.addEventListener('mouseover', () => {
            header.style.background = 'rgba(66, 133, 244, 0.1)';
            toggleIcon.style.transform = 'scale(1.2)';
            header.style.opacity = '1';
        });

        header.addEventListener('mouseout', () => {
            header.style.background = 'transparent';
            toggleIcon.style.transform = 'scale(1)';
            header.style.opacity = '0.9';
        });

        header.addEventListener('focus', () => {
            header.style.outline = '2px solid #4285f4';
            header.style.boxShadow = '0 0 10px rgba(66, 133, 244, 0.5)';
        });

        header.addEventListener('blur', () => {
            header.style.outline = 'none';
            header.style.boxShadow = 'none';
        });

        container.addEventListener('mouseover', () => {
            container.style.boxShadow = '0 8px 18px rgba(66, 133, 244, 0.2), inset 0 1px 3px rgba(255, 255, 255, 0.05)';
        });

        container.addEventListener('mouseout', () => {
            container.style.boxShadow = '0 6px 15px rgba(0, 0, 0, 0.25), inset 0 1px 3px rgba(255, 255, 255, 0.05)';
        });

        setTimeout(() => {
            container.style.opacity = '1';
        }, 100);

        const styleSheet = document.createElement('style');
        styleSheet.textContent = `
            @keyframes pulse {
                0% { box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.7); }
                50% { box-shadow: 0 0 0 8px rgba(66, 133, 244, 0.3); }
                100% { box-shadow: 0 0 0 0 rgba(66, 133, 244, 0.7); }
            }
        `;
        document.head.appendChild(styleSheet);

        return container;
    }

    function createButton(text, link, textColor, backgroundStyle, iconColor) {
        const buttonContainer = document.createElement('div');
        buttonContainer.setAttribute('role', 'button');
        buttonContainer.setAttribute('tabindex', '0');
        buttonContainer.style.padding = '10px 18px';
        buttonContainer.style.borderRadius = '8px';
        buttonContainer.style.transition = 'all 0.3s ease';
        buttonContainer.style.background = backgroundStyle;
        buttonContainer.style.color = textColor;
        buttonContainer.style.border = '1px solid rgba(102, 192, 244, 0.2)';
        buttonContainer.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
        buttonContainer.style.display = 'flex';
        buttonContainer.style.alignItems = 'center';
        buttonContainer.style.fontWeight = '600';
        buttonContainer.style.fontFamily = '"Motiva Sans", Arial, sans-serif';
        buttonContainer.style.fontSize = '14px';
        buttonContainer.style.lineHeight = '20px';
        buttonContainer.style.cursor = 'pointer';
        buttonContainer.style.opacity = '0.95';

        const icon = document.createElement('span');
        icon.innerHTML = `<svg style="width: 16px; height: 16px; margin-right: 10px;" fill="${iconColor}" viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>`;
        buttonContainer.appendChild(icon);

        const buttonText = document.createElement('span');
        buttonText.innerText = text;
        buttonText.style.whiteSpace = 'nowrap';
        buttonText.style.overflow = 'hidden';
        buttonText.style.textOverflow = 'ellipsis';
        buttonContainer.appendChild(buttonText);

        buttonContainer.addEventListener('click', function(event) {
            event.preventDefault();
            openUniqueTab(link);
        });

        buttonContainer.addEventListener('keydown', function(event) {
            if (event.key === 'Enter') {
                event.preventDefault();
                openUniqueTab(link);
            }
        });

        buttonContainer.addEventListener('mouseover', () => {
            buttonContainer.style.background = 'linear-gradient(135deg, #3a5f7d 0%, #1b2838 100%)';
            buttonContainer.style.transform = 'scale(1.05)';
            buttonContainer.style.opacity = '1';
            buttonContainer.style.boxShadow = '0 5px 15px rgba(66, 133, 244, 0.4)';
        });

        buttonContainer.addEventListener('mouseout', () => {
            buttonContainer.style.background = backgroundStyle;
            buttonContainer.style.transform = 'scale(1)';
            buttonContainer.style.opacity = '0.95';
            buttonContainer.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
        });

        buttonContainer.addEventListener('focus', () => {
            buttonContainer.style.outline = '2px solid #4285f4';
            buttonContainer.style.boxShadow = '0 0 12px rgba(66, 133, 244, 0.5)';
            buttonContainer.style.animation = 'pulse 1.5s infinite';
        });

        buttonContainer.addEventListener('blur', () => {
            buttonContainer.style.outline = 'none';
            buttonContainer.style.boxShadow = '0 3px 8px rgba(0, 0, 0, 0.2)';
            buttonContainer.style.animation = 'none';
        });

        return buttonContainer;
    }

    function openUniqueTab(url) {
        window.open(url, '_blank');
    }

    window.addEventListener('load', createF95ZoneButton);
})();