Skip to content

Instantly share code, notes, and snippets.

@Zorg64
Forked from shawnli87/download_gofile.sh
Last active April 18, 2024 13:28
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Zorg64/f0feddf1fedda3299babeef2c3c25029 to your computer and use it in GitHub Desktop.
Save Zorg64/f0feddf1fedda3299babeef2c3c25029 to your computer and use it in GitHub Desktop.
Bash script to download files from gofile.io
#!/bin/bash
# Prompts the user for a URL if not provided as a command-line argument.
# Extracts the ID from the given URL to use in API requests.
function promptForURL() {
local url="$1"
until [ -n "$url" ]; do
read -p "URL: " url
done
id=$(sed 's|.*gofile.io/d/||g' <<< "$url")
echo -n "Loading $id content..."
}
# Retrieves a guest token from GoFile's API for authenticated requests.
function retrieveToken() {
token=$(curl -s -X POST 'https://api.gofile.io/accounts' \
-H 'User-Agent: Mozilla/5.0' \
-H 'Accept-Encoding: gzip, deflate, br' \
-H 'Accept: */*' \
-H 'Connection: keep-alive' | jq -r '.data.token' 2>/dev/null)
if [ -z "$token" ]; then
echo "Failed to create guest account. Please try again later."
exit 1
fi
}
# Retrieves a website token required for some API requests.
function retrieveWebsiteToken() {
websiteToken=$(curl -s 'https://gofile.io/dist/js/alljs.js' | awk -F'"' '/var fetchData = { wt:/ {print $(NF-1)}')
if [ -z "$websiteToken" ]; then
echo "Failed to retrieve website token. Please try again later."
exit 1
fi
}
# Retrieve content from GoFile using the content ID.
# If a password is required, prompts the user for it.
function retrieveContent() {
local password_hash="$1"
local extra_params=""
if [ -n "$password_hash" ]; then
extra_params="&password=$password_hash"
fi
resp=$(curl -H "Authorization: Bearer $token" "https://api.gofile.io/contents/$id?wt=$websiteToken&cache=true$extra_params" 2>/dev/null)
local status=$(jq -r '.status' <<< "$resp")
local notFound=$(jq -r '.data.notFound // "false"' <<< "$resp")
local passwordRequired=$(jq -r '.data.password // "false"' <<< "$resp")
local passwordStatus=$(jq -r '.data.passwordStatus // "none"' <<< "$resp")
if [[ "$notFound" == "true" ]]; then
printf "\r\033[K"
echo "Content not found. Please check the URL and try again."
exit 1
elif [[ "$status" == "ok" && "$passwordRequired" == "true" && "$passwordStatus" == "passwordRequired" && -z "$password_hash" ]]; then
handlePassword
elif [[ "$status" == "ok" && "$passwordRequired" == "true" && "$passwordStatus" != "passwordOk" ]]; then
printf "\r\033[K"
echo "Incorrect password provided. Please try again."
exit 1
elif [[ "$status" != "ok" ]]; then
printf "\r\033[K"
echo "Failed to retrieve content. Please check the URL or try again later."
exit 1
fi
}
# Prompts the user for a password if needed to access protected content.
function handlePassword() {
printf "\r\033[K"
read -sp "Enter password: " password
echo
password_hash=$(printf "%s" "$password" | sha256sum | cut -d' ' -f1)
retrieveContent "$password_hash"
}
# Shows a spinner animation while background processes are running.
function spinner {
local -r pid=$1
local -r delay=0.2
local spinstr='|/-\'
tput civis # Hide cursor
echo -n " "
while kill -0 $pid 2>/dev/null; do
local temp=${spinstr#?}
printf "\b%c" "$spinstr"
local spinstr=$temp${spinstr%"$temp"}
sleep $delay
done
printf "\b "
tput cnorm # Show cursor
}
# Formats the size of the file from bytes to a more readable format (MB or GB).
function format_size {
local -i bytes=$1
if [ $bytes -lt 1000000000 ]; then
echo $(awk "BEGIN {printf \"%.2f\", $bytes/1000000}")" MB"
else
echo $(awk "BEGIN {printf \"%.2f\", $bytes/1000000000}")" GB"
fi
}
# Manages the stopping and starting of a spinner for background operations.
function start_and_stop_spinner() {
local -a pids=("${!1}")
spinner "${pids[0]}" &
spinner_pid=$!
# Waits for all processes to finish.
for pid in "${pids[@]}"; do
if [[ -n "$pid" && "$pid" =~ ^[0-9]+$ ]]; then
wait "$pid"
fi
done
# Stops the spinner if it's still running.
if kill -0 "$spinner_pid" 2>/dev/null; then
kill -9 "$spinner_pid"
wait "$spinner_pid" 2>/dev/null
fi
}
# Tracks download progress and updates the console.
function download_progress() {
local total_size=$1
local name=$2
local -a pids=("${!3}")
local current_size=0
local percent=0
# Ensures the reported total size is valid.
if [ "$total_size" -le 0 ]; then
echo "Error: Total size is invalid."
return
fi
tput civis
# Continuously updates progress until all parts are downloaded.
while : ; do
current_size=0
for part in "$name.part"*; do
if [ -f "$part" ]; then
local size=$(stat -c "%s" "$part")
current_size=$((current_size + size))
fi
done
percent=$((current_size * 100 / total_size))
printf "\r$name | $f_size | $percent%%"
# Checks if all download processes have finished.
local all_done=1
for pid in "${pids[@]}"; do
if kill -0 "$pid" 2>/dev/null; then
all_done=0
break
fi
done
[ "$all_done" -eq 1 ] && break
sleep 1
done
printf "\r\033[K"
tput cnorm
}
function determine_download_parts() {
local file_size=$1 # File size in bytes
if [ "$file_size" -le 50000000 ]; then
echo 1 # Files 50 MB or smaller
elif [ "$file_size" -gt 50000000 ] && [ "$file_size" -le 100000000 ]; then
echo 2 # Files larger than 50 MB up to 100 MB
else
echo 3 # Files larger than 100 MB
fi
}
function download_and_combine_file() {
local name=$1
local file_url=$2
local total_size=$3
local expected_md5=$4
local num_parts=$(determine_download_parts "$total_size") # Determine the number of parts
local -a pids=()
local f_size=$(format_size "$total_size")
echo -n "$name | $f_size | "
# Calculate the size of each part based on the total size and the number of parts
local part_size=$(($total_size / $num_parts))
local last_part_size=$(($total_size - ($part_size * ($num_parts - 1))))
# Download each part
for ((part=0; part<num_parts; part++)); do
local start=$(($part * $part_size))
local end=$(($start + $part_size - 1))
# Adjust the end for the last part
if [ $part -eq $(($num_parts - 1)) ]; then
end=$(($start + $last_part_size - 1))
fi
curl -s -H 'Cookie: accountToken='"$token" -H "Range: bytes=$start-$end" "$file_url" -o "$name.part$part" &
pids[${part}]=$!
done
# Monitors download progress.
download_progress "$total_size" "$name" "pids[@]"
echo "$name | $f_size | 100%"
echo -n "Combining parts... "
# Combines parts and calculates MD5 checksum in the background
(
for ((part=0; part<num_parts; part++)); do
cat "$name.part$part" >> "$name" # Appends each part in sequence
done
# Remove part files after combining
for ((part=0; part<num_parts; part++)); do
rm -f "$name.part$part"
done
# Calculating MD5 checksum of the combined file
calculated_md5=$(md5sum "$name" | awk '{print $1}')
echo $calculated_md5 > "$name.md5"
) &
combine_pid=$!
# Handles the spinner for the combining and MD5 calculation process.
local -a combine_pids=($combine_pid)
start_and_stop_spinner "combine_pids[@]"
printf "\r\033[K"
# Read the calculated MD5 from the file
calculated_md5=$(<"$name.md5")
# Comparing checksums
if [ "$calculated_md5" != "$expected_md5" ]; then
echo "Combining parts... Error! Download corrupt, removing '$name'"
rm -f "$name"
failed_dl+=("$name")
rm -f "$name.md5"
return 1
else
echo "Combining parts... Done!"
fi
rm -f "$name.md5"
}
# Processes each file found within the content fetched from GoFile.
function processFiles() {
# Extracts names and links for all files within the content and sorts them alphabetically.
while IFS="|" read -r name total_size file_url md5_checksum; do
if [ ! -f "$name" ]; then
if ! [[ $total_size =~ ^[0-9]+$ ]]; then
echo "Error: Failed to get valid content length for \"$name\""
continue
fi
download_and_combine_file "$name" "$file_url" "$total_size" "$md5_checksum"
fi
done < <(jq -r '.data.children[] | "\(.name)|\(.size)|\(.link)|\(.md5)"' <<< "$resp" | sort -t "|" -k1,1)
}
#Main
# Initiates the script by prompting the user for a URL if not supplied as an argument.
promptForURL "$1"
# Retrieves a token for authentication with the GoFile API.
retrieveToken
# Retrieves a website token required for further API interactions.
retrieveWebsiteToken
# Fetch content info from GoFile using the ID extracted from the URL.
retrieveContent
mkdir -p "$id"
cd "$id"
printf "\r\033[K"
echo "Loading $id content...✓"
# Processes files within the fetched content.
failed_dl=()
processFiles
# Show any failed downloads
echo
if [ ${#failed_dl[@]} -ne 0 ]; then
echo "Failed Downloads: ${#failed_dl[@]}"
for file in "${failed_dl[@]}"; do
echo "- $file"
done
echo -e "\nRun script again to attempt failed files.\n"
else
echo -e "All downloads successful.\n"
fi
echo -e "\nNote: gofile.io is entirely free with no ads,\nYou can support it at https://gofile.io/donate"
@Zorg64
Copy link
Author

Zorg64 commented Mar 15, 2024

  • Modifications:
    • Updated for API update 2024-03-10.
    • Additional information and progress indicators.
    • Files are downloaded concurrently in three parts.
      • Three is probably gofile limit, errors when trying more,
    • Combines downloaded parts into the final file, with clean-up of intermediates.

@manavchaudhary1
Copy link

Error:
└─$ bash download_gofile.sh
URL:
Loading content...grep: -P supports only unibyte and UTF-8 locales
Failed to retrieve website token. Please try again later.

Changing Lang Format :
export LANG=en_US.UTF

Loading RwqlnG content...✓
jq: error: syntax error, unexpected INVALID_CHARACTER (Windows cmd shell quoting issues?) at , line 1:
].name
jq: 1 compile error
jq: error: syntax error, unexpected INVALID_CHARACTER (Windows cmd shell quoting issues?) at , line 1:
].link
jq: 1 compile error
curl: option : blank argument where content is expected
curl: try 'curl --help' or 'curl --manual' for more information
Error: Failed to get valid content length for
jq: error: syntax error, unexpected INVALID_CHARACTER (Windows cmd shell quoting issues?) at , line 1:
].name
jq: 1 compile error
jq: error: syntax error, unexpected INVALID_CHARACTER (Windows cmd shell quoting issues?) at , line 1:
].link
jq: 1 compile error
curl: option : blank argument where content is expected
curl: try 'curl --help' or 'curl --manual' for more information
Error: Failed to get valid content length for
jq: error: syntax error, unexpected INVALID_CHARACTER (Windows cmd shell quoting issues?) at , line 1:
].name
jq: 1 compile error
jq: error: syntax error, unexpected INVALID_CHARACTER (Windows cmd shell quoting issues?) at , line 1:
].link
jq: 1 compile error
curl: option : blank argument where content is expected
curl: try 'curl --help' or 'curl --manual' for more information
Error: Failed to get valid content length for
Downloading: 1_5148048852976141111.mp4, 58.20 MB... Done!
Combining parts... Done!

Note: gofile.io is entirely free with no ads,
You can support it at https://gofile.io/donate

Getting Errors but download is Success

@Zorg64
Copy link
Author

Zorg64 commented Mar 22, 2024

@manavchaudhary1
I made a change that should address the grep error, but not sure cause of jq error and I can't find a way to replicate. Maybe check encoding?
file download_gofile.sh
Also do you get same error(s) from other links? Didn't want to download the content from the link that got you errors so can't really test. Here is a link to a dummy file, filled with 0, created with dd if=/dev/zero of=dummyfile bs=1M count=100
https://gofile.io/d/yBqMcL

@Zorg64
Copy link
Author

Zorg64 commented Mar 24, 2024

  • Changes 2024-03-23:
    • Additional error handling.
    • Progress indication formatting.
    • Files are downloaded concurrently in up to three parts, determined by file size.
      • 50MB or less, 50-100MB, and greater than 100MB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment