#!/usr/bin/env bash set -e VERSION=0.5 LOGIN_PAGE=https://www.humblebundle.com/login HOME_PAGE=https://www.humblebundle.com/home COOKIE_JAR= FILE= DESTINATION= DOWNLOAD=1 USERNAME= PASSWORD= KEYS=() STORAGE= login() { if [ -z "$PASSWORD" ]; then read -rsp 'Enter Humble account password: ' PASSWORD echo fi printf '%s' "$PASSWORD" | \ curl -s --cookie-jar "$COOKIE_JAR" \ --data-urlencode "username=$USERNAME" --data-urlencode password@- "$LOGIN_PAGE" } discover_url() { local LISTING_PAGE="$1" curl -s --cookie "$COOKIE_JAR" "$LISTING_PAGE" | grep "/$FILE?" | grep 'data-web=' | \ sed -e "s/.* data-web='\([^']*\)'.*/\1/" | head -n 1 } usage() { cat < Directory for searching and saving files. The normal save location is still used, but will be a symbolic link -h This help -k Search key's files. Use multiple times for multiple keys -o Name to use when saving file -p Use pass to login. If specified multiple times, the last is used -P The name of the password available via gnome-keyring-query -s Print URL to stdout instead of downloading. Incompatible with -d -u Use user to login. Search account's files. If specified multiple times, the last is used If you specify -u, then all of that account's bundles are searched. If a key is associated with a HIB account then you must use -u/-p, since that key only works when logged into that account. It is not helpful to specify -k for bundles associated with an account. EOF } handle_download() { local SAVE_FILE="$1" local LISTING_PAGES=() COOKIE_JAR=$(mktemp) if [ -n "$USERNAME" ]; then login LISTING_PAGES+=("$HOME_PAGE") fi for KEY in "${KEYS[@]}"; do LISTING_PAGES+=("https://www.humblebundle.com/downloads?key=$KEY") done if [ -z "$LISTING_PAGES" ]; then echo "You must specify at least one of -u and -k" >&2 exit 1 fi local URL for LISTING_PAGE in "${LISTING_PAGES[@]}"; do URL=$(discover_url "$LISTING_PAGE") if [ -n "$URL" ]; then break; fi done # Cookie no longer necessary. rm "$COOKIE_JAR" if [ ! -n "$URL" ]; then echo "Could not find URL for file: $FILE" >&2 exit 2 fi if [ $DOWNLOAD -eq 0 ]; then echo "$URL" else curl -C - --retry 3 --retry-delay 3 -o "$SAVE_FILE" "$URL" fi } main() { if [ $# -eq 0 ]; then usage exit 1 fi while getopts "hd:k:o:p:P:su:" opt; do case $opt in \?) exit 1 ;; h) usage exit 1 ;; d) STORAGE="$OPTARG" ;; k) KEYS+=("$OPTARG") ;; o) DESTINATION="$OPTARG" ;; p) PASSWORD="$OPTARG" ;; P) PASSWORD="$(gnome-keyring-query get "$OPTARG")" ;; s) DOWNLOAD=0 ;; u) USERNAME="$OPTARG" ;; esac done shift $(($OPTIND - 1)) if [ $# == 0 ]; then echo "Missing argument FILE" >&2 exit 1 fi if [ $# != 1 ]; then echo "Unexpected argument: $2" >&2 exit 1 fi if [ $DOWNLOAD -eq 0 -a -n "$STORAGE" ]; then echo "-s and -d are incompatible" >&2 exit 1 fi FILE="$1" if [ "$FILE" != "$(basename "$FILE")" ]; then # We need a simple file name without any slashes. Instead of erroring, we # strip the file name out so that hib://filename is a valid argument. FILE=$(basename "$FILE") # Since we are doing something fancy, give the user a clue that we modified # their input. echo "Searching for $FILE" >&2 fi if [ -z "$DESTINATION" ]; then DESTINATION="." fi if [ -d "$DESTINATION" ]; then DESTINATION="$DESTINATION/$FILE" fi if [ -z "$STORAGE" ]; then handle_download "$DESTINATION" else local STORAGE_FILE STORAGE_FILE=$(find "$STORAGE" -name "$FILE") if [ -z "$STORAGE_FILE" ]; then STORAGE_FILE="$STORAGE/$FILE" handle_download "$STORAGE_FILE" fi if [ ! "$STORAGE_FILE" -ef "$DESTINATION" ]; then ln -s "$STORAGE_FILE" "$DESTINATION" if [ ! "$STORAGE_FILE" -ef "$DESTINATION" ]; then # If DESTINATION is in a different directory than the CWD and STORAGE # is a relative path, then ln -s will produce a broken link. To produce # a valid link we resolve the path with readlink. This is unfortunate # because having relative links is nicer and we will undo any symlinks # in the path. Therefore, we detect when things have broken and only # use readlink when we must. STORAGE_FILE=$(readlink -f "$STORAGE_FILE") rm "$DESTINATION" ln -s "$STORAGE_FILE" "$DESTINATION" fi fi fi } main "$@"