SteamDB Community Items Download

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

// ==UserScript==
// @name     				SteamDB Community Items Download
// @author   				dougwritescode
// @description     Creates download links to mass download community-item-related media from steamdb.info
// @version  				2
// @grant    				none
// @include  				https://steamdb.info/app/*
// @icon		 				https://steamdb.info/static/logos/vector_prefers_schema.svg
// @namespace       dougwritescode
// @license	 				MIT
// ==/UserScript==

// Find tab-content element
tab_content_elem = document.querySelector(".tab-content");

// Get the title for this repo
title_name = document.querySelector('h1[itemprop="name"]').innerText;

// Download function stolen from here: https://bobbyhadz.com/blog/javascript-download-image#how-to-download-images-using-javascript
// Creates an anchor element and duplicates the asset link therein, appends the element to the document body, assigning the download attribute.
// It then clicks the element and removes then removes it.
async function downloadAsset(
  imageSrc,
  nameOfDownload
) {
  const response = await fetch(imageSrc);
  const assetBlob = await response.blob();
  const href = URL.createObjectURL(assetBlob);

  const anchorElement = document.createElement('a');
  anchorElement.href = href;
  anchorElement.download = nameOfDownload;
  document.body.appendChild(anchorElement);
  anchorElement.click();

  document.body.removeChild(anchorElement);
  window.URL.revokeObjectURL(href);
}

// Generalized download button making
function make_button(
  titleString, 
  onClickFunc, 
  classStr = "btn btn-info", 
  styleStr = "margin-left: 15px; padding: 1px 5px;"
) {
  var new_button = document.createElement("input");
  new_button.type = "button";
  new_button.value = titleString;
  new_button.onclick = onClickFunc;
  new_button.setAttribute("class", classStr);
  new_button.setAttribute("style", styleStr);
  return new_button
}

// Function to add download button for badges
function add_download_badges_button(headerObj, itemContainerObj) {
  
  async function downloadBadges() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("img").src;
      const extension = img_src.split(".").pop();
      const badge_title = items[i].innerText
      const name = `${title_name} - badge ${parseInt(i) + 1} - ${badge_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }
  
  var dl_button = make_button("Download All", downloadBadges);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button); 
}

// Function to add download buttons for trading cards and card wallpapers
function add_download_trading_cards_button(headerObj, itemContainerObj) {
  
  async function downloadTradingCards() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("img").src;
      const extension = img_src.split(".").pop();
      const card_title = items[i].innerText
      const name = `${title_name} - card ${parseInt(i) + 1} - ${card_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }
  
  async function downloadTradingCardWallpapers() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("a").href;
      const extension = img_src.split(".").pop();
      const wall_title = items[i].innerText
      const name = `${title_name} - card wallpaper ${parseInt(i) + 1} - ${wall_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }
  
  var dl_button = make_button("Download Cards", downloadTradingCards);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button); 
  
  var dll_button = make_button("Download Wallpapers", downloadTradingCardWallpapers);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dll_button); 
}

// Function to add download button for profile backgrounds
function add_download_profile_backgrounds_button(headerObj, itemContainerObj) {

  async function downloadProfileBackgrounds() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector(".view-profile-background").href;
      const extension = img_src.split(".").pop();
      const background_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - profile background ${parseInt(i) + 1} - ${background_title}.${extension}`
      downloadAsset(img_src, name);
      const webm_link = items[i].querySelector('a[href$=".webm"]')
      if (webm_link != null) {
        const webm_src = webm_link.href;
        const webm_name = `${title_name} - animated profile background ${parseInt(i) + 1} - ${background_title}.webm,`
        downloadAsset(webm_src, webm_name);
      }
    }
  }
  
  var dl_button = make_button("Download All", downloadProfileBackgrounds);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button); 
}

// Function to add download buttons for emoticons
function add_download_emoticons_button(headerObj, itemContainerObj) {
  
  async function downloadSmallEmoticons() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelectorAll("img")[0].src;
      const extension = img_src.split(".").pop();
      const emoticon_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - small emoticon ${parseInt(i) + 1} - ${emoticon_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }
    
  async function downloadLargeEmoticons() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelectorAll("img")[1].src;
      const extension = img_src.split(".").pop();
      const emoticon_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - large emoticon ${parseInt(i) + 1} - ${emoticon_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }
  
  var dl_button = make_button("Download Small Emoticons", downloadSmallEmoticons);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
  
  var dll_button = make_button("Download Large Emoticons", downloadLargeEmoticons);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dll_button);
}

// Function to add download button for booster pack
function add_download_booster_pack_image_button(headerObj, itemContainerObj) {
  
  async function downloadBoosterPack() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("img").src;
      const extension = img_src.split(".").pop();
      const name = `${title_name} - Booster Pack.${extension}`
      downloadAsset(img_src, name);
    }
  }

  var dl_button = make_button("Download Pack", downloadBoosterPack);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
}

// Adds download button for chat stickers
function add_download_chat_stickers_button(headerObj, itemContainerObj) {
  
  async function downloadChatStickers() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("img").src;
      const extension = img_src.split(".").pop();
      const sticker_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - sticker ${parseInt(i) + 1} - ${sticker_title}.${extension}`
      downloadAsset(img_src, name);
    }
	}

  var dl_button = make_button("Download All", downloadChatStickers);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
}

// Adds download button for chat effects (So far, the only ones are from the Winter Sale Event 2019) 
function add_download_chat_effects_button(headerObj, itemContainerObj) {
    
  async function downloadChatEffects() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelectorAll("img")[1].src;
      const extension = img_src.split(".").pop();
      const effect_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - chat effect icon ${parseInt(i) + 1} - ${effect_title}.${extension}`
      downloadAsset(img_src, name);
    }
	}

  var dl_button = make_button("Download All", downloadChatEffects);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
}

// Adds download button for mini profile backgrounds
function add_download_mini_profile_backgrounds_button(headerObj, itemContainerObj) {
  
  async function downloadMiniProfileBackgrounds() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector('a[href$=".webm"]').href;
      const extension = img_src.split(".").pop();
      const mini_profile_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - mini profile background ${parseInt(i) + 1} - ${mini_profile_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }

  var dl_button = make_button("Download All", downloadMiniProfileBackgrounds);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
}

// Adds download button for avatar frames
function add_download_avatar_frames_button(headerObj, itemContainerObj) {

  async function downloadAvatarFrames() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("img").src;
      const extension = img_src.split(".").pop();
      const avatar_frame_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - avatar frame ${parseInt(i) + 1} - ${avatar_frame_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }

  var dl_button = make_button("Download All", downloadAvatarFrames);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
}

// Adds download button for animated avatars
function add_download_animated_avatars_button(headerObj, itemContainerObj) {

  async function downloadAnimatedAvatars() {
    let items = itemContainerObj.querySelectorAll(".community-item");
    for (let i in items) {
      const img_src = items[i].querySelector("img").src;
      const extension = img_src.split(".").pop();
      const animated_avatar_title = items[i].querySelector(".b").innerText;
      const name = `${title_name} - animated avatar ${parseInt(i) + 1} - ${animated_avatar_title}.${extension}`
      downloadAsset(img_src, name);
    }
  }

  var dl_button = make_button("Download All", downloadAnimatedAvatars);
  headerObj.querySelector(".panel-heading").children[0].appendChild(dl_button);
}

// Adds all download buttons for which there are headers
function add_buttons() {
  
  if (!document.querySelector(".community-items-header")) {
    return false;
  }
  
  // Locate all of the item header panel elements
  panel_headers = tab_content_elem.querySelectorAll(".community-items");
  
  if (panel_headers.length == 0) {
  	return false; 
  }
  
  for (let i in panel_headers) {
    var item_class_id = panel_headers[i].id;
    if (item_class_id == null) {
      continue;
    }
    var item_container = panel_headers[i].querySelector(".community-items-container");
    switch(item_class_id) {
      case "item-class-1": // Badges
        add_download_badges_button(panel_headers[i], item_container);
        break;
      case "item-class-2": // Trading cards
        add_download_trading_cards_button(panel_headers[i], item_container);
        break;
      case "item-class-3": // Profile backgrounds
        add_download_profile_backgrounds_button(panel_headers[i], item_container);
        break;
      case "item-class-4": // Emoticons
        add_download_emoticons_button(panel_headers[i], item_container);
        break;
      case "item-class-5": // Booster packs
        add_download_booster_pack_image_button(panel_headers[i], item_container);
        break;
      // item-class-6 is ?
      // item-class-7 is ?
      // item-class-8 is profile modifiers (collections of avatar + frame + profile background + mini profile background), nothing to download
      // item-class-9 is ?
      // item-class-10 is ?
      case "item-class-11": // Chat stickers
        add_download_chat_stickers_button(panel_headers[i], item_container);
        break;
      case "item-class-12": // Chat effects
        add_download_chat_effects_button(panel_headers[i], item_container);
        break;
      case "item-class-13": // Mini profile backgrounds 
        add_download_mini_profile_backgrounds_button(panel_headers[i], item_container);
        break;
      case "item-class-14": // Avatar frames
        add_download_avatar_frames_button(panel_headers[i], item_container);
        break;
      case "item-class-15": // Animated avatars
        add_download_animated_avatars_button(panel_headers[i], item_container);
        break;
      // item-class-16 is Steam Deck keyboards (nothing to download)
      // item-class-17 is Steam Deck / Big Picture startup movies (direct links not provided on steamdb as of Feb 9, 2024)
      default:
        break;
    }
  } 
  return true;
}

// Event listener for vanilla javascript to trigger adding buttons once the DOM has loaded
document.addEventListener("DOMContentLoaded", () => {
  add_buttons()
});

// Mutation observer code to trigger adding buttons in the event of moving to the community items tab from another repo page
const targetNode = tab_content_elem;
const config = { childList: true };
const callback = (mutationList, observer) => {
  for (const mutation of mutationList) {
    let added = add_buttons();
    if (added == true) {
      observer.disconnect();
    }
  }
};
const observer = new MutationObserver(callback);
observer.observe(targetNode, config);