// ==UserScript==
// @name Multi-Platform Search Buttons for DLsite
// @namespace http://tampermonkey.net/
// @version 2.7
// @description Adds a compact, open-by-default dropdown menu for search buttons on DLsite pages, with grouped buttons for gaming forums, correct favicons, and a design that matches DLsite's aesthetic.
// @author FunkyJustin
// @match https://www.dlsite.com/maniax/work/=/product_id/*
// @match https://www.dlsite.com/pro/work/=/product_id/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// Inject CSS styles
const style = document.createElement('style');
style.innerHTML = `
:root {
--button-bg-start: #D6BCFA; /* Light purple */
--button-bg-end: #FBB6CE; /* Soft pink */
--button-text: #f5f5f5; /* Off-white for better contrast */
--button-shadow: rgba(0, 0, 0, 0.1);
--button-border: #B794F4; /* Softer border color */
--dropdown-bg: #f9f9f9;
}
.search-btn {
display: inline-flex;
align-items: center;
padding: 4px 8px;
margin: 2px;
background: linear-gradient(45deg, var(--button-bg-start), var(--button-bg-end));
color: var(--button-text);
text-decoration: none;
border-radius: 12px;
border: 1px solid var(--button-border);
box-shadow: 0 2px 4px var(--button-shadow);
font-family: Arial, sans-serif;
font-size: 15px;
font-weight: bold;
transition: box-shadow 0.2s, opacity 0.3s;
}
.search-btn:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
opacity: 0.95;
}
.search-btn img {
width: 18px;
height: 18px;
margin-right: 6px;
}
.dropdown-container {
max-width: 600px; /* Limit width to align left */
margin-top: 10px;
margin-bottom: 20px;
}
.dropdown-toggle {
display: flex;
align-items: center;
padding: 5px 10px;
background-color: #E91E63; /* Matches DLsite's pink */
color: #fff;
border: none;
border-radius: 12px;
font-family: Arial, sans-serif;
font-size: 14px;
font-weight: bold;
cursor: pointer;
transition: background-color 0.2s;
}
.dropdown-toggle:hover {
background-color: #C2185B;
}
.dropdown-toggle::after {
content: '▼';
margin-left: 5px;
transition: transform 0.2s;
}
.dropdown-toggle.collapsed::after {
transform: rotate(180deg);
}
.dropdown-content {
display: block; /* Open by default */
background-color: var(--dropdown-bg);
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 10px;
margin-top: 5px;
}
.dropdown-content.hidden {
display: none;
}
.button-group {
margin-bottom: 8px;
}
.group-label {
font-family: Arial, sans-serif;
font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 5px;
}
.button-group-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, auto));
gap: 5px;
}
.circle-btn-wrapper {
display: inline-flex;
flex-wrap: nowrap;
align-items: center;
margin-left: 10px;
margin-bottom: 15px;
margin-right: 10px; /* Prevent overlap with "Support the Circle" */
}
`;
document.head.appendChild(style);
// Icon URLs for each website
const icons = {
f95zone: 'https://f95zone.to/favicon.ico',
ryuugames: 'https://www.ryuugames.com/favicon.ico',
otomi: 'https://otomi-games.com/favicon.ico'
};
// Function to create a styled button
function createButton(text, url, iconUrl) {
const a = document.createElement('a');
a.className = 'search-btn';
a.href = url;
a.target = '_blank';
a.title = text; // Tooltip
if (iconUrl) {
const img = document.createElement('img');
img.src = iconUrl;
img.alt = '';
a.appendChild(img);
}
a.appendChild(document.createTextNode(text));
return a;
}
// Main function to add buttons
function addSearchButtons() {
// Extract game/circle info
const gameTitleEl = document.querySelector('h1#work_name');
const circleNameEl = document.querySelector('span[itemprop="brand"] a');
if (!gameTitleEl || !circleNameEl) {
console.warn('Required elements not found.');
return;
}
const gameTitle = gameTitleEl.innerText.trim();
const circleName = circleNameEl.innerText.trim();
const productIdMatch = window.location.href.match(/product_id\/([^\/]+)/);
const productId = productIdMatch ? productIdMatch[1].replace('.html', '') : '';
// Define search URLs
const urls = {
f95zoneGame: `https://f95zone.to/sam/latest_alpha/#/cat=games/page=1/search=${encodeURIComponent(gameTitle)}`,
f95zoneForum: `https://f95zone.to/search/?q=${encodeURIComponent(gameTitle)}&t=post&c[child_nodes]=1&c[nodes][0]=2&c[title_only]=1&o=relevance`,
f95zonePID: `https://f95zone.to/search/?q=${encodeURIComponent(productId)}`,
f95zoneCircle: `https://f95zone.to/sam/latest_alpha/#/cat=games/page=1/creator=${encodeURIComponent(circleName)}`,
otomiGame: `https://otomi-games.com/?s=${encodeURIComponent(gameTitle)}`,
otomiPID: `https://otomi-games.com/${productId}/`,
ryuuGame: `https://www.ryuugames.com/?s=${encodeURIComponent(gameTitle)}`,
ryuuPID: `https://www.ryuugames.com/?s=${encodeURIComponent(productId)}`,
ryuuCircle: `https://www.ryuugames.com/?s=${encodeURIComponent(circleName)}`
};
// Create buttons with correct icons
const buttons = {
f95Game: createButton('Search on F95Zone', urls.f95zoneGame, icons.f95zone),
f95Forum: createButton('Search on F95Zone Forums', urls.f95zoneForum, icons.f95zone),
f95PID: createButton('Search Product ID on F95Zone', urls.f95zonePID, icons.f95zone),
otomiGame: createButton('Search on OtomiGames', urls.otomiGame, icons.otomi),
otomiPID: createButton('Search Product ID on OtomiGames', urls.otomiPID, icons.otomi),
ryuuGame: createButton('Search on Ryuugames', urls.ryuuGame, icons.ryuugames),
ryuuPID: createButton('Search Product ID on Ryuugames', urls.ryuuPID, icons.ryuugames),
f95Circle: createButton('Search Circle on F95Zone', urls.f95zoneCircle, icons.f95zone),
ryuuCircle: createButton('Search Circle on Ryuugames', urls.ryuuCircle, icons.ryuugames)
};
// Append title buttons in a dropdown menu
const titleContainer = document.querySelector('.base_title_br');
if (titleContainer) {
const dropdownContainer = document.createElement('div');
dropdownContainer.className = 'dropdown-container';
// Create toggle button
const toggleButton = document.createElement('button');
toggleButton.className = 'dropdown-toggle';
toggleButton.textContent = 'Search Options';
toggleButton.addEventListener('click', () => {
dropdownContent.classList.toggle('hidden');
toggleButton.classList.toggle('collapsed');
});
// Create dropdown content
const dropdownContent = document.createElement('div');
dropdownContent.className = 'dropdown-content';
// F95Zone group
const f95Group = document.createElement('div');
f95Group.className = 'button-group';
const f95Label = document.createElement('div');
f95Label.className = 'group-label';
f95Label.textContent = 'F95Zone';
const f95Grid = document.createElement('div');
f95Grid.className = 'button-group-grid';
[buttons.f95Game, buttons.f95Forum, buttons.f95PID].forEach(btn => f95Grid.appendChild(btn));
f95Group.appendChild(f95Label);
f95Group.appendChild(f95Grid);
// OtomiGames group
const otomiGroup = document.createElement('div');
otomiGroup.className = 'button-group';
const otomiLabel = document.createElement('div');
otomiLabel.className = 'group-label';
otomiLabel.textContent = 'OtomiGames';
const otomiGrid = document.createElement('div');
otomiGrid.className = 'button-group-grid';
[buttons.otomiGame, buttons.otomiPID].forEach(btn => otomiGrid.appendChild(btn));
otomiGroup.appendChild(otomiLabel);
otomiGroup.appendChild(otomiGrid);
// Ryuugames group
const ryuuGroup = document.createElement('div');
ryuuGroup.className = 'button-group';
const ryuuLabel = document.createElement('div');
ryuuLabel.className = 'group-label';
ryuuLabel.textContent = 'Ryuugames';
const ryuuGrid = document.createElement('div');
ryuuGrid.className = 'button-group-grid';
[buttons.ryuuGame, buttons.ryuuPID].forEach(btn => ryuuGrid.appendChild(btn));
ryuuGroup.appendChild(ryuuLabel);
ryuuGroup.appendChild(ryuuGrid);
// Append groups to dropdown content
dropdownContent.appendChild(f95Group);
dropdownContent.appendChild(otomiGroup);
dropdownContent.appendChild(ryuuGroup);
// Append toggle button and content to container
dropdownContainer.appendChild(toggleButton);
dropdownContainer.appendChild(dropdownContent);
titleContainer.appendChild(dropdownContainer);
}
// Append circle buttons side by side
const circleWrapper = document.createElement('span');
circleWrapper.className = 'circle-btn-wrapper';
[buttons.f95Circle, buttons.ryuuCircle].forEach(btn => circleWrapper.appendChild(btn));
circleNameEl.insertAdjacentElement('afterend', circleWrapper);
}
// Use MutationObserver to detect when required elements are available
const observer = new MutationObserver((mutations, obs) => {
if (document.querySelector('h1#work_name') && document.querySelector('span[itemprop="brand"] a')) {
addSearchButtons();
obs.disconnect();
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();