Steam | Display game languages

Display images of selected languages on store search steam page.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name            Steam | Display game languages
// @name:uk         Steam | Відобразити мови гри
// @namespace       http://tampermonkey.net/
// @version         06.06.2024
// @description     Display images of selected languages on store search steam page.
// @description:uk  Виводить обрані мови (svg картинка) на сторінці пошуку ігор в steam.
// @author          Black_Yuzia
// @match           https://store.steampowered.com/search*
// @match           https://store.steampowered.com/charts/*
// @icon            https://store.steampowered.com/favicon.ico
// @grant           GM_addStyle
// @grant           GM.xmlHttpRequest
// @grant           GM.getValue
// @grant           GM.setValue
// @grant           GM.registerMenuCommand
// @grant           GM.notification
// @run-at          document-end
// @license         CC BY-NC-ND 4.0
// @license-url     https://creativecommons.org/licenses/by-nc-nd/4.0/
// @compatible      firefox
// @compatible      chrome
// @compatible      opera
// @compatible      safari
// @compatible      edge
// @connect         localhost:1500
// @connect         tm.starserv.net
// ==/UserScript==
(() => {
  // src/index.ts
  (async function() {
    "use strict";
    GM_addStyle(`
        .mr-2 {
            margin-right: 2px
        }
    `);
    const languages = await initLanguages();
    await registerMenuCommands();
    const leftcol = document.querySelector(".leftcol");
    const charts = waitForElement("div[class*=steamchartsshell_SteamChartsShell]");
    if (leftcol) {
      await proceedStoreGames();
      const searchObserver = new MutationObserver(proceedStoreGames);
      searchObserver.observe(leftcol, {
        childList: true,
        subtree: true
      });
    }
    if (await charts) {
      await proceedChartsGames();
      const chartsObserver = new MutationObserver(proceedChartsGames);
      chartsObserver.observe(await charts, {
        childList: true,
        subtree: true
      });
    }
    async function proceedStoreGames() {
      const games = [...document.querySelectorAll(".search_result_row:not(.sld_process)")];
      for (const game of games) {
        game.className += " sld_process";
      }
      if (games.length > 0) {
        const ids = games.map(parseGameID);
        const res = await GM.xmlHttpRequest({
          // @ts-expect-error
          url: `${"https://tm.starserv.net"}/steam/games/lang?id=${ids.join(",")}`,
          method: "GET",
          responseType: "json",
          fetch: true
        });
        if (res.status === 200) {
          const { response } = res;
          for (let i = 0; i < response.length; i++) {
            const items = response[i];
            if (items.length > 0) {
              for (const lang of languages) {
                if (items.includes(lang)) {
                  const game = games[i];
                  const img = document.createElement("img");
                  img.height = 20;
                  img.width = 20;
                  img.className = "mr-2";
                  img.src = `https://fastdl.starserv.net/languages/${lang}@2x.svg`;
                  game.querySelector(".platform_img")?.parentElement.prepend(img);
                }
              }
            }
          }
        }
      }
    }
    async function proceedChartsGames() {
      const games = [...document.querySelectorAll("tr[class*=weeklytopsellers_TableRow]:not(.sld_process)")];
      for (const game of games) {
        game.className += " sld_process";
      }
      if (games.length > 0) {
        const ids = games.map((game) => parseGameID(game.querySelector("a")));
        const res = await GM.xmlHttpRequest({
          // @ts-expect-error
          url: `${"https://tm.starserv.net"}/steam/games/lang?id=${ids.join(",")}`,
          method: "GET",
          responseType: "json",
          fetch: true
        });
        if (res.status === 200) {
          const { response } = res;
          for (let i = 0; i < response.length; i++) {
            const items = response[i];
            if (items.length > 0) {
              for (const lang of languages) {
                if (items.includes(lang)) {
                  const game = games[i];
                  const img = document.createElement("img");
                  img.height = 20;
                  img.width = 20;
                  img.className = "mr-2";
                  img.src = `https://fastdl.starserv.net/languages/${lang}@2x.svg`;
                  game.querySelector("div[class*=weeklytopsellers_GameName]")?.prepend(img);
                }
              }
            }
          }
        }
      }
    }
  })();
  function parseGameID(e) {
    const [id] = e?.href?.match(/\d+/) || [];
    return id;
  }
  async function initLanguages() {
    const notification = await GM.getValue("notification", false);
    const languages = await GM.getValue("languages", ["ukrainian", "russian"]);
    console.log("lang", languages);
    if (!notification) {
      GM.notification("Please choose languages in menu! Default lang: 'Ukrainian,\u0279ussian'", "Warning");
    }
    return languages.reverse();
  }
  async function registerMenuCommands() {
    await GM.registerMenuCommand("Set Languages", async () => {
      const languages = prompt("Set language(s) [example: Ukrainian,English,\u0279ussian]");
      if (languages) {
        await GM.setValue("notification", true);
        await GM.setValue("languages", languages.split(",").map((v) => v.trim().toLowerCase()));
      }
    });
  }
  function waitForElement(selector) {
    return new Promise((resolve) => {
      if (document.querySelector(selector)) {
        return resolve(document.querySelector(selector));
      }
      const observer = new MutationObserver(() => {
        if (document.querySelector(selector)) {
          observer.disconnect();
          resolve(document.querySelector(selector));
        }
      });
      observer.observe(document.body, {
        childList: true,
        subtree: true
      });
      setTimeout(() => {
        resolve(null);
      }, 3e4);
    });
  }
})();