Skip to content

Instantly share code, notes, and snippets.

@deverton
Last active October 5, 2019 22:29
Show Gist options
  • Save deverton/e0a404a1c64bf9c24f8b207e458aa4da to your computer and use it in GitHub Desktop.
Save deverton/e0a404a1c64bf9c24f8b207e458aa4da to your computer and use it in GitHub Desktop.
Download All Humble Bundle Packages and Trove
#!/bin/bash
# TODO: Generate md5sums file to verify quicker
##
# Script to download everything you own from Humble Bundle.
# Writes to the current directory in folders named after the
# internal bundle name and product type. For example, Humble
# Bundle 1 product Aquaria would be under `hib1/aquarria`.
#
# Hard coded to skip other platforms than Windows but easy enough
# to change.
##
# Value of _simpleauth_sess cookie.
# Get this from your browser after logging in
# Remeber to include the " in the cookie value
AUTH=''
API_BASE="https://www.humblebundle.com/api/v1"
hb_get() {
curl $1 \
--header 'Accept: application/json' \
--cookie "_simpleauth_sess=${AUTH}" \
$2
}
download_link() {
hb_get "-s -X POST -d machine_name=$1 -d filename=$2" "${API_BASE}/user/download/sign" | jq -r '.signed_url'
}
rm md5sums
# Handle Trove
for page in {0..10}; do
echo -e "Processing trove page [${page}]:"
while read -r trove_json; do
TROVE_NAME=$(echo "${trove_json}" | jq -r '."human-name"')
TROVE_ITEM=$(echo "${trove_json}" | jq -r '.machine_name')
echo -e "\tProcessing trove item [${TROVE_NAME}]:"
# Windows only for now
TROVE_MACHINE_NAME=$(echo "${trove_json}" | jq -r '.downloads.windows.machine_name')
TROVE_FILE=$(echo "${trove_json}" | jq -r '.downloads.windows.url.web')
TROVE_SIZE=$(echo "${trove_json}" | jq -r '.downloads.windows.file_size')
TROVE_MD5=$(echo "${trove_json}" | jq -r '.downloads.windows.md5')
LOCAL_PATH="trove/${TROVE_ITEM}/windows/${TROVE_FILE}"
echo -n -e "\t\tFound download [${TROVE_MACHINE_NAME}] [windows] [${TROVE_FILE}] "
echo "${TROVE_MD5} *${LOCAL_PATH}" >> md5sums
DOWNLOAD_FILE_SIZE=$(stat --printf="%s" "${LOCAL_PATH}" 2> /dev/null || echo "0")
if [[ ! -f "${LOCAL_PATH}" ]] || [[ "${DOWNLOAD_FILE_SIZE}" != "${TROVE_SIZE}" ]]; then
echo "downloading "
TROVE_URL=$(download_link "${TROVE_MACHINE_NAME}" "${TROVE_FILE}")
hb_get "-# -o ${LOCAL_PATH} --create-dirs -C -" "${TROVE_URL}"
DOWNLOAD_FILE_MD5=$(md5sum "${LOCAL_PATH}" | cut -d ' ' -f 1)
if [[ "${DOWNLOAD_FILE_MD5}" == "${TROVE_MD5}" ]]; then
echo "matched MD5."
else
echo "ERROR MD5 does not match!"
fi
elif [[ "${DOWNLOAD_FILE_SIZE}" == "${TROVE_SIZE}" ]]; then
echo "matched expected file size."
else
echo "ERROR file size does not match!"
fi
done < <(hb_get -s "${API_BASE}/trove/chunk?index=${page}" | jq -rc '.[]')
done
## Handle Bundles
for gamekey in $(hb_get -s "${API_BASE}/user/order?ajax=true" | jq -r '.[].gamekey' | sort); do
BUNDLE_JSON=$(hb_get -s "${API_BASE}/order/${gamekey}?all_tpkds=true")
BUNDLE_NAME=$(echo "${BUNDLE_JSON}" | jq -r '.product.human_name')
BUNDLE_MACHINE_NAME=$(echo "${BUNDLE_JSON}" | jq -r '.product.machine_name')
echo "Processing bundle [${BUNDLE_NAME}]:"
for product in $(echo "${BUNDLE_JSON}" | jq -S -r '.subproducts[].machine_name' | sort); do
PRODUCT_JSON=$(echo "${BUNDLE_JSON}" | jq -r --arg machine_name "${product}" '.subproducts[] | select(.machine_name == $machine_name)')
PRODUCT_NAME=$(echo "${PRODUCT_JSON}" | jq -r '.human_name')
echo -e "\tProcessing product [${PRODUCT_NAME}]:"
for download in $(echo "${PRODUCT_JSON}" | jq -r '.downloads[].machine_name' | sort); do
DOWNLOAD_JSON=$(echo "${PRODUCT_JSON}" | jq -r --arg machine_name "${download}" '.downloads[] | select(.machine_name == $machine_name)')
DOWNLOAD_PLATFORM=$(echo "${DOWNLOAD_JSON}" | jq -r '.platform')
if [[ "android" == "${DOWNLOAD_PLATFORM}" ]]; then
continue
fi
if [[ "asmjs" == "${DOWNLOAD_PLATFORM}" ]]; then
continue
fi
if [[ "mac" == "${DOWNLOAD_PLATFORM}" ]]; then
continue
fi
if [[ "linux" == "${DOWNLOAD_PLATFORM}" ]]; then
continue
fi
DS=$(echo "${DOWNLOAD_JSON}" | jq -r '.download_struct | length')
for ((i=0;i<DS;i++)); do
DOWNLOAD_NAME=$(echo "${DOWNLOAD_JSON}" | jq -r --argjson index $i '.download_struct[$index].name')
DOWNLOAD_URL=$(echo "${DOWNLOAD_JSON}" | jq -r --argjson index $i '.download_struct[$index].url.web')
DOWNLOAD_SIZE=$(echo "${DOWNLOAD_JSON}" | jq -r --argjson index $i '.download_struct[$index].file_size')
DOWNLOAD_MD5=$(echo "${DOWNLOAD_JSON}" | jq -r --argjson index $i '.download_struct[$index].md5')
DOWNLOAD_FILE=$(echo "${DOWNLOAD_URL}" | cut -d '/' -f 4- | cut -d '?' -f 1)
LOCAL_PATH="${BUNDLE_MACHINE_NAME}/${product}/${DOWNLOAD_PLATFORM}/${DOWNLOAD_FILE}"
if [[ "null" == "${DOWNLOAD_SIZE}" ]]; then
continue
fi
echo -n -e "\t\tFound download [${DOWNLOAD_NAME}] [${DOWNLOAD_PLATFORM}] [${DOWNLOAD_FILE}] "
echo "${DOWNLOAD_MD5} *${LOCAL_PATH}" >> md5sums
DOWNLOAD_FILE_SIZE=$(stat --printf="%s" "${LOCAL_PATH}" 2> /dev/null || echo "0")
if [[ ! -f "${LOCAL_PATH}" ]] || [[ "${DOWNLOAD_FILE_SIZE}" != "${DOWNLOAD_SIZE}" ]]; then
echo "downloading "
hb_get "-# -o ${LOCAL_PATH} --create-dirs -C -" "${DOWNLOAD_URL}"
DOWNLOAD_FILE_MD5=$(md5sum "${LOCAL_PATH}" | cut -d ' ' -f 1)
if [[ "${DOWNLOAD_FILE_MD5}" == "${DOWNLOAD_MD5}" ]]; then
echo "matched MD5."
else
echo "ERROR MD5 does not match!"
fi
elif [[ "${DOWNLOAD_FILE_SIZE}" == "${DOWNLOAD_SIZE}" ]]; then
echo "matched expected file size."
else
echo "ERROR file size does not match!"
fi
done
done
done
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment