您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
单个和合集创意工坊,一键复制下载命令,按钮样式和图标严格匹配页面元素,支持合集内单mod复制,图标用Material Symbols字体图标(file_export)
// ==UserScript== // @name steam创意工坊(单个+合集)一键复制下载命令(全集成版) // @namespace http://tampermonkey.net/ // @version 2.0 // @description 单个和合集创意工坊,一键复制下载命令,按钮样式和图标严格匹配页面元素,支持合集内单mod复制,图标用Material Symbols字体图标(file_export) // @author Dost51552 // @match https://steamcommunity.com/workshop/browse/* // @match https://steamcommunity.com/sharedfiles/filedetails/* // @match https://steamcommunity.com/workshop/filedetails/* // @icon https://www.google.com/s2/favicons?sz=64&domain=steamcommunity.com // @require https://unpkg.com/[email protected]/dist/jquery.min.js // @license MIT // ==/UserScript== (() => { "use strict"; const SELECTED_COLOR = "#FF0000"; // 选择框选中颜色 const COMMON_BTN_STYLE = ` position: relative; height: 30px; padding-left: 28px; padding-right: 12px; background-repeat: no-repeat; display: inline-block; line-height: 30px; color: #939393; cursor: pointer; user-select: none; margin-left: 6px; border-radius: 3px; background-color: rgba(0, 0, 0, 0.4); transition: background-color 0.2s ease; border: none; `; const ICON_COMMON_STYLE = { background: "url('https://community.fastly.steamstatic.com/public/images/sharedfiles/ico_subscribe_tiled.png')", backgroundPosition: "0px 0px", backgroundRepeat: "no-repeat", height: "30px", width: "16px", position: "absolute", top: "0px", left: "8px", pointerEvents: "none", }; // 加载Material Symbols字体(file_export图标) function loadMaterialSymbolsFont() { if (!document.getElementById("material-symbols-style")) { const linkEl = document.createElement("link"); linkEl.id = "material-symbols-style"; linkEl.rel = "stylesheet"; linkEl.href = "https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200&icon_names=file_export"; document.head.appendChild(linkEl); } } // 获取游戏ID function getGameId() { return $("div.apphub_OtherSiteInfo.responsive_hidden > a").data("appid"); } // 获取页面mod id数组(合集中所有mod) function getModIds() { const ids = [...$(".collectionItemDetails > a")] .map( (el) => $(el) .attr("href") ?.match(/id=(\d+)/)?.[1] ) .filter(Boolean); if (!ids.length) { // 单个mod详情页也取id const singleId = window.location.search.match(/id=(\d+)/)?.[1]; if (singleId) ids.push(singleId); } return ids; } // 生成下载命令文本 function generateDownloadText(gameId, modIds) { return modIds.map((id) => `workshop_download_item ${gameId} ${id}`).join("\n"); } // 显示按钮下方提示浮层,2秒自动消失 function showToastBelowButton(btn, msg, isCollection = false) { $(".tm_custom_toast").remove(); const rect = btn.getBoundingClientRect(); const toast = document.createElement("div"); toast.className = "tm_custom_toast"; toast.innerText = msg; Object.assign(toast.style, { position: "fixed", left: `${rect.left}px`, top: `${rect.bottom + 6}px`, backgroundColor: isCollection ? "#97C0E3" : "#91b007", color: isCollection ? "#3C3D3E" : "#fff", padding: "6px 14px", borderRadius: "4px", fontSize: "14px", fontWeight: "600", boxShadow: "0 2px 6px rgba(0,0,0,0.25)", zIndex: 10000, userSelect: "none", pointerEvents: "none", whiteSpace: "nowrap", opacity: "0", transition: "opacity 0.3s ease", }); document.body.appendChild(toast); requestAnimationFrame(() => (toast.style.opacity = "1")); const hide = () => { toast.style.opacity = "0"; setTimeout(() => toast.remove(), 300); window.removeEventListener("scroll", hide); window.removeEventListener("click", hide); }; const timeoutId = setTimeout(hide, 2000); window.addEventListener( "scroll", () => { clearTimeout(timeoutId); hide(); }, { once: true } ); window.addEventListener( "click", () => { clearTimeout(timeoutId); hide(); }, { once: true } ); } // 复制文本到剪贴板,失败时fallback提示或跳转第三方 function copyToClipboard(text, btn, isCollection = false) { navigator.clipboard .writeText(text) .then(() => showToastBelowButton(btn, "下载命令已复制", isCollection)) .catch(() => { const modIds = getModIds(); if (!modIds.length) { prompt("复制失败,请手动复制:", text); return; } const fullUrl = `http://steamcommunity.com/sharedfiles/filedetails/?id=${modIds[0]}`; const newWin = window.open("https://steamworkshopdownloader.io/"); if (!newWin) { prompt("复制失败,且弹窗被阻止,请手动复制:", text); return; } const intervalId = setInterval(() => { try { if (newWin.document && newWin.document.readyState === "complete") { clearInterval(intervalId); const input = newWin.document.getElementById("downloadUrlLabel"); if (input) { input.value = fullUrl; let enterEvent; if (typeof KeyboardEvent === "function") { enterEvent = new KeyboardEvent("keydown", { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true, cancelable: true, }); } else { enterEvent = new Event("keydown", { bubbles: true, cancelable: true, }); } input.dispatchEvent(enterEvent); } } } catch { // 跨域异常忽略 } }, 300); }); } // 鼠标悬停按钮样式切换 function addHoverEffect(el) { el.addEventListener("mouseenter", () => { el.style.backgroundColor = "#97C0E3"; el.style.color = "#3C3D3E"; el.style.textDecoration = "none"; }); el.addEventListener("mouseleave", () => { el.style.backgroundColor = "rgba(0, 0, 0, 0.4)"; el.style.color = "#939393"; }); } // --- 创建单mod详情页复制按钮 --- function createButtonForSingle() { const refBtn = document.getElementById("SubscribeItemBtn"); if (!refBtn) return null; const btn = document.createElement("a"); btn.className = refBtn.className; btn.style.cssText = `${refBtn.style.cssText}; display: inline-block; margin-top: 6px; user-select:none; cursor:pointer; position: relative;`; const iconDiv = document.createElement("div"); iconDiv.className = "subscribeIcon"; Object.assign(iconDiv.style, { position: "absolute", top: "0", left: "8px", height: "30px", width: "16px", filter: "brightness(0%) saturate(100%) invert(85%) sepia(20%) hue-rotate(180deg)", backgroundImage: "url('https://community.fastly.steamstatic.com/public/images/sharedfiles/rate_ico_up_tiled.png?v=1')", backgroundPosition: "0 0", backgroundRepeat: "no-repeat", }); const spanText = document.createElement("span"); spanText.className = "subscribeText"; const optAdd = document.createElement("div"); optAdd.className = "subscribeOption subscribe selected"; optAdd.textContent = "复制"; const optSubscribed = document.createElement("div"); optSubscribed.className = "subscribeOption subscribed"; optSubscribed.textContent = "已订阅"; const optRemove = document.createElement("div"); optRemove.className = "subscribeOption remove"; optRemove.textContent = "取消订阅"; spanText.append(optAdd, optSubscribed, optRemove); btn.append(iconDiv, spanText); btn.onclick = () => { const gameId = getGameId(); const modIds = getModIds(); if (!gameId || !modIds.length) { showToastBelowButton(btn, "未获取到游戏ID或Mod ID"); return; } copyToClipboard(generateDownloadText(gameId, modIds), btn); }; refBtn.parentNode.insertBefore(btn, refBtn.nextSibling); return btn; } // --- 创建合集复制按钮 --- function createButtonForCollection() { const container = document.querySelector(".subscribeCollection"); const itemControls = document.getElementById("ItemControls"); if (!container) { if (!itemControls) return null; const btn = document.createElement("span"); btn.className = "general_btn subscribe"; btn.style.cssText = COMMON_BTN_STYLE; const iconDiv = document.createElement("div"); iconDiv.className = "duplicateCollectionIcon"; Object.assign(iconDiv.style, ICON_COMMON_STYLE); btn.appendChild(iconDiv); const spanText = document.createElement("span"); spanText.textContent = "复制命令"; btn.appendChild(spanText); addHoverEffect(btn); btn.onclick = () => { const gameId = getGameId(); const modIds = getModIds(); if (!gameId || !modIds.length) { showToastBelowButton(btn, "未获取到游戏ID或Mod ID"); return; } copyToClipboard(generateDownloadText(gameId, modIds), btn, true); }; const btns = itemControls.querySelectorAll(".general_btn"); if (btns.length) btns[btns.length - 1].insertAdjacentElement("afterend", btn); else itemControls.appendChild(btn); return btn; } const saveBtn = [...container.querySelectorAll(".general_btn.subscribe")].find( (el) => el.textContent.trim() === "保存至收藏" ); if (!saveBtn) return null; const btn = document.createElement("span"); btn.className = "general_btn subscribe"; btn.style.cssText = COMMON_BTN_STYLE; const iconDiv = document.createElement("div"); iconDiv.className = "duplicateCollectionIcon"; Object.assign(iconDiv.style, ICON_COMMON_STYLE); btn.appendChild(iconDiv); const spanText = document.createElement("span"); spanText.textContent = "复制命令"; btn.appendChild(spanText); addHoverEffect(btn); btn.onclick = () => { const gameId = getGameId(); const modIds = getModIds(); if (!gameId || !modIds.length) { showToastBelowButton(btn, "未获取到游戏ID或Mod ID"); return; } copyToClipboard(generateDownloadText(gameId, modIds), btn, true); }; saveBtn.insertAdjacentElement("afterend", btn); return btn; } // --- 合集内单个mod复制按钮 --- function createCopyButtonForSingleModsInCollection() { loadMaterialSymbolsFont(); $(".collectionItemDetails").each((_, container) => { const $container = $(container); const href = $container.find("> a").attr("href"); const modId = href?.match(/id=(\d+)/)?.[1]; if (!modId) return; // 获取游戏ID(合集内mod) const appidHref = $container.find(".workshopItemAuthorName a").attr("href"); const appId = appidHref?.match(/appid=(\d+)/)?.[1]; if (!appId) return; const subBtn = $(`#SubscribeItemBtn${modId}`); if (!subBtn.length || subBtn.prev(".tm_copy_single_mod_btn").length) return; const copyBtn = document.createElement("a"); copyBtn.className = "general_btn subscribe tm_copy_single_mod_btn"; Object.assign(copyBtn.style, { position: "relative", height: "30px", width: "31px", padding: "0", backgroundRepeat: "no-repeat", display: "inline-block", lineHeight: "30px", cursor: "pointer", color: "#939393", userSelect: "none", marginBottom: "0", borderRadius: "3px", backgroundColor: "rgba(0, 0, 0, 0.4)", transition: "background-color 0.2s ease", }); addHoverEffect(copyBtn); const iconSpan = document.createElement("span"); iconSpan.className = "material-symbols-outlined"; iconSpan.textContent = "file_export"; Object.assign(iconSpan.style, { position: "absolute", top: "5px", left: "4px", fontVariationSettings: "'wght' 400, 'FILL' 0, 'GRAD' 0", fontSize: "22px", color: "#DADADA", pointerEvents: "none", userSelect: "none", }); copyBtn.appendChild(iconSpan); copyBtn.addEventListener("click", (e) => { e.preventDefault(); e.stopPropagation(); copyToClipboard(`workshop_download_item ${appId} ${modId}`, copyBtn, true); }); const parent = subBtn.parent(); if (parent.css("position") === "static") parent.css("position", "relative"); subBtn.before(copyBtn); }); } // --- 浏览页每个mod添加复制按钮与选择框 --- function addButtonsToMods() { $(".workshopItem").each((_, item) => { const $item = $(item); if ($item.css("position") === "static") { $item.css("position", "relative"); } const href = $item.find("a.item_link").attr("href"); if (!href) return; const modIdMatch = href.match(/id=(\d+)/); if (!modIdMatch) return; const modId = modIdMatch[1]; const gameId = getGameId(); if (!gameId) return; if ($item.find(".tm_copy_btn").length === 0) { // 复制按钮 - 右下角 const copyBtn = $("<button>") .addClass("tm_copy_btn subscribeIcon") .attr("title", "复制下载命令") .css({ position: "absolute", bottom: "4px", right: "4px", height: "30px", width: "16px", cursor: "pointer", "background-color": "transparent", "background-image": "url('https://community.fastly.steamstatic.com/public/images/sharedfiles/ico_subscribe_tiled.png')", "background-position": "0 0", "background-repeat": "no-repeat", border: "none", outline: "none", }) .on("click", () => { const cmd = generateDownloadText(gameId, [modId]); navigator.clipboard.writeText(cmd).then(() => { showToastBelowButton(copyBtn[0], "下载命令已复制"); }).catch(() => { prompt("复制失败,请手动复制下面的命令:", cmd); }); }); // 选择框 - 复制按钮正上方,紧邻 const selectCheckbox = $("<button>") .addClass("tm_select_checkbox") .attr({ title: "选择该Mod", "data-selected": "false", }) .css({ position: "absolute", bottom: "36px", // 30 + 6 right: "4px", height: "16px", width: "16px", cursor: "pointer", padding: 0, outline: "none", "background-color": "transparent", border: `2px solid #FFFFFF`, "border-radius": "3px", }) .on("click", function () { const selected = $(this).attr("data-selected") === "true"; if (selected) { $(this).attr("data-selected", "false"); $(this).css("background-color", "transparent"); } else { $(this).attr("data-selected", "true"); $(this).css("background-color", SELECTED_COLOR); } }); $item.append(copyBtn, selectCheckbox); } }); } // --- 批量复制按钮添加到“按日期筛选”按钮右侧,间距5px --- function addBatchCopyButton() { const filterBtn = $("span.general_btn.createCollection"); if (filterBtn.length === 0) return; if ($("#tm_batch_copy_btn").length > 0) return; const batchBtn = $("<button>") .attr("id", "tm_batch_copy_btn") .text("批量复制下载命令") .addClass("general_btn createCollection") .css({ "margin-left": "5px", cursor: "pointer", }) .on("mouseenter", function () { this.style.cursor = "pointer"; }) .on("click", () => { const gameId = getGameId(); if (!gameId) { alert("未找到游戏ID,无法复制"); return; } const selectedMods = []; $(".workshopItem .tm_select_checkbox[data-selected='true']").each((_, el) => { const $item = $(el).closest(".workshopItem"); const href = $item.find("a.item_link").attr("href"); const modIdMatch = href?.match(/id=(\d+)/); if (modIdMatch) selectedMods.push(modIdMatch[1]); }); if (selectedMods.length === 0) { alert("未选择任何Mod"); return; } const cmdText = selectedMods.map(id => `workshop_download_item ${gameId} ${id}`).join("\n"); navigator.clipboard.writeText(cmdText).then(() => { alert(`已复制${selectedMods.length}个Mod的下载命令`); }).catch(() => { prompt("复制失败,请手动复制下面的命令:", cmdText); }); }); filterBtn.after(batchBtn); } // --- 入口函数 --- function main() { $(".tm_custom_toast").remove(); $("#tm_batch_copy_btn").remove(); const path = window.location.pathname; // 单个mod详情页 或 合集详情页 if ( path.startsWith("/workshop/filedetails") || path.startsWith("/sharedfiles/filedetails") ) { createButtonForCollection(); createCopyButtonForSingleModsInCollection(); // 没合集按钮时,详情页加单个按钮 if ( !document.querySelector(".general_btn.subscribe") && document.querySelector(".game_area_purchase_game #SubscribeItemBtn") ) { createButtonForSingle(); } } // 浏览页加选择框和复制按钮及批量复制按钮 if (path.startsWith("/workshop/browse")) { addButtonsToMods(); addBatchCopyButton(); } } $(document).ready(main); // 如果页面有动态加载需求,可考虑加观察者自动刷新按钮,暂不添加 })();