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