SteamDB Community Items Download

Creates download links to mass download community-item-related media from steamdb.info

  1. // ==UserScript==
  2. // @name SteamDB Community Items Download
  3. // @author dougwritescode
  4. // @description Creates download links to mass download community-item-related media from steamdb.info
  5. // @version 2
  6. // @grant none
  7. // @include https://steamdb.info/app/*
  8. // @icon https://steamdb.info/static/logos/vector_prefers_schema.svg
  9. // @namespace dougwritescode
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. // Find tab-content element
  14. tab_content_elem = document.querySelector(".tab-content");
  15.  
  16. // Get the title for this repo
  17. title_name = document.querySelector('h1[itemprop="name"]').innerText;
  18.  
  19. // Download function stolen from here: https://bobbyhadz.com/blog/javascript-download-image#how-to-download-images-using-javascript
  20. // Creates an anchor element and duplicates the asset link therein, appends the element to the document body, assigning the download attribute.
  21. // It then clicks the element and removes then removes it.
  22. async function downloadAsset(
  23. imageSrc,
  24. nameOfDownload
  25. ) {
  26. const response = await fetch(imageSrc);
  27. const assetBlob = await response.blob();
  28. const href = URL.createObjectURL(assetBlob);
  29.  
  30. const anchorElement = document.createElement('a');
  31. anchorElement.href = href;
  32. anchorElement.download = nameOfDownload;
  33. document.body.appendChild(anchorElement);
  34. anchorElement.click();
  35.  
  36. document.body.removeChild(anchorElement);
  37. window.URL.revokeObjectURL(href);
  38. }
  39.  
  40. // Generalized download button making
  41. function make_button(
  42. titleString,
  43. onClickFunc,
  44. classStr = "btn btn-info",
  45. styleStr = "margin-left: 15px; padding: 1px 5px;"
  46. ) {
  47. var new_button = document.createElement("input");
  48. new_button.type = "button";
  49. new_button.value = titleString;
  50. new_button.onclick = onClickFunc;
  51. new_button.setAttribute("class", classStr);
  52. new_button.setAttribute("style", styleStr);
  53. return new_button
  54. }
  55.  
  56. // Function to add download button for badges
  57. function add_download_badges_button(headerObj, itemContainerObj) {
  58. async function downloadBadges() {
  59. let items = itemContainerObj.querySelectorAll(".community-item");
  60. for (let i in items) {
  61. const img_src = items[i].querySelector("img").src;
  62. const extension = img_src.split(".").pop();
  63. const badge_title = items[i].innerText
  64. const name = `${title_name} - badge ${parseInt(i) + 1} - ${badge_title}.${extension}`
  65. downloadAsset(img_src, name);
  66. }
  67. }
  68. var dl_button = make_button("Download All", downloadBadges);
  69. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  70. }
  71.  
  72. // Function to add download buttons for trading cards and card wallpapers
  73. function add_download_trading_cards_button(headerObj, itemContainerObj) {
  74. async function downloadTradingCards() {
  75. let items = itemContainerObj.querySelectorAll(".community-item");
  76. for (let i in items) {
  77. const img_src = items[i].querySelector("img").src;
  78. const extension = img_src.split(".").pop();
  79. const card_title = items[i].innerText
  80. const name = `${title_name} - card ${parseInt(i) + 1} - ${card_title}.${extension}`
  81. downloadAsset(img_src, name);
  82. }
  83. }
  84. async function downloadTradingCardWallpapers() {
  85. let items = itemContainerObj.querySelectorAll(".community-item");
  86. for (let i in items) {
  87. const img_src = items[i].querySelector("a").href;
  88. const extension = img_src.split(".").pop();
  89. const wall_title = items[i].innerText
  90. const name = `${title_name} - card wallpaper ${parseInt(i) + 1} - ${wall_title}.${extension}`
  91. downloadAsset(img_src, name);
  92. }
  93. }
  94. var dl_button = make_button("Download Cards", downloadTradingCards);
  95. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  96. var dll_button = make_button("Download Wallpapers", downloadTradingCardWallpapers);
  97. headerObj.querySelector(".panel-heading").children[0].appendChild(dll_button);
  98. }
  99.  
  100. // Function to add download button for profile backgrounds
  101. function add_download_profile_backgrounds_button(headerObj, itemContainerObj) {
  102.  
  103. async function downloadProfileBackgrounds() {
  104. let items = itemContainerObj.querySelectorAll(".community-item");
  105. for (let i in items) {
  106. const img_src = items[i].querySelector(".view-profile-background").href;
  107. const extension = img_src.split(".").pop();
  108. const background_title = items[i].querySelector(".b").innerText;
  109. const name = `${title_name} - profile background ${parseInt(i) + 1} - ${background_title}.${extension}`
  110. downloadAsset(img_src, name);
  111. const webm_link = items[i].querySelector('a[href$=".webm"]')
  112. if (webm_link != null) {
  113. const webm_src = webm_link.href;
  114. const webm_name = `${title_name} - animated profile background ${parseInt(i) + 1} - ${background_title}.webm,`
  115. downloadAsset(webm_src, webm_name);
  116. }
  117. }
  118. }
  119. var dl_button = make_button("Download All", downloadProfileBackgrounds);
  120. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  121. }
  122.  
  123. // Function to add download buttons for emoticons
  124. function add_download_emoticons_button(headerObj, itemContainerObj) {
  125. async function downloadSmallEmoticons() {
  126. let items = itemContainerObj.querySelectorAll(".community-item");
  127. for (let i in items) {
  128. const img_src = items[i].querySelectorAll("img")[0].src;
  129. const extension = img_src.split(".").pop();
  130. const emoticon_title = items[i].querySelector(".b").innerText;
  131. const name = `${title_name} - small emoticon ${parseInt(i) + 1} - ${emoticon_title}.${extension}`
  132. downloadAsset(img_src, name);
  133. }
  134. }
  135. async function downloadLargeEmoticons() {
  136. let items = itemContainerObj.querySelectorAll(".community-item");
  137. for (let i in items) {
  138. const img_src = items[i].querySelectorAll("img")[1].src;
  139. const extension = img_src.split(".").pop();
  140. const emoticon_title = items[i].querySelector(".b").innerText;
  141. const name = `${title_name} - large emoticon ${parseInt(i) + 1} - ${emoticon_title}.${extension}`
  142. downloadAsset(img_src, name);
  143. }
  144. }
  145. var dl_button = make_button("Download Small Emoticons", downloadSmallEmoticons);
  146. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  147. var dll_button = make_button("Download Large Emoticons", downloadLargeEmoticons);
  148. headerObj.querySelector(".panel-heading").children[0].appendChild(dll_button);
  149. }
  150.  
  151. // Function to add download button for booster pack
  152. function add_download_booster_pack_image_button(headerObj, itemContainerObj) {
  153. async function downloadBoosterPack() {
  154. let items = itemContainerObj.querySelectorAll(".community-item");
  155. for (let i in items) {
  156. const img_src = items[i].querySelector("img").src;
  157. const extension = img_src.split(".").pop();
  158. const name = `${title_name} - Booster Pack.${extension}`
  159. downloadAsset(img_src, name);
  160. }
  161. }
  162.  
  163. var dl_button = make_button("Download Pack", downloadBoosterPack);
  164. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  165. }
  166.  
  167. // Adds download button for chat stickers
  168. function add_download_chat_stickers_button(headerObj, itemContainerObj) {
  169. async function downloadChatStickers() {
  170. let items = itemContainerObj.querySelectorAll(".community-item");
  171. for (let i in items) {
  172. const img_src = items[i].querySelector("img").src;
  173. const extension = img_src.split(".").pop();
  174. const sticker_title = items[i].querySelector(".b").innerText;
  175. const name = `${title_name} - sticker ${parseInt(i) + 1} - ${sticker_title}.${extension}`
  176. downloadAsset(img_src, name);
  177. }
  178. }
  179.  
  180. var dl_button = make_button("Download All", downloadChatStickers);
  181. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  182. }
  183.  
  184. // Adds download button for chat effects (So far, the only ones are from the Winter Sale Event 2019)
  185. function add_download_chat_effects_button(headerObj, itemContainerObj) {
  186. async function downloadChatEffects() {
  187. let items = itemContainerObj.querySelectorAll(".community-item");
  188. for (let i in items) {
  189. const img_src = items[i].querySelectorAll("img")[1].src;
  190. const extension = img_src.split(".").pop();
  191. const effect_title = items[i].querySelector(".b").innerText;
  192. const name = `${title_name} - chat effect icon ${parseInt(i) + 1} - ${effect_title}.${extension}`
  193. downloadAsset(img_src, name);
  194. }
  195. }
  196.  
  197. var dl_button = make_button("Download All", downloadChatEffects);
  198. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  199. }
  200.  
  201. // Adds download button for mini profile backgrounds
  202. function add_download_mini_profile_backgrounds_button(headerObj, itemContainerObj) {
  203. async function downloadMiniProfileBackgrounds() {
  204. let items = itemContainerObj.querySelectorAll(".community-item");
  205. for (let i in items) {
  206. const img_src = items[i].querySelector('a[href$=".webm"]').href;
  207. const extension = img_src.split(".").pop();
  208. const mini_profile_title = items[i].querySelector(".b").innerText;
  209. const name = `${title_name} - mini profile background ${parseInt(i) + 1} - ${mini_profile_title}.${extension}`
  210. downloadAsset(img_src, name);
  211. }
  212. }
  213.  
  214. var dl_button = make_button("Download All", downloadMiniProfileBackgrounds);
  215. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  216. }
  217.  
  218. // Adds download button for avatar frames
  219. function add_download_avatar_frames_button(headerObj, itemContainerObj) {
  220.  
  221. async function downloadAvatarFrames() {
  222. let items = itemContainerObj.querySelectorAll(".community-item");
  223. for (let i in items) {
  224. const img_src = items[i].querySelector("img").src;
  225. const extension = img_src.split(".").pop();
  226. const avatar_frame_title = items[i].querySelector(".b").innerText;
  227. const name = `${title_name} - avatar frame ${parseInt(i) + 1} - ${avatar_frame_title}.${extension}`
  228. downloadAsset(img_src, name);
  229. }
  230. }
  231.  
  232. var dl_button = make_button("Download All", downloadAvatarFrames);
  233. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  234. }
  235.  
  236. // Adds download button for animated avatars
  237. function add_download_animated_avatars_button(headerObj, itemContainerObj) {
  238.  
  239. async function downloadAnimatedAvatars() {
  240. let items = itemContainerObj.querySelectorAll(".community-item");
  241. for (let i in items) {
  242. const img_src = items[i].querySelector("img").src;
  243. const extension = img_src.split(".").pop();
  244. const animated_avatar_title = items[i].querySelector(".b").innerText;
  245. const name = `${title_name} - animated avatar ${parseInt(i) + 1} - ${animated_avatar_title}.${extension}`
  246. downloadAsset(img_src, name);
  247. }
  248. }
  249.  
  250. var dl_button = make_button("Download All", downloadAnimatedAvatars);
  251. headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  252. }
  253.  
  254. // Adds all download buttons for which there are headers
  255. function add_buttons() {
  256. if (!document.querySelector(".community-items-header")) {
  257. return false;
  258. }
  259. // Locate all of the item header panel elements
  260. panel_headers = tab_content_elem.querySelectorAll(".community-items");
  261. if (panel_headers.length == 0) {
  262. return false;
  263. }
  264. for (let i in panel_headers) {
  265. var item_class_id = panel_headers[i].id;
  266. if (item_class_id == null) {
  267. continue;
  268. }
  269. var item_container = panel_headers[i].querySelector(".community-items-container");
  270. switch(item_class_id) {
  271. case "item-class-1": // Badges
  272. add_download_badges_button(panel_headers[i], item_container);
  273. break;
  274. case "item-class-2": // Trading cards
  275. add_download_trading_cards_button(panel_headers[i], item_container);
  276. break;
  277. case "item-class-3": // Profile backgrounds
  278. add_download_profile_backgrounds_button(panel_headers[i], item_container);
  279. break;
  280. case "item-class-4": // Emoticons
  281. add_download_emoticons_button(panel_headers[i], item_container);
  282. break;
  283. case "item-class-5": // Booster packs
  284. add_download_booster_pack_image_button(panel_headers[i], item_container);
  285. break;
  286. // item-class-6 is ?
  287. // item-class-7 is ?
  288. // item-class-8 is profile modifiers (collections of avatar + frame + profile background + mini profile background), nothing to download
  289. // item-class-9 is ?
  290. // item-class-10 is ?
  291. case "item-class-11": // Chat stickers
  292. add_download_chat_stickers_button(panel_headers[i], item_container);
  293. break;
  294. case "item-class-12": // Chat effects
  295. add_download_chat_effects_button(panel_headers[i], item_container);
  296. break;
  297. case "item-class-13": // Mini profile backgrounds
  298. add_download_mini_profile_backgrounds_button(panel_headers[i], item_container);
  299. break;
  300. case "item-class-14": // Avatar frames
  301. add_download_avatar_frames_button(panel_headers[i], item_container);
  302. break;
  303. case "item-class-15": // Animated avatars
  304. add_download_animated_avatars_button(panel_headers[i], item_container);
  305. break;
  306. // item-class-16 is Steam Deck keyboards (nothing to download)
  307. // item-class-17 is Steam Deck / Big Picture startup movies (direct links not provided on steamdb as of Feb 9, 2024)
  308. default:
  309. break;
  310. }
  311. }
  312. return true;
  313. }
  314.  
  315. // Event listener for vanilla javascript to trigger adding buttons once the DOM has loaded
  316. document.addEventListener("DOMContentLoaded", () => {
  317. add_buttons()
  318. });
  319.  
  320. // Mutation observer code to trigger adding buttons in the event of moving to the community items tab from another repo page
  321. const targetNode = tab_content_elem;
  322. const config = { childList: true };
  323. const callback = (mutationList, observer) => {
  324. for (const mutation of mutationList) {
  325. let added = add_buttons();
  326. if (added == true) {
  327. observer.disconnect();
  328. }
  329. }
  330. };
  331. const observer = new MutationObserver(callback);
  332. observer.observe(targetNode, config);
  333.