3 ##########################################################################
8 # Permission is hereby granted, free of charge,
9 # to any person obtaining a copy of this software and
10 # associated documentation files (the "Software"), to
11 # deal in the Software without restriction, including
12 # without limitation the rights to use, copy, modify,
13 # merge, publish, distribute, sublicense, and/or sell
14 # copies of the Software, and to permit persons to whom
15 # the Software is furnished to do so,
16 # subject to the following conditions:
18 # The above copyright notice and this permission notice
19 # shall be included in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
23 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
25 # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 ##########################################################################
30 # https://github.com/jomo/imgur-screenshot
31 # https://help.imgur.com/hc/en-us/articles/209592766-Tools-for-Imgur
33 # Slightly modified for `nnn` integration
36 # Description: Upload an image file to imgur
38 if [ "${1}" = "--debug" ]; then
39 echo "########################################"
40 echo "Enabling debug mode"
41 echo "Please remove credentials before pasting"
42 echo "########################################"
45 for arg in ${0} "${@}"; do
53 current_version="v1.7.4"
56 uname | grep -q "Darwin"
59 ### IMGUR-SCREENSHOT DEFAULT CONFIG ####
61 # You can override the config in ~/.config/imgur-screenshot/settings.conf
63 imgur_anon_id="ea6c0ef2987808e"
64 imgur_icon_path="${HOME}/Pictures/imgur.png"
71 credentials_file="${HOME}/.config/imgur-screenshot/credentials.conf"
73 file_name_format="imgur-%Y_%m_%d-%H:%M:%S.png" # when using scrot, must end with .png!
74 file_dir="${HOME}/Pictures"
76 upload_connect_timeout="5"
80 # shellcheck disable=SC2034
82 screenshot_select_command="screencapture -i %img"
83 screenshot_window_command="screencapture -iWa %img"
84 screenshot_full_command="screencapture %img"
85 open_command="open %url"
87 screenshot_select_command="scrot -s %img"
88 screenshot_window_command="scrot %img"
89 screenshot_full_command="scrot %img"
90 open_command="xdg-open %url"
95 edit_command="gimp %img"
97 exit_on_album_creation_fail="true"
99 log_file="${HOME}/.imgur-screenshot.log"
106 # NOTICE: if you make changes here, also edit the docs at
107 # https://github.com/jomo/imgur-screenshot/wiki/Config
109 # You can override the config in ~/.config/imgur-screenshot/settings.conf
111 ############## END CONFIG ##############
113 settings_path="${HOME}/.config/imgur-screenshot/settings.conf"
114 if [ -f "${settings_path}" ]; then
115 # shellcheck disable=SC1090
116 source "${settings_path}"
120 if [ "${1}" = "--check" ]; then
121 (type grep &>/dev/null && echo "OK: found grep") || echo "ERROR: grep not found"
123 if type growlnotify &>/dev/null; then
124 echo "OK: found growlnotify"
125 elif type terminal-notifier &>/dev/null; then
126 echo "OK: found terminal-notifier"
128 echo "ERROR: growlnotify nor terminal-notifier found"
130 (type screencapture &>/dev/null && echo "OK: found screencapture") || echo "ERROR: screencapture not found"
131 (type pbcopy &>/dev/null && echo "OK: found pbcopy") || echo "ERROR: pbcopy not found"
133 (type notify-send &>/dev/null && echo "OK: found notify-send") || echo "ERROR: notify-send (from libnotify-bin) not found"
134 (type scrot &>/dev/null && echo "OK: found scrot") || echo "ERROR: scrot not found"
135 (type xclip &>/dev/null && echo "OK: found xclip") || echo "ERROR: xclip not found"
137 (type curl &>/dev/null && echo "OK: found curl") || echo "ERROR: curl not found"
142 # notify <'ok'|'error'> <title> <text>
145 if type growlnotify &>/dev/null; then
146 growlnotify --icon "${imgur_icon_path}" --iconpath "${imgur_icon_path}" --title "${2}" --message "${3}"
148 terminal-notifier -appIcon "${imgur_icon_path}" -contentImage "${imgur_icon_path}" -title "imgur: ${2}" -message "${3}"
151 if [ "${1}" = "error" ]; then
152 notify-send -a ImgurScreenshot -u critical -c "im.error" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}"
154 notify-send -a ImgurScreenshot -u low -c "transfer.complete" -i "${imgur_icon_path}" -t 500 "imgur: ${2}" "${3}"
159 function take_screenshot() {
160 echo "Please select area"
161 is_mac || sleep 0.1 # https://bbs.archlinux.org/viewtopic.php?pid=1246173#p1246173
163 cmd="screenshot_${mode}_command"
164 cmd=${!cmd//\%img/${1}}
166 if ! shot_err="$(${cmd} &>/dev/null)"; then #takes a screenshot with selection
167 echo "Failed to take screenshot '${1}': '${shot_err}'. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}"
168 notify error "Something went wrong :(" "Information has been logged"
173 function check_for_update() {
174 # exit non-zero on HTTP error, output only the body (no stats) but output errors, follow redirects, output everything to stdout
175 remote_version="$(curl --compressed -fsSL --stderr - "https://api.github.com/repos/jomo/imgur-screenshot/releases" | grep -Em 1 --color 'tag_name":\s*".*"' | cut -d '"' -f 4)"
176 if [ -n "$remote_version" ]; then
177 if [ ! "${current_version}" = "${remote_version}" ] && [ -n "${current_version}" ] && [ -n "${remote_version}" ]; then
179 echo "Version ${remote_version} is available (You have ${current_version})"
180 notify ok "Update found" "Version ${remote_version} is available (You have ${current_version}). https://github.com/jomo/imgur-screenshot"
181 echo "Check https://github.com/jomo/imgur-screenshot/releases/${remote_version} for more info."
182 elif [ -z "${current_version}" ] || [ -z "${remote_version}" ]; then
183 echo "Invalid empty version string"
184 echo "Current (local) version: '${current_version}'"
185 echo "Latest (remote) version: '${remote_version}'"
187 echo "Version ${current_version} is up to date."
190 echo "Failed to check for latest version: ${remote_version}"
194 function check_oauth2_client_secrets() {
195 if [ -z "${imgur_acct_key}" ] || [ -z "${imgur_secret}" ]; then
196 echo "In order to upload to your account, register a new application at:"
197 echo "https://api.imgur.com/oauth2/addclient"
198 echo "Select 'OAuth 2 authorization without a callback URL'"
199 echo "Then, set the imgur_acct_key (Client ID) and imgur_secret in your config."
204 function load_access_token() {
206 # check for saved access_token and its expiration date
207 if [ -f "${credentials_file}" ]; then
208 # shellcheck disable=SC1090
209 source "${credentials_file}"
211 current_time="$(date +%s)"
212 preemptive_refresh_time="$((10*60))"
213 expired="$((current_time > (token_expire_time - preemptive_refresh_time)))"
214 if [ -n "${refresh_token}" ]; then
216 if [ "${expired}" -eq "0" ]; then
218 refresh_access_token "${credentials_file}"
221 acquire_access_token "${credentials_file}"
225 function acquire_access_token() {
226 check_oauth2_client_secrets
228 authorize_url="https://api.imgur.com/oauth2/authorize?client_id=${imgur_acct_key}&response_type=pin"
230 echo "${authorize_url}"
231 echo "and grant access to this application."
232 read -rp "Enter the PIN: " imgur_pin
234 if [ -z "${imgur_pin}" ]; then
235 echo "PIN not entered, exiting"
239 # exchange the PIN for access token and refresh token
240 response="$(curl --compressed -fsSL --stderr - \
241 -F "client_id=${imgur_acct_key}" \
242 -F "client_secret=${imgur_secret}" \
243 -F "grant_type=pin" \
244 -F "pin=${imgur_pin}" \
245 https://api.imgur.com/oauth2/token)"
246 save_access_token "${response}" "${1}"
249 function refresh_access_token() {
250 check_oauth2_client_secrets
251 token_url="https://api.imgur.com/oauth2/token"
252 # exchange the refresh token for access_token and refresh_token
253 if ! response="$(curl --compressed -fsSL --stderr - \
254 -F "client_id=${imgur_acct_key}" \
255 -F "client_secret=${imgur_secret}" \
256 -F "grant_type=refresh_token" \
257 -F "refresh_token=${refresh_token}" \
261 handle_upload_error "${response}" "${token_url}"
264 save_access_token "${response}" "${1}"
267 function save_access_token() {
268 if ! grep -q "access_token" <<<"${1}"; then
269 # server did not send access_token
270 echo "Error: Something is wrong with your credentials:"
275 access_token="$(grep -Eo 'access_token":".*"' <<<"${1}" | cut -d '"' -f 3)"
276 refresh_token="$(grep -Eo 'refresh_token":".*"' <<<"${1}" | cut -d '"' -f 3)"
277 expires_in="$(grep -Eo 'expires_in":[0-9]*' <<<"${1}" | cut -d ':' -f 2)"
278 token_expire_time="$(( $(date +%s) + expires_in ))"
280 # create dir if not exist
281 mkdir -p "$(dirname "${2}")" 2>/dev/null
282 touch "${2}" && chmod 600 "${2}"
284 access_token="${access_token}"
285 refresh_token="${refresh_token}"
286 token_expire_time="${token_expire_time}"
290 function fetch_account_info() {
291 response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/account/me)"
292 if grep -Eq '"success":\s*true' <<<"${response}"; then
293 username="$(grep -Eo '"url":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
294 echo "Logged in as ${username}."
295 echo "https://${username}.imgur.com"
297 echo "Failed to fetch info: ${response}"
301 function delete_image() {
302 response="$(curl --compressed -X DELETE -fsSL --stderr - -H "Authorization: Client-ID ${1}" "https://api.imgur.com/3/image/${2}")"
303 if grep -Eq '"success":\s*true' <<<"${response}"; then
304 echo "Image successfully deleted (delete hash: ${2})." >> "${3}"
306 echo "The Image could not be deleted: ${response}." >> "${3}"
310 function upload_authenticated_image() {
311 echo "Uploading '${1}'..."
312 title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)"
313 if [ -n "${album_id}" ]; then
314 response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)"
316 response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -F "title=${title}" -F "image=@\"${1}\"" -H "Authorization: Bearer ${access_token}" https://api.imgur.com/3/image)"
319 # JSON parser premium edition (not really)
320 if grep -Eq '"success":\s*true' <<<"${response}"; then
321 img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
322 img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https!
323 del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
325 if [ -n "${auto_delete}" ]; then
326 export -f delete_image
327 echo "Deleting image in ${auto_delete} seconds."
328 nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" &
331 handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}"
333 err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
334 test -z "${err_msg}" && err_msg="${response}"
335 handle_upload_error "${err_msg}" "${1}"
339 function upload_anonymous_image() {
340 echo "Uploading '${1}'..."
341 title="$(echo "${1}" | rev | cut -d "/" -f 1 | cut -d "." -f 2- | rev)"
342 if [ -n "${album_id}" ]; then
343 response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" -F "album=${album_id}" https://api.imgur.com/3/image)"
345 response="$(curl --compressed --connect-timeout "${upload_connect_timeout}" -m "${upload_timeout}" --retry "${upload_retries}" -fsSL --stderr - -H "Authorization: Client-ID ${imgur_anon_id}" -F "title=${title}" -F "image=@\"${1}\"" https://api.imgur.com/3/image)"
347 # JSON parser premium edition (not really)
348 if grep -Eq '"success":\s*true' <<<"${response}"; then
349 img_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
350 img_ext="$(grep -Eo '"link":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4 | rev | cut -d "." -f 1 | rev)" # "link" itself has ugly '\/' escaping and no https!
351 del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
353 if [ -n "${auto_delete}" ]; then
354 export -f delete_image
355 echo "Deleting image in ${auto_delete} seconds."
356 nohup /bin/bash -c "sleep ${auto_delete} && delete_image ${imgur_anon_id} ${del_id} ${log_file}" &
359 handle_upload_success "https://i.imgur.com/${img_id}.${img_ext}" "https://imgur.com/delete/${del_id}" "${1}"
361 err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
362 test -z "${err_msg}" && err_msg="${response}"
363 handle_upload_error "${err_msg}" "${1}"
367 function handle_upload_success() {
369 echo "image link: ${1}"
370 echo "delete link: ${2}"
372 if [ "${copy_url}" = "true" ] && [ -z "${album_title}" ]; then
374 echo -n "${1}" | pbcopy
376 echo -n "${1}" | xclip -selection clipboard
378 echo "URL copied to clipboard"
381 # print to log file: image link, image location, delete link
382 echo -e "${1}\t${3}\t${2}" >> "${log_file}"
384 notify ok "Upload done!" "${1}"
386 # if [ ! -z "${open_command}" ] && [ "${open}" = "true" ]; then
387 # open_cmd=${open_command//\%url/${1}}
388 # open_cmd=${open_cmd//\%img/${2}}
389 # echo "Opening '${open_cmd}'"
394 function handle_upload_error() {
395 error="Upload failed: \"${1}\""
397 echo -e "Error\t${2}\t${error}" >> "${log_file}"
398 notify error "Upload failed :(" "${1}"
401 function handle_album_creation_success() {
403 echo "Album link: ${1}"
404 echo "Delete hash: ${2}"
407 notify ok "Album created!" "${1}"
409 if [ "${copy_url}" = "true" ]; then
411 echo -n "${1}" | pbcopy
413 echo -n "${1}" | xclip -selection clipboard
415 echo "URL copied to clipboard"
418 # print to log file: album link, album title, delete hash
419 echo -e "${1}\t\"${3}\"\t${2}" >> "${log_file}"
422 function handle_album_creation_error() {
423 error="Album creation failed: \"${1}\""
424 echo -e "Error\t${2}\t${error}" >> "${log_file}"
425 notify error "Album creation failed :(" "${1}"
426 if [ ${exit_on_album_creation_fail} ]; then
431 while [ ${#} != 0 ]; do
434 echo "usage: ${0} [--debug] [-c | --check | -v | -h | -u]"
435 echo " ${0} [--debug] [option]... [file]..."
437 echo " --debug Enable debugging, must be first option"
438 echo " -h, --help Show this help, exit"
439 echo " -v, --version Show current version, exit"
440 echo " --check Check if all dependencies are installed, exit"
441 echo " -c, --connect Show connected imgur account, exit"
442 echo " -o, --open <true|false> Override 'open' config"
443 echo " -e, --edit <true|false> Override 'edit' config"
444 echo " -i, --edit-command <command> Override 'edit_command' config (include '%img'), sets --edit 'true'"
445 echo " -l, --login <true|false> Override 'login' config"
446 echo " -a, --album <album_title> Create new album and upload there"
447 echo " -A, --album-id <album_id> Override 'album_id' config"
448 echo " -k, --keep-file <true|false> Override 'keep_file' config"
449 echo " -d, --auto-delete <s> Automatically delete image after <s> seconds"
450 echo " -u, --update Check for updates, exit"
451 echo " file Upload file instead of taking a screenshot"
454 echo "${current_version}"
466 # shellcheck disable=SC2034
499 upload_files=("${@}")
504 if [ "${login}" = "true" ]; then
505 # load before changing directory
510 if [ -n "${album_title}" ]; then
511 if [ "${login}" = "true" ]; then
512 response="$(curl -fsSL --stderr - \
513 -F "title=${album_title}" \
514 -H "Authorization: Bearer ${access_token}" \
515 https://api.imgur.com/3/album)"
517 response="$(curl -fsSL --stderr - \
518 -F "title=${album_title}" \
519 -H "Authorization: Client-ID ${imgur_anon_id}" \
520 https://api.imgur.com/3/album)"
522 if grep -Eq '"success":\s*true' <<<"${response}"; then # Album creation successful
523 echo "Album '${album_title}' successfully created"
524 album_id="$(grep -Eo '"id":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
525 del_id="$(grep -Eo '"deletehash":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
526 handle_album_creation_success "https://imgur.com/a/${album_id}" "${del_id}" "${album_title}"
528 if [ "${login}" = "false" ]; then
531 else # Album creation failed
532 err_msg="$(grep -Eo '"error":\s*"[^"]+"' <<<"${response}" | cut -d "\"" -f 4)"
533 test -z "${err_msg}" && err_msg="${response}"
534 handle_album_creation_error "${err_msg}" "${album_title}"
538 if [ -z "${upload_files[*]}" ]; then
542 for upload_file in "${upload_files[@]}"; do
544 if [ -z "${upload_file}" ]; then
545 cd "${file_dir}" || exit 1
547 # new filename with date
548 img_file="$(date +"${file_name_format}")"
549 take_screenshot "${img_file}"
551 # upload file instead of screenshot
552 img_file="${upload_file}"
556 #cd "$(dirname "$(realpath "${img_file}")")"
557 #img_file="$(realpath "${img_file}")"
559 # check if file exists
560 if ! [ -f "${img_file}" ]; then
561 echo "file '${img_file}' doesn't exist !"
566 # open image in editor if configured
567 if [ "${edit}" = "true" ]; then
568 edit_cmd=${edit_command//\%img/${img_file}}
569 echo "Opening editor '${edit_cmd}'"
570 if ! (eval "${edit_cmd}"); then
571 echo "Error for image '${img_file}': command '${edit_cmd}' failed, not uploading. For more information visit https://github.com/jomo/imgur-screenshot/wiki/Troubleshooting" | tee -a "${log_file}"
572 notify error "Something went wrong :(" "Information has been logged"
577 if [ "${login}" = "true" ]; then
578 upload_authenticated_image "${img_file}"
580 upload_anonymous_image "${img_file}"
583 # delete file if configured
584 if [ "${keep_file}" = "false" ] && [ -z "${1}" ]; then
585 echo "Deleting temp file ${file_dir}/${img_file}"
593 if [ "${check_update}" = "true" ]; then