#!/bin/bash # save_page_as -- Save a webpage using your browser. # https://github.com/abiyani/automate-save-page-as # Last Modified By: Dan Jacobson # http://jidanni.org/ # Last Modified On: Thu Oct 18 00:17:04 2018 # Update Count : 27 set -e set -u set -o pipefail # Assert existence of xdotool to begin with if ! xdotool --help &>/dev/null; then printf "ERROR: 'xdotool' is not present (or not in the PATH). Please visit http://www.semicomplete.com/projects/xdotool/ to download it for your platform.\n" >&2 exit 1 fi load_wait_time=4 save_wait_time=8 dialog_wait_time=3 scriptname=$(basename $0) destination=. browser=${BROWSER?} suffix="" url="" function print_usage() { #Not too wide. Must fit on all screens. cat 1>&2 <&2 print_usage exit 1 ;; *) if [ ! -z "$url" ]; then printf "ERROR: Expected exactly one positional argument (URL) to be present, but encountered a second one ('%s').\n\n" "${1}" >&2 print_usage exit 1 fi url="$1" shift; ;; esac done # Returns 1 if input param contains any non-printable or non-ascii character, else returns 0 # (Inspiration: http://stackoverflow.com/a/13596664/1857518) function has_non_printable_or_non_ascii() { LANG=C if printf "%s" "${1}" | grep '[^ -~]\+' &>/dev/null; then printf 1 else printf 0 fi } function validate_input() { if [[ -z "${url}" ]]; then printf "ERROR: URL must be specified.\n" >&2 print_usage exit 1 fi if [[ -d "${destination}" ]]; then printf "INFO: The specified destination ('%s') is a directory path, will save file inside it with the default name.\n" "${destination}">&2 else local basedir="$(dirname "${destination}")" if [[ ! -d "${basedir}" ]]; then printf "ERROR: Directory '%s' does not exist - Will NOT continue.\n" "${basedir}" >&2 exit 1 fi fi destination="$(readlink -f "$destination")" # Ensure absolute path case $browser in google-chrome|chromium|firefox|epiphany);; *) echo $0: ERROR: Browser $browser is not supported. 1>&2; exit 1;; esac if ! command -v "${browser}" &>/dev/null; then printf "ERROR: Command '${browser}' not found. Make sure it is installed, and in path.\n" >&2 exit 1 fi local num_regexp='^.[0-9]+$|^[0-9]+$|^[0-9]+.[0-9]+$' # Matches a valid number (in decimal notation) if [[ ! "${load_wait_time}" =~ $num_regexp || ! "${save_wait_time}" =~ $num_regexp || ! "${dialog_wait_time}" =~ $num_regexp ]] ; then printf "ERROR: --load-wait-time (='%s'), and --save_wait_time(='%s') and --dialog-wait-time(='%s') must be valid numbers.\n" "${load_wait_time}" "${load_wait_time}" "${dialog_wait_time}" >&2 exit 1 fi if [[ $(has_non_printable_or_non_ascii "${destination}") -eq 1 || $(has_non_printable_or_non_ascii "${suffix}") -eq 1 ]]; then printf "ERROR: Either --destination ('%s') or --suffix ('%s') contains a non ascii or non-printable ascii character(s).\n" "${destination}" "${suffix}" >&2 printf "'xdotool' does not mingle well with non-ascii characters (https://code.google.com/p/semicomplete/issues/detail?id=14).\n\n" >&2 printf '!!!! Will NOT proceed !!!!\n' >&2 exit 1 fi } validate_input ############## # Launch ${browser}, and wait for the page to load "${browser}" "${url}" &>/dev/null & sleep ${load_wait_time} # Find the id for the ${browser} window browser_wid="$(xdotool search --sync --onlyvisible --class "${browser}" | head -n 1)" wid_re='^[0-9]+$' # window-id must be a valid integer if [[ ! "${browser_wid}" =~ ${wid_re} ]]; then printf "ERROR: Unable to find X-server window id for browser.\n" >&2 exit 1 fi # Activate the ${browser} window, and "press" ctrl+s xdotool windowactivate "${browser_wid}" key --clearmodifiers "ctrl+s" sleep ${dialog_wait_time} # Give 'Save as' dialog box time to show up # Resolve the expected title name for save file dialog box (chrome & firefox differ in this regard) if [[ "${browser}" == "epiphany" ]]; then savefile_dialog_title="Save" elif [[ "${browser}" == "firefox" ]]; then savefile_dialog_title="Save as" else savefile_dialog_title="Save file" fi # Find window id for the "Save file" dialog box savefile_wid="$(xdotool search --name "$savefile_dialog_title" | head -n 1)" if [[ ! "${savefile_wid}" =~ ${wid_re} ]]; then printf "ERROR: Unable to find window id for 'Save File' Dialog.\n" >&2 exit 1 fi # Fix for Issue #1: Explicitly focus on the "name" field (works on both: gnome, and kde) xdotool windowactivate "${savefile_wid}" key --delay 20 --clearmodifier "Alt+n" # Check if we are using kde is_kde=0 # Don't feel bad if DESKTOP_SESSION env variable is not present set +u if [[ "${DESKTOP_SESSION}" =~ ^kde-? ]]; then is_kde=1 fi set -u if [[ ! -z "${suffix}" ]]; then ########################### # Make sure that we are at correct position before typing the "suffix" # # If the user is using 'kde-plasma', then the full name of the file including the extension is highlighted # in the name field, so simply pressing a Right key and adding suffix leads to incorrect result. # Hence as a special case for 'kde-*' we move back 5 characters Left from the end before adding the suffix. # Now this strategy is certainly not full proof and assumes that file extension is always 4 characters long ('html'), # but this is the only fix I can think for this special case right now. Of course it's easy to tweak the number of # Left key moves you need if you know your file types in advance. if [[ "${is_kde}" -eq 1 ]]; then printf "INFO: Desktop session is found to be '${DESKTOP_SESSION}', hence the full file name will be highlighted.\n" >&2 printf "Assuming extension .html to move back 5 character left before adding suffix (change accordingly if you need to).\n" >&2 xdotool windowactivate "${savefile_wid}" key --delay 40 --clearmodifier End Left Left Left Left Left else xdotool windowactivate "${savefile_wid}" key --delay 20 --clearmodifiers Right fi set -u ########################### extraarg="" if [[ "${suffix::1}}" == "-" ]]; then extraarg="-" fi xdotool type --delay 10 --clearmodifiers "${extraarg}" "${suffix}" fi # Activate the 'Save File' dialog and type in the appropriate filename #(depending on ${destination} value: 1) directory, 2) full path, 3) empty) if [[ ! -z "${destination}" ]]; then if [[ -d "${destination}" ]]; then # Case 1: --destination was a directory. xdotool windowactivate "${savefile_wid}" key --delay 20 --clearmodifiers Home xdotool type --delay 10 --clearmodifiers "${destination}/" else # Case 2: --destination was full path. xdotool windowactivate "${savefile_wid}" key --delay 20 --clearmodifiers "ctrl+a" "BackSpace" xdotool type --delay 10 --clearmodifiers "${destination}" fi fi xdotool windowactivate "${savefile_wid}" key --delay 20 --clearmodifiers Return printf "INFO: Saving web page ...\n" >&2 # Wait for the file to be completely saved sleep ${save_wait_time} case $browser in epiphany) is_kde=1;; esac # Close the browser tab/window (Ctrl+w for KDE, Ctrl+F4 otherwise) if [[ "${is_kde}" -eq 1 ]]; then xdotool windowactivate "${browser_wid}" key --clearmodifiers "ctrl+w" else xdotool windowactivate "${browser_wid}" key --clearmodifiers "ctrl+F4" fi printf "INFO: Done!\n">&2 #Maybe also say: # echo See: #or Proof: # ls -l $destination