您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a button to copy SteamCMD workshop download commands
// ==UserScript== // @name Steam Workshop Copy SteamCMD Button // @namespace http://steamcommunity.com/ // @version 1.0 // @description Adds a button to copy SteamCMD workshop download commands // @author TheFantasticLoki // @match https://steamcommunity.com/sharedfiles/filedetails/* // @grant GM_setClipboard // @license MIT // ==/UserScript== (function() { 'use strict'; // Function to extract item ID from URL function getWorkshopItemIdFromUrl() { const urlParams = new URLSearchParams(window.location.search); return urlParams.get('id'); } // Function to find game ID function getGameId() { // Look for game ID in breadcrumbs const breadcrumbs = document.querySelectorAll('.breadcrumbs a'); for (const crumb of breadcrumbs) { const href = crumb.getAttribute('href'); if (href && href.includes('/app/')) { const match = href.match(/\/app\/(\d+)/); if (match && match[1]) { return match[1]; } } } // Alternative method: look for game app link const appLinks = document.querySelectorAll('a[href*="/app/"]'); for (const link of appLinks) { const match = link.href.match(/\/app\/(\d+)/); if (match && match[1]) { return match[1]; } } return null; } function createCopyButton(gameId, workshopItemId) { // Create styles for our custom elements const styleEl = document.createElement('style'); styleEl.textContent = ` .steam_cmd_copy_btn { display: flex; align-items: center; justify-content: center; cursor: pointer; width: 33px; height: 33px; background: #3d6c8d; border-radius: 4px; margin: 0 5px; transition: background 0.2s; position: relative; } .steam_cmd_copy_btn:hover { background: #67c1f5; } .steam_cmd_copy_btn svg { width: 25px; height: 25px; vertical-align: middle; fill: white; } .steam_cmd_copy_tooltip { display: none; position: fixed; background-color: #171a21; color: #fff; text-align: center; border-radius: 3px; padding: 8px 12px; z-index: 9999; border: 1px solid #4d4d4d; white-space: nowrap; font-size: 12px; box-shadow: 0 0 5px rgba(0,0,0,0.3); pointer-events: none; } .steam_cmd_command { font-family: monospace; background: #32353c; padding: 4px 6px; border-radius: 2px; margin-top: 5px; display: block; } `; document.head.appendChild(styleEl); // Create the button element const copyButton = document.createElement('div'); copyButton.className = 'steam_cmd_copy_btn'; // Create SVG icon for clipboard copyButton.innerHTML = ` <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/> </svg> `; // Create tooltip as a separate element attached to body const tooltip = document.createElement('div'); tooltip.className = 'steam_cmd_copy_tooltip'; tooltip.innerHTML = `Copy SteamCMD<span class="steam_cmd_command">workshop_download_item ${gameId} ${workshopItemId}</span>`; document.body.appendChild(tooltip); // Handle tooltip positioning copyButton.addEventListener('mouseenter', function(e) { const rect = copyButton.getBoundingClientRect(); tooltip.style.display = 'block'; // Position tooltip above the button tooltip.style.left = rect.left + (rect.width / 2) - (tooltip.offsetWidth / 2) + 'px'; tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px'; // If tooltip would go off the top, show it below instead if (rect.top - tooltip.offsetHeight < 0) { tooltip.style.top = rect.bottom + 10 + 'px'; } }); copyButton.addEventListener('mouseleave', function() { tooltip.style.display = 'none'; }); // Add click event to copy the command copyButton.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); const command = `workshop_download_item ${gameId} ${workshopItemId}`; GM_setClipboard(command); // Visual feedback tooltip.innerHTML = 'Copied!'; setTimeout(function() { tooltip.innerHTML = `Copy SteamCMD<span class="steam_cmd_command">workshop_download_item ${gameId} ${workshopItemId}</span>`; }, 2000); }); // Clean up tooltip when button is removed const cleanupTooltip = new MutationObserver(function(mutations) { if (!document.body.contains(copyButton)) { tooltip.remove(); cleanupTooltip.disconnect(); } }); cleanupTooltip.observe(document.body, { childList: true, subtree: true }); return copyButton; } // Main function to run when page is loaded function addCopyButtons() { // Check if we've already run if (document.querySelector('.steam_cmd_copy_btn')) { return; // Skip if buttons already exist } const gameId = getGameId(); if (!gameId) { console.log('Could not find game ID'); return; } // First check for single item page const singleSubscribeButton = document.getElementById('SubscribeItemBtn'); if (singleSubscribeButton) { const workshopItemId = getWorkshopItemIdFromUrl(); if (workshopItemId) { // Find the parent div of the subscribe button const subscribeButtonContainer = singleSubscribeButton.closest('div'); if (subscribeButtonContainer && subscribeButtonContainer.parentNode) { // Create a new container div for our button const copyButtonContainer = document.createElement('div'); copyButtonContainer.style.display = 'inline-block'; copyButtonContainer.style.marginLeft = '5px'; // Add the copy button to its container const copyButton = createCopyButton(gameId, workshopItemId); copyButtonContainer.appendChild(copyButton); // Insert after the subscribe button's container subscribeButtonContainer.parentNode.insertBefore( copyButtonContainer, subscribeButtonContainer.nextSibling ); } } return; } // Collection page handling - targeting the exact structure seen in collections const subscriptionControls = document.querySelectorAll('.subscriptionControls'); subscriptionControls.forEach(controlsDiv => { // Check if we already added a button here if (controlsDiv.querySelector('.steam_cmd_copy_btn')) { return; } // Find the subscribe button inside this controls div const subscribeButton = controlsDiv.querySelector('[id^="SubscribeItemBtn"]'); if (subscribeButton) { // Extract workshop ID from button ID (format: SubscribeItemBtn###) const match = subscribeButton.id.match(/SubscribeItemBtn(\d+)/); if (match && match[1]) { const workshopItemId = match[1]; // Create copy button const copyButton = createCopyButton(gameId, workshopItemId); // Directly append to the controls div instead of creating a new container controlsDiv.appendChild(copyButton); } } }); } // Use mutation observer to run when content changes (for dynamic loading) function initObserver() { const observer = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.addedNodes.length) { addCopyButtons(); } } }); observer.observe(document.body, { childList: true, subtree: true }); } // Check if the page is already loaded if (document.readyState === 'complete' || document.readyState === 'interactive') { addCopyButtons(); initObserver(); } else { // Wait for page to load window.addEventListener('DOMContentLoaded', () => { addCopyButtons(); initObserver(); }); } })();