您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds CNPrice column to SteamDB sales page with selectable currency conversions.
- // ==UserScript==
- // @name SteamDB Sales CNPrice Injector
- // @namespace https://liangying.eu.org/
- // @version 1.1.0
- // @description Adds CNPrice column to SteamDB sales page with selectable currency conversions.
- // @author LiangYing
- // @match https://steamdb.info/sales/*
- // @grant GM_xmlhttpRequest
- // @grant GM_setValue
- // @grant GM_getValue
- // @connect store.steampowered.com
- // @connect api.exchangerate-api.com
- // @icon https://store.steampowered.com/favicon.ico
- // @license MIT
- // @run-at document-end
- // ==/UserScript==
- (function() {
- 'use strict';
- const COLUMN_CLASS = 'compare-price-column';
- const PRICE_CACHE_DURATION = 24 * 60 * 60 * 1000; // 24小时的缓存时间
- // 货币符号映射
- const CURRENCY_SYMBOLS = {
- CNY: '¥',
- JPY: '¥',
- HKD: 'HK$',
- USD: '$',
- RUB: '₽',
- PHP: '₱',
- INR: '₹',
- KRW: '₩',
- CAD: 'C$'
- };
- // 汇率对象 - 存储1 CNY兑换多少目标货币
- let exchangeRates = {
- CNY: 1,
- JPY: 16.5, // 1 CNY = 16.5 JPY
- HKD: 1.09, // 1 CNY = 1.09 HKD
- USD: 0.14, // 1 CNY = 0.14 USD
- RUB: 12.7, // 1 CNY = 12.7 RUB
- PHP: 7.74, // 1 CNY = 7.74 PHP
- INR: 11.52, // 1 CNY = 11.52 INR
- KRW: 185.87, // 1 CNY = 185.87 KRW
- CAD: 0.19 // 1 CNY = 0.19 CAD
- };
- // 当前选择的货币
- let currentCurrency = null;
- // 价格缓存
- const priceCache = {
- // 获取缓存的价格
- get: function (appId) {
- const cached = GM_getValue(`price_${appId}`);
- if (!cached) return null;
- const { timestamp, data } = JSON.parse(cached);
- if (Date.now() - timestamp > PRICE_CACHE_DURATION) {
- GM_setValue(`price_${appId}`, '');
- return null;
- }
- return data;
- },
- // 设置价格缓存
- set: function (appId, priceData) {
- const cacheData = {
- timestamp: Date.now(),
- data: priceData
- };
- GM_setValue(`price_${appId}`, JSON.stringify(cacheData));
- }
- };
- // 创建UI元素
- function createUI() {
- // 尝试找到现有的容器
- const existingContainer = document.querySelector('.dt-layout-end');
- if (existingContainer && !existingContainer.querySelector('.currency-selector')) {
- const rateSelect = document.createElement('select');
- rateSelect.className = 'currency-selector';
- rateSelect.innerHTML = `
- <option value="">-- LiangYing Exchange --</option>
- <option value="CNY">CNY (中国)</option>
- <option value="JPY">JPY (日本)</option>
- <option value="HKD">HKD (香港)</option>
- <option value="USD">USD (美国)</option>
- <option value="RUB">RUB (俄罗斯)</option>
- <option value="PHP">PHP (菲律宾)</option>
- <option value="INR">INR (印度)</option>
- <option value="KRW">KRW (韩国)</option>
- <option value="CAD">CAD (加拿大)</option>
- `;
- rateSelect.style.marginLeft = '10px';
- rateSelect.style.padding = '5px';
- rateSelect.style.backgroundColor = '#1b2838';
- rateSelect.style.color = '#c6d4df';
- rateSelect.style.border = '1px solid #2a475e';
- existingContainer.appendChild(rateSelect);
- rateSelect.addEventListener('change', function () {
- currentCurrency = this.value;
- if (!currentCurrency) {
- removePriceColumn();
- return;
- }
- updateExchangeRates(() => {
- ensurePriceColumn();
- refreshPrices();
- });
- });
- return;
- }
- // 创建新的容器
- if (!document.querySelector('.currency-selector')) {
- const controlContainer = document.createElement('div');
- controlContainer.className = 'currency-selector-container';
- controlContainer.style.margin = '10px 0';
- controlContainer.style.textAlign = 'right';
- const rateSelect = document.createElement('select');
- rateSelect.className = 'currency-selector';
- rateSelect.innerHTML = `
- <option value="">-- LiangYing Exchange --</option>
- <option value="CNY">CNY (中国)</option>
- <option value="JPY">JPY (日本)</option>
- <option value="HKD">HKD (香港)</option>
- <option value="USD">USD (美国)</option>
- <option value="RUB">RUB (俄罗斯)</option>
- <option value="PHP">PHP (菲律宾)</option>
- <option value="INR">INR (印度)</option>
- <option value="KRW">KRW (韩国)</option>
- <option value="CAD">CAD (加拿大)</option>
- `;
- rateSelect.style.marginLeft = '10px';
- rateSelect.style.padding = '5px';
- rateSelect.style.backgroundColor = '#1b2838';
- rateSelect.style.color = '#c6d4df';
- rateSelect.style.border = '1px solid #2a475e';
- controlContainer.appendChild(rateSelect);
- // 插入UI元素
- const tableElement = document.querySelector('.table-sales') ||
- document.querySelector('.dataTable');
- if (tableElement && tableElement.parentNode) {
- tableElement.parentNode.insertBefore(controlContainer, tableElement);
- } else {
- const tableContainer = document.querySelector('.table-container') ||
- document.querySelector('.table-responsive');
- if (tableContainer) {
- tableContainer.insertBefore(controlContainer, tableContainer.firstChild);
- }
- }
- rateSelect.addEventListener('change', function () {
- currentCurrency = this.value;
- if (!currentCurrency) {
- removePriceColumn();
- return;
- }
- updateExchangeRates(() => {
- ensurePriceColumn();
- refreshPrices();
- });
- });
- }
- }
- // 更新汇率数据
- function updateExchangeRates(callback) {
- GM_xmlhttpRequest({
- method: 'GET',
- url: 'https://api.exchangerate-api.com/v4/latest/CNY',
- onload: function (response) {
- if (response.status === 200) {
- try {
- const data = JSON.parse(response.responseText);
- exchangeRates.JPY = data.rates.JPY;
- exchangeRates.HKD = data.rates.HKD;
- exchangeRates.USD = data.rates.USD;
- exchangeRates.RUB = data.rates.RUB;
- exchangeRates.PHP = data.rates.PHP;
- exchangeRates.INR = data.rates.INR;
- exchangeRates.KRW = data.rates.KRW;
- exchangeRates.CAD = data.rates.CAD;
- if (callback) callback();
- } catch (error) {
- console.error('Failed to parse exchange rates, using defaults:', error);
- if (callback) callback();
- }
- } else {
- console.error('Failed to fetch exchange rates, using defaults:', response.status);
- if (callback) callback();
- }
- },
- onerror: function() {
- console.error('Failed to fetch exchange rates, using defaults');
- if (callback) callback();
- }
- });
- }
- // 解析价格
- function parsePrice(priceStr) {
- return parseFloat(priceStr.replace(/[^0-9.]/g, '')) || 0;
- }
- // 获取商店价格
- function fetchGamePrice(appId, callback, retryCount = 0) {
- const maxRetries = 3;
- // 先检查缓存
- const cachedPrice = priceCache.get(appId);
- if (cachedPrice) {
- callback(cachedPrice);
- return;
- }
- GM_xmlhttpRequest({
- method: 'GET',
- url: `https://store.steampowered.com/api/appdetails/?appids=${appId}&cc=cn`,
- timeout: 10000,
- onload: function (response) {
- try {
- const data = JSON.parse(response.responseText);
- if (data[appId]?.success) {
- const priceInfo = data[appId].data.price_overview;
- if (priceInfo) {
- priceCache.set(appId, priceInfo);
- }
- callback(priceInfo);
- } else {
- callback(null);
- }
- } catch (error) {
- if (retryCount < maxRetries) {
- setTimeout(() => {
- fetchGamePrice(appId, callback, retryCount + 1);
- }, 2000 * (retryCount + 1));
- } else {
- callback(null);
- }
- }
- },
- onerror: function () {
- if (retryCount < maxRetries) {
- setTimeout(() => {
- fetchGamePrice(appId, callback, retryCount + 1);
- }, 2000 * (retryCount + 1));
- } else {
- callback(null);
- }
- }
- });
- }
- // 确保价格列存在
- function ensurePriceColumn() {
- if (!currentCurrency) return;
- const header = document.querySelector('.table-sales thead tr, .dataTable thead tr');
- if (!header) return;
- let priceHeader = header.querySelector(`.${COLUMN_CLASS}`);
- if (!priceHeader) {
- priceHeader = document.createElement('th');
- priceHeader.className = COLUMN_CLASS;
- header.appendChild(priceHeader);
- }
- // 更新列标题
- const symbol = CURRENCY_SYMBOLS[currentCurrency] || currentCurrency;
- priceHeader.textContent = `${symbol} Price Comparison`;
- priceHeader.style.whiteSpace = 'nowrap';
- const rows = document.querySelectorAll('.table-sales tbody tr, .dataTable tbody tr');
- rows.forEach(row => {
- if (!row.querySelector(`.${COLUMN_CLASS}`)) {
- const priceCell = document.createElement('td');
- priceCell.className = COLUMN_CLASS;
- row.appendChild(priceCell);
- }
- });
- }
- // 移除价格列
- function removePriceColumn() {
- const header = document.querySelector('.table-sales thead tr, .dataTable thead tr');
- if (!header) return;
- const priceHeader = header.querySelector(`.${COLUMN_CLASS}`);
- if (priceHeader) {
- priceHeader.remove();
- }
- const rows = document.querySelectorAll('.table-sales tbody tr, .dataTable tbody tr');
- rows.forEach(row => {
- const priceCell = row.querySelector(`.${COLUMN_CLASS}`);
- if (priceCell) {
- priceCell.remove();
- }
- });
- }
- // 更新单个游戏的价格显示
- function updateGamePrice(row) {
- if (!currentCurrency) return;
- // 获取游戏ID
- const appId = row.dataset.appid;
- if (!appId) return;
- // 找到价格单元格
- let priceCell = row.querySelector(`.${COLUMN_CLASS}`);
- if (!priceCell) {
- priceCell = document.createElement('td');
- priceCell.className = COLUMN_CLASS;
- row.appendChild(priceCell);
- }
- // 如果已经有价格数据,则跳过
- if (priceCell.textContent && !priceCell.textContent.includes('Loading')) {
- return;
- }
- priceCell.textContent = 'Loading...';
- // 从SteamDB表格中获取目标货币价格(第5列)
- const targetCurrencyPriceElement = row.querySelector('td:nth-child(5)');
- const targetCurrencyPrice = targetCurrencyPriceElement ?
- parsePrice(targetCurrencyPriceElement.textContent) : 0;
- if (!targetCurrencyPrice) {
- priceCell.textContent = 'N/A';
- return;
- }
- fetchGamePrice(appId, (priceInfo) => {
- if (priceInfo) {
- // 中国区价格(人民币)
- const cnPrice = priceInfo.final / 100;
- // 汇率:1 CNY = X 目标货币
- const exchangeRate = exchangeRates[currentCurrency];
- // 转换后的目标货币价格
- const convertedPrice = cnPrice * exchangeRate;
- // 计算比例:转换后价格 / SteamDB显示的目标货币价格
- const ratio = targetCurrencyPrice > 0 ?
- (convertedPrice / targetCurrencyPrice * 100).toFixed(2) : 'N/A';
- // 设置颜色
- const color = (ratio < 100) ? '#5cff47' :
- (ratio > 100) ? '#ff4747' : '#ccc';
- // 获取货币符号
- const targetSymbol = CURRENCY_SYMBOLS[currentCurrency] || currentCurrency;
- const cnySymbol = CURRENCY_SYMBOLS.CNY;
- // 更新单元格内容
- priceCell.innerHTML = `
- <div>${cnySymbol}${cnPrice.toFixed(2)}</div>
- <div>${targetSymbol}${convertedPrice.toFixed(2)}</div>
- <div style="color: ${color}; font-weight: bold">${ratio}%</div>
- `;
- } else {
- priceCell.textContent = 'N/A';
- }
- });
- }
- // 刷新所有价格
- function refreshPrices() {
- if (!currentCurrency) return;
- ensurePriceColumn();
- const rows = document.querySelectorAll('.table-sales tbody tr, .dataTable tbody tr');
- rows.forEach((row, index) => {
- setTimeout(() => {
- try {
- updateGamePrice(row);
- } catch (error) {
- console.error(`Error updating price for row ${index}:`, error);
- }
- }, index * 300);
- });
- }
- // 监听表格变化
- function setupTableObserver() {
- const tableBody = document.querySelector('.table-sales tbody, .dataTable tbody');
- if (tableBody) {
- const tableObserver = new MutationObserver((mutations) => {
- for (const mutation of mutations) {
- if (mutation.type === 'childList' && currentCurrency) {
- ensurePriceColumn();
- const newRows = Array.from(mutation.addedNodes).filter(node =>
- node.nodeType === 1 && node.matches('tr')
- );
- newRows.forEach(updateGamePrice);
- }
- }
- });
- tableObserver.observe(tableBody, {
- childList: true,
- subtree: true
- });
- }
- // 监听分页变化
- const paginationContainer = document.querySelector('.pagination, .dataTables_paginate');
- if (paginationContainer) {
- const paginationObserver = new MutationObserver(() => {
- if (currentCurrency) {
- ensurePriceColumn();
- refreshPrices();
- }
- });
- paginationObserver.observe(paginationContainer, {
- childList: true,
- subtree: true
- });
- }
- // 筛选表单监听
- const filterForm = document.getElementById('js-filters');
- if (filterForm) {
- filterForm.addEventListener('submit', () => {
- setTimeout(() => {
- if (currentCurrency) {
- ensurePriceColumn();
- refreshPrices();
- }
- }, 500);
- });
- }
- }
- // 初始化
- function init() {
- createUI();
- setupTableObserver();
- }
- // 等待页面加载完成后初始化
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', init);
- } else {
- init();
- }
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址