SWDD - Steam Workshop Description Downloader

Adds buttons to download Steam Workshop descriptions in .MD and .BBCode format.

2024/01/12のページです。最新版はこちら。

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
  1. // ==UserScript==
  2.  
  3. // @name SWDD - Steam Workshop Description Downloader
  4. // @namespace https://criskkky.carrd.co/
  5. // @version 1.0.2
  6. // @description Adds buttons to download Steam Workshop descriptions in .MD and .BBCode format.
  7. // @description:en Adds buttons to download Steam Workshop descriptions in .MD and .BBCode format.
  8. // @description:es Añade botones para descargar descripciones de la Workshop de Steam en formato .MD y .BBCode.
  9. // @description:pt Adiciona botões para baixar descrições da Workshop do Steam em formato .MD e .BBCode.
  10. // @description:fr Ajoute des boutons pour télécharger les descriptions de la Workshop Steam au format .MD et .BBCode.
  11. // @description:it Aggiunge pulsanti per scaricare le descrizioni della Workshop di Steam in formato .MD e .BBCode.
  12. // @description:uk Додає кнопки для завантаження описів Workshop Steam у форматі .MD та .BBCode.
  13. // @description:ru Добавляет кнопки для загрузки описаний Workshop Steam в формате .MD и .BBCode.
  14.  
  15. // @author https://criskkky.carrd.co/
  16. // @supportURL https://github.com/criskkky/SWDD/issues
  17. // @homepageURL https://github.com/criskkky/SWDD/
  18. // @icon https://raw.githubusercontent.com/criskkky/criskkky.github.io/main/media/icons/swdd.png
  19. // @copyright https://github.com/criskkky/SWDD/tree/stable?tab=readme-ov-file#license
  20. // @license https://github.com/criskkky/SWDD/tree/stable?tab=readme-ov-file#license
  21.  
  22. // @grant none
  23. // @match https://steamcommunity.com/sharedfiles/filedetails/*
  24. // ==/UserScript==
  25.  
  26. /*
  27. I RECOMMEND TAKE A LOOK TO SUPPORTED SYNTAX CONVERSIONS HERE:
  28. https://github.com/criskkky/SWDD?tab=readme-ov-file#supported-conversions
  29.  
  30. ANYWAYS, YOU CAN HELP ME TO IMPROVE THIS SCRIPT
  31. BY DOING A PULL REQUEST OR OPENING AN ISSUE ON GITHUB :D
  32. https://github.com/criskkky/SWDD
  33.  
  34. AVOID USING OFUSCATED CODE OR LIBS, PLEASE,
  35. OR YOUR PULL REQUEST WILL BE REJECTED. THANKS!
  36. */
  37.  
  38. // Function to download content as a file
  39. function downloadContent(content, fileName) {
  40. var blob = new Blob([content], { type: 'text/plain' });
  41. var link = document.createElement('a');
  42. link.href = window.URL.createObjectURL(blob);
  43. link.download = fileName;
  44. link.click();
  45. }
  46.  
  47. function getDescription() {
  48. var descriptionElement = document.querySelector('.workshopItemDescription');
  49. if (descriptionElement) {
  50. // Get the HTML content of the description
  51. var descriptionHTML = descriptionElement.innerHTML;
  52.  
  53. return descriptionHTML;
  54. }
  55. return null;
  56. }
  57.  
  58. function getHTMLtoBBC(descriptionHTML) {
  59. // Custom replacements for Steam HTML to BBCode conversion
  60. var bbReplacements = {
  61. // Essential
  62. '<br>': '\n',
  63. '<span class="bb_link_host">([\\s\\S]*?)<\/span>': '',
  64. '<span>([\\s\\S]*?)<\/span>': '$1',
  65. // Headers
  66. '<div class="bb_h1">([^<]+)<\/div>': '[h1]$1[/h1]\n',
  67. '<div class="bb_h2">([^<]+)<\/div>': '[h2]$1[/h2]\n',
  68. '<div class="bb_h3">([^<]+)<\/div>': '[h3]$1[/h3]\n',
  69. // Font styling
  70. '<b>([\\s\\S]*?)<\/b>': '[b]$1[/b]',
  71. '<u>([\\s\\S]*?)<\/u>': '[u]$1[/u]',
  72. '<i>([\\s\\S]*?)<\/i>': '[i]$1[/i]',
  73. // Font formatting
  74. '<span class="bb_strike">([\\s\\S]*?)<\/span>': '[strike]$1[/strike]',
  75. '<span class="bb_spoiler">([\\s\\S]*?)<\/span>': '[spoiler]$1[/spoiler]',
  76. '<a[^>]*class="bb_link"[^>]*href="([^"]+)"(?:[^>]*target="([^"]+)")?(?:[^>]*rel="([^"]+)")?[^>]*>([\\s\\S]*?)<\/a>': '[url=$1]$4[/url]',
  77. // Lists
  78. '<ul class="bb_ul">([\\s\\S]*?)<\/ul>': '\n[list]\n$1\n[/list]',
  79. '<li>([\\s\\S]*?)<\/li>': '[*]$1',
  80. '<ol>([\\s\\S]*?)<\/ol>': '\n[olist]\n$1\n[/olist]',
  81. // Font formatting
  82. '<div class="bb_code">([\\s\\S]*?)<\/div>': '\n[code]\n$1[/code]\n',
  83. // TODO: Fix noparse. It's not working properly. Do PR if you can fix it.
  84. // Tables
  85. // TODO: Fix bb_table. It's not working properly. Do PR if you can fix it.
  86. '<div class="bb_table_tr">([\\s\\S]*?)<\/div>': '\n[tr]\n$1\n[/tr]\n',
  87. '<div class="bb_table_th">([\\s\\S]*?)<\/div>': '[th]$1[/th]',
  88. '<div class="bb_table_td">([\\s\\S]*?)<\/div>': '[td]$1[/td]',
  89. // Images
  90. '<img src="([^"]+)"[^>]*>': '[img]$1[/img]',
  91. // Others
  92. '<hr>': '[hr]',
  93. '<blockquote class="bb_blockquote">([\\s\\S]*?)</blockquote>' : '[quote]$1[/quote]',
  94. };
  95.  
  96. // Apply custom replacements
  97. for (var pattern in bbReplacements) {
  98. var regex = new RegExp(pattern, 'gi');
  99. descriptionHTML = descriptionHTML.replace(regex, bbReplacements[pattern]);
  100. }
  101.  
  102. // Clear unsupported tags
  103. descriptionHTML = descriptionHTML.replace(/<(?!\/?(h1|h2|h3|b|u|i|strike|spoiler|ul|li|ol|code|tr|th|td|img|hr|blockquote|\/blockquote))[^>]+>/g, '');
  104.  
  105. var bbcodeContent = descriptionHTML;
  106.  
  107. return bbcodeContent;
  108. }
  109.  
  110. function getHTMLtoMD(descriptionHTML) {
  111. // Custom replacements for Steam HTML to Markdown conversion
  112. var mdReplacements = {
  113. // Essential
  114. '<br>': '\n',
  115. '<span class="bb_link_host">([\\s\\S]*?)<\/span>': '',
  116. '<span>([\\s\\S]*?)<\/span>': '$1',
  117. // Headers
  118. '<div class="bb_h1">([\\s\\S]*?)<\/div>': '# $1\n',
  119. '<div class="bb_h2">([\\s\\S]*?)<\/div>': '## $1\n',
  120. '<div class="bb_h3">([\\s\\S]*?)<\/div>': '### $1\n',
  121. // Font styling
  122. '<b>([\\s\\S]*?)<\/b>': '**$1**',
  123. '<u>([\\s\\S]*?)<\/u>': '__$1__',
  124. '<i>([\\s\\S]*?)<\/i>': '*$1*',
  125. // Font formatting
  126. '<span class="bb_strike">([\\s\\S]*?)<\/span>': '~~$1~~',
  127. '<span class="bb_spoiler">([\\s\\S]*?)<\/span>': '<details><summary>Spoiler</summary>$1</details>',
  128. '<a[^>]*class="bb_link"[^>]*href="([^"]+)"(?:[^>]*target="([^"]+)")?(?:[^>]*rel="([^"]+)")?[^>]*>([\\s\\S]*?)<\/a>': '[$4]($1)',
  129. // Lists
  130. '<li>([\\s\\S]*?)<\/li>': '* $1',
  131. '<ol>([\\s\\S]*?)<\/ol>': (match, p1, offset, string) => {
  132. const lines = p1.trim().split('\n');
  133. let currentIndex = 1;
  134. const formattedLines = lines.map((line, index) => {
  135. // Check if the current line starts a new ordered list
  136. const isNewList = line.trim().startsWith('<li>');
  137. // If it's a new list, reset currentIndex to 1
  138. if (isNewList) {
  139. currentIndex = 1;
  140. }
  141. // Replace asterisks (*) or hyphens (-) with incremental numbers
  142. return line.replace(/^\s*[\*\-]/, () => {
  143. const updatedNumber = currentIndex++;
  144. return `${updatedNumber}.`;
  145. });
  146. });
  147. return `<ol>\n${formattedLines.join('\n')}\n</ol>`;
  148. },
  149. // Font formatting
  150. '<div class="bb_code">([\\s\\S]*?)<\/div>': '\n```\n$1\n```\n',
  151. // TODO: Fix noparse. It's not working properly. Do PR if you can fix it.
  152. // Tables
  153. // TODO: Fix bb_table. It's not working properly. Do PR if you can fix it.
  154. // TODO: Fix bb_table_tr. It's not working properly. Do PR if you can fix it.
  155. // TODO: Fix bb_table_th. It's not working properly. Do PR if you can fix it.
  156. // TODO: Fix bb_table_td. It's not working properly. Do PR if you can fix it.
  157. // Images
  158. '<img src="([^"]+)"[^>]*>': '![image]($1)',
  159. // Others
  160. '<hr>': '---',
  161. '<blockquote class="bb_blockquote">([\\s\\S]*?)</blockquote>' : '> $1',
  162. };
  163.  
  164. // Apply custom replacements
  165. for (var pattern in mdReplacements) {
  166. var regex = new RegExp(pattern, 'gi');
  167. descriptionHTML = descriptionHTML.replace(regex, mdReplacements[pattern]);
  168. }
  169.  
  170. // Clear unsupported tags except for <details><summary>Spoiler</summary>$1</details>
  171. descriptionHTML = descriptionHTML.replace(/<(?!details><summary>Spoiler<\/summary>\$1<\/details>)[^>]+>/g, '');
  172.  
  173. var markdownContent = descriptionHTML;
  174.  
  175. return markdownContent;
  176. }
  177.  
  178. function insertButton(downloadButton) {
  179. var fixedMargin = document.querySelector('.game_area_purchase_margin');
  180. if (fixedMargin) {
  181. // Better alignment ...
  182. fixedMargin.style.marginBottom = 'auto';
  183. // Find the element after which the button should be inserted
  184. var targetElement = document.querySelector('.workshopItemDescription');
  185. if (targetElement) {
  186. // Insert the button after the target element
  187. targetElement.parentNode.insertBefore(downloadButton, targetElement.nextSibling);
  188. }
  189. }
  190. }
  191. // Create go to repo button
  192. function createGoToRepoButton() {
  193. var goToRepoButton = document.createElement('a');
  194. goToRepoButton.innerHTML = '<img src="https://raw.githubusercontent.com/criskkky/criskkky.github.io/main/media/icons/github_line.png" style="vertical-align: middle; margin-right: 5px; margin-left: -4px; max-width: 20px; max-height: 20px;">Repository';
  195. goToRepoButton.classList.add('btn_darkblue_white_innerfade', 'btn_border_2px', 'btn_medium');
  196. goToRepoButton.style.marginBottom = '5px';
  197. goToRepoButton.style.marginRight = '5px';
  198. goToRepoButton.style.padding = '5px 10px';
  199. goToRepoButton.style.height = '21px';
  200. goToRepoButton.style.fontSize = '14px';
  201. goToRepoButton.href = 'https://github.com/criskkky/SWDD';
  202. goToRepoButton.target = '_blank';
  203.  
  204. insertButton(goToRepoButton);
  205. }
  206.  
  207. // Create the download button for Markdown
  208. function createDownloadButtonMD() {
  209. var downloadButton = document.createElement('button');
  210. downloadButton.innerHTML = '<img src="https://raw.githubusercontent.com/criskkky/criskkky.github.io/main/media/icons/cloud-download-white.svg" style="vertical-align: middle; margin-right: 5px; margin-left: -6px; max-width: 20px; max-height: 20px;">Download .MD';
  211. downloadButton.classList.add('btn_green_white_innerfade', 'btn_border_2px', 'btn_medium');
  212. downloadButton.style.marginBottom = '5px';
  213. downloadButton.style.marginRight = '5px';
  214. downloadButton.style.padding = '5px 10px';
  215. downloadButton.style.height = '34.43px';
  216. downloadButton.style.fontSize = '14px';
  217. downloadButton.addEventListener('click', function () {
  218. var markdownContent = getHTMLtoMD(getDescription());
  219. if (markdownContent) {
  220. downloadContent(markdownContent, 'WorkshopDownload.md');
  221. } else {
  222. alert('No content found in Markdown format.');
  223. }
  224. });
  225.  
  226. insertButton(downloadButton);
  227. }
  228.  
  229. // Create the download button for BBCode
  230. function createDownloadButtonBBC() {
  231. var downloadButton = document.createElement('button');
  232. downloadButton.innerHTML = '<img src="https://raw.githubusercontent.com/criskkky/criskkky.github.io/main/media/icons/cloud-download-white.svg" style="vertical-align: middle; margin-right: 5px; margin-left: -6px; max-width: 20px; max-height: 20px;">Download .BBCode';
  233. downloadButton.classList.add('btn_green_white_innerfade', 'btn_border_2px', 'btn_medium');
  234. downloadButton.style.marginBottom = '5px';
  235. downloadButton.style.marginRight = '5px';
  236. downloadButton.style.padding = '5px 10px';
  237. downloadButton.style.height = '34.43px';
  238. downloadButton.style.fontSize = '14px';
  239. downloadButton.addEventListener('click', function () {
  240. var bbcodeContent = getHTMLtoBBC(getDescription());
  241. if (bbcodeContent) {
  242. downloadContent(bbcodeContent, 'WorkshopDownload.bbcode');
  243. } else {
  244. alert('No content found in BBCode format.');
  245. }
  246. });
  247.  
  248. insertButton(downloadButton);
  249. }
  250.  
  251. // Execute the functions when the page loads
  252. window.addEventListener('load', function () {
  253. createGoToRepoButton();
  254. createDownloadButtonMD();
  255. createDownloadButtonBBC();
  256. });