GGn Get Languages From Steam

Easily get languages from Steam. Edited from "GGn Steam Language BBCode quick copy".

  1. // ==UserScript==
  2. // @name GGn Get Languages From Steam
  3. // @version 7
  4. // @description Easily get languages from Steam. Edited from "GGn Steam Language BBCode quick copy".
  5. // @author lucianjp, ingts
  6. // @match https://gazellegames.net/torrents.php?action=editgroup*
  7. // @match https://gazellegames.net/torrents.php?id=*
  8. // @match https://gazellegames.net/upload.php*
  9. // @match https://store.steampowered.com/app/*
  10. // @match https://web.archive.org/*/store.steampowered.com/*
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_setClipboard
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @grant GM_deleteValue
  16. // @grant unsafeWindow
  17. // @connect store.steampowered.com
  18. // @namespace https://greasyfork.org/
  19. // ==/UserScript==
  20.  
  21. const text_only = true
  22. const auto_get = true
  23. const use_language_codes = false
  24. const uppercase_language_codes = false
  25. const bold_list = false
  26. const delimiter = ', '
  27.  
  28. const globals = unsafeWindow.GetLanguagesFromSteam = {}
  29. if (location.hostname === 'store.steampowered.com' || location.hostname === 'web.archive.org')
  30. steamButton()
  31. if (location.href.endsWith('upload.php') && GM_getValue('steam', null))
  32. GM_deleteValue('steam')
  33.  
  34. if (auto_get && location.href.includes('torrents.php?id=')) {
  35. GM_deleteValue('steam')
  36. const steamLink = document.querySelector('a[title=Steam]')
  37. if (steamLink)
  38. GM_setValue('steam', /\d+/.exec(steamLink.href)[0])
  39. }
  40.  
  41. const langSelect = document.getElementById('language')
  42. if (location.href.includes('upload')) {
  43. ggn_upload()
  44. }
  45.  
  46. function steamButton() {
  47. const $btn = document.createElement('a')
  48. const text = 'Copy BBCode'
  49. $btn.classList.add('btnv6_blue_hoverfade', 'btn_small')
  50. const $text = $btn.appendChild(document.createElement('span'))
  51. $text.innerHTML = `${text}<img src="https://ptpimg.me/sx226x.png">`
  52. $btn.addEventListener('click', function () {
  53. GM_setClipboard(globals.parseSteamLanguage(null), 'text')
  54. $text.childNodes[0].nodeValue = 'copied'
  55. setTimeout(function () {
  56. $text.childNodes[0].nodeValue = text
  57. }, 3000)
  58. })
  59.  
  60. const $container = document.querySelector('table.game_language_options').closest('.block').querySelector('.block_title') || document.querySelector('#LanguagesHeader')
  61. $container.style = 'display: flex;justify-content: space-between;align-items: center;'
  62. $container.appendChild($btn)
  63. }
  64.  
  65. function ggn_upload() {
  66. let fetchInput = document.createElement('input')
  67. fetchInput.type = 'text'
  68. fetchInput.placeholder = "Steam Link or ID"
  69. langSelect.after(fetchInput)
  70. fetchInput.onblur = () => {
  71. getSteamLanguages(/\d+/.exec(fetchInput.value)).catch(() => {
  72. fetchInput.value = 'Failed to get languages'
  73. fetchInput.style.color = 'red'
  74. fetchInput.disabled = true
  75. })
  76. }
  77. if (auto_get) {
  78. setTimeout(() => { // to support Reuploader script so it won't add languages again
  79. if (!document.getElementById('release_desc').value) {
  80. const savedID = GM_getValue('steam', null)
  81. if (savedID) {
  82. fetchInput.value = savedID
  83. fetchInput.dispatchEvent(new Event('blur'))
  84. GM_deleteValue('steam')
  85. }
  86. }
  87. }, 500)
  88. }
  89. }
  90.  
  91. function getSteamLanguages(steamId) {
  92. return new Promise((resolve, reject) => {
  93. GM_xmlhttpRequest({
  94. url: "https://store.steampowered.com/api/appdetails?l=en&appids=" + steamId,
  95. method: 'GET',
  96. responseType: "json",
  97. onload: function (response) {
  98. if (response.status === 200 && response.response[steamId].success) {
  99. resolve(globals.parseSteamLanguage(response.response[steamId].data.supported_languages))
  100. } else reject()
  101. }
  102. })
  103. })
  104. }
  105.  
  106. globals.parseSteamLanguage = function (supported_languages) {
  107. const langCodes = new Map([
  108. ["Afrikaans", "af"],
  109. ["Albanian", "sq"],
  110. ["Amharic", "am"],
  111. ["Arabic", "ar"],
  112. ["Armenian", "hy"],
  113. ["Assamese", "as"],
  114. ["Azerbaijani", "az"],
  115. ["Bangla", "bn"],
  116. ["Basque", "eu"],
  117. ["Belarusian", "be"],
  118. ["Bulgarian", "bg"],
  119. ["Bosnian", "bs"],
  120. ["Simplified Chinese", "zh-cn"],
  121. ["Traditional Chinese", "zh-tw"],
  122. ["Catalan", "ca"],
  123. ["Croatian", "hr"],
  124. ["Czech", "cs"],
  125. ["Danish", "da"],
  126. ["Dutch", "nl"],
  127. ["English", "en"],
  128. ["Estonian", "et"],
  129. ["Filipino", "tl"],
  130. ["Farsi", "fa"],
  131. ["Finnish", "fi"],
  132. ["French", "fr"],
  133. ["German", "de"],
  134. ["Greek", "el"],
  135. ["Hebrew", "he"],
  136. ["Hausa", "ha"],
  137. ["Hindi", "hi"],
  138. ["Hungarian", "hu"],
  139. ["Icelandic", "is"],
  140. ["Igbo", "ig"],
  141. ["Indonesian", "id"],
  142. ["Irish", "ga"],
  143. ["Italian", "it"],
  144. ["Japanese", "ja"],
  145. ["Kannada", "kn"],
  146. ["Korean", "ko"],
  147. ["Kazakh", "kk"],
  148. ["Khmer", "km"],
  149. ["Kurdish", "ku"],
  150. ["Kinyarwanda", "rw"],
  151. ["Kyrgyz", "ky"],
  152. ["Latvian", "lv"],
  153. ["Lithuanian", "lt"],
  154. ["Luxembourgish", "lb"],
  155. ["Macedonian", "mk"],
  156. ["Malay", "ms"],
  157. ["Malayalam", "ml"],
  158. ["Maltese", "mt"],
  159. ["Mongolian", "mn"],
  160. ["Maori", "mi"],
  161. ["Nepali", "ne"],
  162. ["Odia", "or"],
  163. ["Norwegian", "no"],
  164. ["Persian", "fa"],
  165. ["Quechua", "qu"],
  166. ["Polish", "pl"],
  167. ["Portuguese - Brazil", "pt-br"],
  168. ["Portuguese", "pt"],
  169. ["Punjabi", "pa"],
  170. ["Scots", "gd"],
  171. ["Romanian", "ro"],
  172. ["Russian", "ru"],
  173. ["Serbian", "sr"],
  174. ["Slovak", "sk"],
  175. ["Slovenian", "sl"],
  176. ["Sorbian", "sb"],
  177. ["Sotho", "st"],
  178. ["Swahili", "sw"],
  179. ["Spanish - Spain", "es"],
  180. ["Spanish - Latin America", "es-la"],
  181. ["Swedish", "sv"],
  182. ["Thai", "th"],
  183. ["Tajik", "tg"],
  184. ["Tamil", "ta"],
  185. ["Tatar", "tt"],
  186. ["Telugu", "te"],
  187. ["Tsonga", "ts"],
  188. ["Tigrinya", "ti"],
  189. ["Tswana", "tn"],
  190. ["Turkmen", "tk"],
  191. ["Turkish", "tr"],
  192. ["Ukrainian", "ua"],
  193. ["Uyghur", "ug"],
  194. ["Urdu", "ur"],
  195. ["Uzbek", "uz"],
  196. ["Venda", "ve"],
  197. ["Vietnamese", "vi"],
  198. ["Welsh", "cy"],
  199. ["Wolof", "wo"],
  200. ["Xhosa", "xh"],
  201. ["Yoruba", "yo"],
  202. ["Yiddish", "ji"],
  203. ["Zulu", "zu"],
  204. ])
  205.  
  206. const languages = {Subtitles: []}
  207. if (supported_languages) {
  208. for (const str of supported_languages.replace(/<br>.*$/, '').split(', ')) {
  209. const lang = str.replace("<strong>*<\/strong>", '')
  210. if (str.includes('*')) {
  211. if (!languages['Full Audio']) {
  212. languages['Full Audio'] = []
  213. }
  214. languages['Full Audio'].push(lang)
  215. }
  216. languages['Subtitles'].push(lang)
  217. }
  218. } else {
  219. const table = document.querySelector('table.game_language_options')
  220. for (let r = 0; r < table.rows.length; r++) {
  221. for (let c = 0; c < table.rows[r].cells.length; c++) {
  222. const cell = table.rows[r].cells[c]
  223. debugger
  224. if (cell.textContent.trim() === '✔' || cell.firstElementChild?.nodeName === 'IMG') {
  225. let header = table.rows[0].cells[c].textContent.trim()
  226. if (!languages[header]) {
  227. languages[header] = []
  228. }
  229. languages[header].push(table.rows[r].cells[0].textContent.trim())
  230. }
  231. }
  232. }
  233. }
  234.  
  235. if (text_only) delete languages['Full Audio']
  236. let textLanguages = languages['Subtitles']?.length > 0 ? languages['Subtitles'] : languages['Interface']
  237. let audioLanguages = languages['Full Audio']
  238.  
  239. debugger
  240. const textMulti = textLanguages.length > 1 ? 's' : ''
  241. const audioMulti = audioLanguages && audioLanguages.length > 1 ? 's' : ''
  242.  
  243. let langSelectValue
  244. if (supported_languages) {
  245. const languageList = [
  246. 'English',
  247. 'German',
  248. 'French',
  249. 'Czech',
  250. 'Italian',
  251. 'Japanese',
  252. 'Korean',
  253. 'Polish',
  254. 'Portuguese',
  255. 'Russian',
  256. 'Spanish',
  257. ]
  258. const inLangList = !textMulti && languageList.some(lang => textLanguages[0].includes(lang))
  259. langSelectValue = textMulti ? 'Multi-Language' : inLangList ? textLanguages[0] : 'Other'
  260. }
  261.  
  262. if (use_language_codes) {
  263. textLanguages = textLanguages.map(l => {
  264. const code = langCodes.get(l)
  265. if (code) {
  266. return uppercase_language_codes ? code.toUpperCase() : code
  267. } else return l
  268. })
  269. if (audioLanguages) {
  270. audioLanguages = audioLanguages.map(l => {
  271. const code = langCodes.get(l)
  272. if (code) {
  273. return uppercase_language_codes ? code.toUpperCase() : code
  274. } else return l
  275. })
  276. }
  277. }
  278.  
  279. const joinedText = textLanguages.join(delimiter)
  280. const joinedAudio = audioLanguages && audioLanguages.join(delimiter)
  281.  
  282. function bold(str) {
  283. const lines = str.split("\n")
  284. const result = []
  285. for (const line of lines) {
  286. const [category, list] = line.split(": ")
  287. result.push(bold_list ? `${category}: [b]${list}[/b]` : `[b]${category}[/b]: ${list}`)
  288. }
  289. return result.join("\n")
  290. }
  291.  
  292. if (supported_languages) {
  293. let description
  294. if (!textMulti && (textLanguages[0].includes('Chinese') || textLanguages[0].includes('ZH'))) {
  295. langSelectValue = 'Chinese'
  296. if (audioLanguages && areSame(textLanguages, audioLanguages)) {
  297. description = bold(`Text and Audio Language${textMulti}: ${joinedText}`)
  298. } else {
  299. description = audioLanguages
  300. ? bold(`Text Language: ${joinedAudio}\nAudio Language${audioMulti}: ${joinedAudio}`)
  301. : bold(`Language: ${joinedText}`)
  302. }
  303. } else if (audioLanguages && areSame(textLanguages, audioLanguages)) {
  304. description = bold(`Text and Audio Language${textMulti}: ${joinedText}`)
  305. } else {
  306. const addText = textMulti ? `Languages: ${joinedText}` : '' // add nothing if there's only 1 language
  307. description = audioLanguages
  308. ? bold(`${addText ? 'Text ' + addText + '\n' : ''}Audio Language${audioMulti}: ${joinedAudio}`)
  309. : addText ? bold(addText) : ''
  310. }
  311.  
  312. langSelect.value = langSelectValue
  313. document.getElementById('release_desc').value += description
  314. return
  315. }
  316.  
  317. if (audioLanguages) {
  318. if (areSame(textLanguages, audioLanguages))
  319. return bold(`Text and Audio Language${textMulti}: ${joinedText}`)
  320. return bold(`Text Language${textMulti}: ${joinedText}\nAudio Language${audioMulti}: ${joinedAudio}`)
  321. }
  322. return bold(`Language${textMulti}: ${joinedText}`)
  323. }
  324.  
  325. function areSame(array1, array2) {
  326. return array1.length === array2.length && array1.sort().every((value, index) => value === array2.sort()[index])
  327. }