Steam | Display game languages

Display images of selected languages on store search steam page.

当前为 2024-01-05 提交的版本,查看 最新版本

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