327 lines
9.9 KiB
Bash
Executable File
327 lines
9.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# -----------------------------------------------------------------------------
|
|
# Script Name: imgbb
|
|
# Description: Uploads an image from the system clipboard or a specified file
|
|
# to ImgBB (imgbb.com) using their V1 API. Retrieves the direct
|
|
# image URL, prints it to stdout, and copies it to the X11 clipboard.
|
|
# Author: Ritchie Cunningham <ritchie@ritchiecunningham.co.uk>
|
|
# Contributors: dacav <dacav@fastmail.com>
|
|
# License: GPL 3.0 License
|
|
# Version: 1.2
|
|
# Date: 22/04/2025
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# === Default Configuration & Argument Parsing. ===
|
|
|
|
api_key=""
|
|
config_file="$HOME/.config/imgbb_uploader.conf"
|
|
filepath=""
|
|
expire_seconds="7200" # 2h - Set to "" for no expiry. or use flag -e 0, never or none.
|
|
custom_name=""
|
|
markdown_mode="false"
|
|
clipboard="$XDG_SESSION_TYPE"
|
|
|
|
[ ! -e "$config_file" ] || . "$config_file"
|
|
|
|
print_usage() {
|
|
echo "Usage: $(basename "$0") [options] [filepath]"
|
|
echo "Uploads image from clipboard (default) or filepath to ImgBB."
|
|
echo ""
|
|
echo "Options:"
|
|
echo " [filepath] Optional path to an image file to upload."
|
|
echo " -e, --expire DURATION Set auto-deletion timeout. DURATION is a number"
|
|
echo " followed by s(econds), m(inutes), h(ours), or d(ays)."
|
|
echo " Default unit is minutes if unspecified."
|
|
echo " '0', 'none', 'never' to override default expiry."
|
|
echo " -n, --name NAME Set a custom filename for the uploaded image."
|
|
echo " --markdown Output/copy the URL in Markdown image format."
|
|
echo " -h, --help Show this help message."
|
|
}
|
|
|
|
die() {
|
|
declare msg
|
|
|
|
msg="$*"
|
|
notify_cmd -u critical "ImgBB Upload Error" "$msg" &>/dev/null
|
|
echo "$msg" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Parse command-line options.
|
|
while [[ $# -gt 0 ]]; do
|
|
key="$1"
|
|
case $key in
|
|
-e|--expire)
|
|
expire_input="$2" # Usr input.
|
|
# Check for 'no expiry value'.
|
|
if [[ "$expire_input" == "0" || "$expire_input" == "never" || "$expire_input" == 'none' ]]; then
|
|
expire_seconds="" # Unset for no expiration.
|
|
echo "Setting NO expiration."
|
|
else
|
|
# Proceed with parsing regular.
|
|
value="" unit="" seconds=""
|
|
|
|
# Regex to capture the number part and optional unit part (s,m,h,d)
|
|
if [[ "$expire_input" =~ ^([0-9]+)([smhd]?)$ ]]; then
|
|
value="${BASH_REMATCH[1]}" # The numeric part.
|
|
unit="${BASH_REMATCH[2]}" # The unit part.
|
|
|
|
# Default to minutes if no unit was provided.
|
|
if [ -z "$unit" ]; then unit="m"; fi
|
|
|
|
# Calculate total seconds based on the unit.
|
|
case "$unit" in
|
|
s) seconds=$((value)) ;;
|
|
m) seconds=$((value * 60)) ;;
|
|
h) seconds=$((value * 3600)) ;; # 60*60.
|
|
d) seconds=$((value * 86400)) ;; # 60*60*24.
|
|
*) # Not really requried given the regex, but hey-ho.
|
|
echo "Error: Internal error parsing unit '$unit' from '$expire_input'."
|
|
exit 1 ;;
|
|
esac
|
|
|
|
# Validate against ImgBB limits (60 seconds to 15552000 seconds / 180 days).
|
|
if (( seconds < 60 )) || (( seconds > 15552000 )); then
|
|
echo "Error: Calculated expiration ($seconds seconds) is outside of ImgBB's allowed range (60s - 15552000s)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Store the final result.
|
|
expire_seconds="$seconds"
|
|
echo "Expiration set to $expire_seconds seconds ($expire_input)"
|
|
else
|
|
# Someone did funky input. Tell them to stop that!
|
|
echo "Error: Invalid expiration format '$expire_input'." >&2
|
|
echo "Use a number optionally followed by s, m, h or d OR use 0/none/never (e.g., 300, 5m, 2h, 7d)." >&2
|
|
print_usage # Maybe they need help.
|
|
exit 1
|
|
fi
|
|
fi
|
|
shift
|
|
shift
|
|
;;
|
|
-n|--name)
|
|
custom_name="$2"
|
|
shift
|
|
shift
|
|
;;
|
|
--markdown)
|
|
markdown_mode="true"
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
print_usage
|
|
exit 0
|
|
;;
|
|
-*)
|
|
# Unknown option.
|
|
echo "Error: Unknown option '$1'" >&2
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
*)
|
|
# Assume it's the filepath (only one allowed).
|
|
if [ -n "$filepath" ]; then
|
|
echo "Error: Multiple filepaths provided ($filepath, $1). Only one allowed." >&2
|
|
print_usage
|
|
exit 1
|
|
fi
|
|
filepath="$1"
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check if filepath exists and is readable if provided.
|
|
if [ -n "$filepath" ] && [ ! -r "$filepath" ]; then
|
|
echo "Error: Cannot read file '$filepath'" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# === Read API Key. ===
|
|
if [[ -n "$IMGBB_API_KEY" ]]; then
|
|
api_key="$IMGBB_API_KEY"
|
|
fi
|
|
|
|
# Check if API key is actually set.
|
|
if [ -z "$api_key" ]; then
|
|
# Use function defined below if notify-send exists.
|
|
notify_cmd -u critical "ImgBB Upload Error" "API Key not found." &>/dev/null
|
|
echo "Error: API Key not found." >&2
|
|
echo "Please store your key in '$config_file' or set the IMGBB_API_KEY environment variable." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# === Check Dependencies. ===
|
|
check_command() {
|
|
if ! command -v "$1" &> /dev/null; then
|
|
notify_cmd -u critical "ImgBB Upload Error" "'$1' command not found. Please install it." &>/dev/null
|
|
echo "Error: '$1' command not found. Please install it (e.g., sudo apt install $1)." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
# Define notify_cmd first based on whether notify-send exists.
|
|
if ! command -v notify-send &> /dev/null; then
|
|
notify_cmd() { :; } # No-op if not found.
|
|
echo "Warning: notify-send not found. Desktop notifications disabled." >&2
|
|
else
|
|
notify_cmd() { notify-send "$@"; } # Actual command if found.
|
|
fi
|
|
# Now check other dependencies.
|
|
check_command curl
|
|
check_command jq
|
|
# Only check the clipboard command if we plan to use the clipboard.
|
|
if [ -z "$filepath" ]; then
|
|
case "$clipboard" in
|
|
x11)
|
|
check_command xclip
|
|
;;
|
|
wayland)
|
|
check_command wl-paste
|
|
;;
|
|
*)
|
|
die "Unsupported clipboard: $clipboard"
|
|
esac
|
|
fi
|
|
|
|
# === Main Logic. ===
|
|
paste_x11() {
|
|
declare tmp
|
|
|
|
tmp_img="$(mktemp)" || die "Could not create temp file."
|
|
|
|
xclip -selection clipboard -t image/png -o >"$tmp_img" 2>/dev/null
|
|
if [ -s "$tmp_img" ]; then
|
|
mv "$tmp_img" "$tmp_img.png"
|
|
printf "%s\n" "$tmp_img.png"
|
|
return 0
|
|
fi
|
|
|
|
xclip -selection clipboard -t image/jpeg -o >"$tmp_img" 2>/dev/null
|
|
if [ -s "$tmp_img" ]; then
|
|
mv "$tmp_img" "$tmp_img.jpeg"
|
|
printf "%s\n" "$tmp_img.jpeg"
|
|
return 0
|
|
fi
|
|
|
|
rm -f "$tmp_img"
|
|
die "Could not get PNG or JPEG image data from clipboard."
|
|
}
|
|
|
|
paste_wl() {
|
|
declare tmp_img
|
|
declare format
|
|
|
|
format="$(wl-paste -l | grep -m1 -F -e image/png -e image/jpeg)" ||
|
|
die "Invalid file type in clipboard."
|
|
|
|
tmp_img="$(mktemp)" || die "Could not create temp file."
|
|
|
|
if wl-paste -t "$format" >"$tmp_img" 2>/dev/null; then
|
|
mv "$tmp_img" "$tmp_img.${format##image/}"
|
|
printf "%s\n" "$tmp_img.${format##image/}"
|
|
return 0
|
|
fi
|
|
|
|
rm -f "$tmp_img"
|
|
die "Could not get PNG or JPEG image data from clipboard."
|
|
}
|
|
|
|
yank_x11() {
|
|
xclip -selection clipboard
|
|
}
|
|
|
|
yank_wl() {
|
|
wl-copy
|
|
}
|
|
|
|
TMP_IMG="" # Path to the image file to upload.
|
|
image_source_description=""
|
|
# Setup trap *only* if using clipboard mode.
|
|
if [ -z "$filepath" ]; then
|
|
# === Clipboard Input Mode. ===
|
|
image_source_description="clipboard"
|
|
TMP_IMG="$(
|
|
case "$clipboard" in
|
|
x11) paste_x11;;
|
|
wayland) paste_wl;;
|
|
esac
|
|
)" || exit
|
|
trap 'rm -f "$TMP_IMG"' EXIT
|
|
else
|
|
# === File Input Mode. ===
|
|
image_source_description="file '$filepath'"
|
|
TMP_IMG="$filepath" # Use the provided file path directly.
|
|
echo "Using image from file: $filepath"
|
|
fi
|
|
|
|
# Build curl options.
|
|
curl_opts=(-s -S -f -L -X POST)
|
|
curl_opts+=(--form "key=$api_key")
|
|
curl_opts+=(--form "image=@$TMP_IMG")
|
|
|
|
if [ -n "$expire_seconds" ]; then
|
|
curl_opts+=(--form "expiration=$expire_seconds")
|
|
fi
|
|
if [ -n "$custom_name" ]; then
|
|
curl_opts+=(--form "name=$custom_name")
|
|
echo "Setting custom name to '$custom_name'."
|
|
fi
|
|
|
|
# Upload the image using curl.
|
|
notify_cmd "ImgBB Uploader" "Uploading image from $image_source_description..."
|
|
response=$(curl "${curl_opts[@]}" 'https://api.imgbb.com/1/upload')
|
|
|
|
# Check curl exit status.
|
|
if [ $? -ne 0 ]; then
|
|
notify_cmd -u critical "ImgBB Upload Error" "curl command failed during upload. Check network?"
|
|
echo "Error: curl command failed during upload. Check network connection." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Parse the JSON response using jq.
|
|
# Check for overall success status from ImgBB API.
|
|
success=$(echo "$response" | jq -r '.success')
|
|
|
|
if [ "$success" != "true" ]; then
|
|
error_msg=$(echo "$response" | jq -r '.error.message // "Unknown API error"')
|
|
notify_cmd -u critical "ImgBB Upload Error" "API Error: $error_msg"
|
|
echo "Error: ImgBB API returned an error - $error_msg" >&2
|
|
echo "Full response: $response" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Extract the direct image URL.
|
|
image_url=$(echo "$response" | jq -r '.data.url')
|
|
|
|
# Check if URL was successfully extracted.
|
|
if [ -z "$image_url" ] || [ "$image_url" == "null" ]; then
|
|
notify_cmd -u critical "ImgBB Upload Error" "Could not parse image URL from API response."
|
|
echo "Error: Could not parse image URL from API response." >&2
|
|
echo "Full response: $response" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Format output based on markdown flag.
|
|
output_url="$image_url"
|
|
if [ "$markdown_mode" = "true" ]; then
|
|
# Basic markdown image syntax - assumes filename is not needed for alt text here.
|
|
output_url=""
|
|
echo "Formatting as Markdown."
|
|
fi
|
|
|
|
# Output the URL to terminal and copy to clipboard.
|
|
echo "Image URL:"
|
|
echo "$output_url"
|
|
# Copy URL only if we determined we are not uploading from file (i.e., we used clipboard input)
|
|
# Or always copy? Let's always copy for now.
|
|
|
|
case "$clipboard" in
|
|
x11) printf "%s\n" "$output_url" | yank_x11;;
|
|
wayland) printf "%s\n" "$output_url" | yank_wl;;
|
|
esac
|
|
notify_cmd "ImgBB Uploader" "Success! URL copied: $output_url"
|
|
|
|
# Temporary files are removed automatically by the 'trap' command on exit if they were created.
|
|
exit 0
|