Steam | Display game languages

Display images of selected languages on store search steam page.

安装此脚本
作者推荐脚本

您可能也喜欢Steam | Hide Sensitive Data

安装此脚本
  1. // ==UserScript==
  2. // @name Steam | Display game languages
  3. // @name:uk Steam | Відобразити мови гри
  4. // @namespace http://tampermonkey.net/
  5. // @version 06.06.2024
  6. // @description Display images of selected languages on store search steam page.
  7. // @description:uk Виводить обрані мови (svg картинка) на сторінці пошуку ігор в steam.
  8. // @author Black_Yuzia
  9. // @match https://store.steampowered.com/search*
  10. // @match https://store.steampowered.com/charts/*
  11. // @icon https://store.steampowered.com/favicon.ico
  12. // @grant GM_addStyle
  13. // @grant GM.xmlHttpRequest
  14. // @grant GM.getValue
  15. // @grant GM.setValue
  16. // @grant GM.registerMenuCommand
  17. // @grant GM.notification
  18. // @run-at document-end
  19. // @license CC BY-NC-ND 4.0
  20. // @license-url https://creativecommons.org/licenses/by-nc-nd/4.0/
  21. // @compatible firefox
  22. // @compatible chrome
  23. // @compatible opera
  24. // @compatible safari
  25. // @compatible edge
  26. // @connect localhost:1500
  27. // @connect tm.starserv.net
  28. // ==/UserScript==
  29. (() => {
  30. // src/index.ts
  31. (async function() {
  32. "use strict";
  33. GM_addStyle(`
  34. .mr-2 {
  35. margin-right: 2px
  36. }
  37. `);
  38. const languages = await initLanguages();
  39. await registerMenuCommands();
  40. const leftcol = document.querySelector(".leftcol");
  41. const charts = waitForElement("div[class*=steamchartsshell_SteamChartsShell]");
  42. if (leftcol) {
  43. await proceedStoreGames();
  44. const searchObserver = new MutationObserver(proceedStoreGames);
  45. searchObserver.observe(leftcol, {
  46. childList: true,
  47. subtree: true
  48. });
  49. }
  50. if (await charts) {
  51. await proceedChartsGames();
  52. const chartsObserver = new MutationObserver(proceedChartsGames);
  53. chartsObserver.observe(await charts, {
  54. childList: true,
  55. subtree: true
  56. });
  57. }
  58. async function proceedStoreGames() {
  59. const games = [...document.querySelectorAll(".search_result_row:not(.sld_process)")];
  60. for (const game of games) {
  61. game.className += " sld_process";
  62. }
  63. if (games.length > 0) {
  64. const ids = games.map(parseGameID);
  65. const res = await GM.xmlHttpRequest({
  66. // @ts-expect-error
  67. url: `${"https://tm.starserv.net"}/steam/games/lang?id=${ids.join(",")}`,
  68. method: "GET",
  69. responseType: "json",
  70. fetch: true
  71. });
  72. if (res.status === 200) {
  73. const { response } = res;
  74. for (let i = 0; i < response.length; i++) {
  75. const items = response[i];
  76. if (items.length > 0) {
  77. for (const lang of languages) {
  78. if (items.includes(lang)) {
  79. const game = games[i];
  80. const img = document.createElement("img");
  81. img.height = 20;
  82. img.width = 20;
  83. img.className = "mr-2";
  84. img.src = `https://fastdl.starserv.net/languages/${lang}@2x.svg`;
  85. game.querySelector(".platform_img")?.parentElement.prepend(img);
  86. }
  87. }
  88. }
  89. }
  90. }
  91. }
  92. }
  93. async function proceedChartsGames() {
  94. const games = [...document.querySelectorAll("tr[class*=weeklytopsellers_TableRow]:not(.sld_process)")];
  95. for (const game of games) {
  96. game.className += " sld_process";
  97. }
  98. if (games.length > 0) {
  99. const ids = games.map((game) => parseGameID(game.querySelector("a")));
  100. const res = await GM.xmlHttpRequest({
  101. // @ts-expect-error
  102. url: `${"https://tm.starserv.net"}/steam/games/lang?id=${ids.join(",")}`,
  103. method: "GET",
  104. responseType: "json",
  105. fetch: true
  106. });
  107. if (res.status === 200) {
  108. const { response } = res;
  109. for (let i = 0; i < response.length; i++) {
  110. const items = response[i];
  111. if (items.length > 0) {
  112. for (const lang of languages) {
  113. if (items.includes(lang)) {
  114. const game = games[i];
  115. const img = document.createElement("img");
  116. img.height = 20;
  117. img.width = 20;
  118. img.className = "mr-2";
  119. img.src = `https://fastdl.starserv.net/languages/${lang}@2x.svg`;
  120. game.querySelector("div[class*=weeklytopsellers_GameName]")?.prepend(img);
  121. }
  122. }
  123. }
  124. }
  125. }
  126. }
  127. }
  128. })();
  129. function parseGameID(e) {
  130. const [id] = e?.href?.match(/\d+/) || [];
  131. return id;
  132. }
  133. async function initLanguages() {
  134. const notification = await GM.getValue("notification", false);
  135. const languages = await GM.getValue("languages", ["ukrainian", "russian"]);
  136. console.log("lang", languages);
  137. if (!notification) {
  138. GM.notification("Please choose languages in menu! Default lang: 'Ukrainian,\u0279ussian'", "Warning");
  139. }
  140. return languages.reverse();
  141. }
  142. async function registerMenuCommands() {
  143. await GM.registerMenuCommand("Set Languages", async () => {
  144. const languages = prompt("Set language(s) [example: Ukrainian,English,\u0279ussian]");
  145. if (languages) {
  146. await GM.setValue("notification", true);
  147. await GM.setValue("languages", languages.split(",").map((v) => v.trim().toLowerCase()));
  148. }
  149. });
  150. }
  151. function waitForElement(selector) {
  152. return new Promise((resolve) => {
  153. if (document.querySelector(selector)) {
  154. return resolve(document.querySelector(selector));
  155. }
  156. const observer = new MutationObserver(() => {
  157. if (document.querySelector(selector)) {
  158. observer.disconnect();
  159. resolve(document.querySelector(selector));
  160. }
  161. });
  162. observer.observe(document.body, {
  163. childList: true,
  164. subtree: true
  165. });
  166. setTimeout(() => {
  167. resolve(null);
  168. }, 3e4);
  169. });
  170. }
  171. })();