diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000..c695a77
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,32 @@
+name: Bug Report
+description: File a bug report
+body:
+ - type: input
+ attributes:
+ label: Workflow version
+ description: Open the Workflow in Alfred Preferences → click [𝓧] → Workflow Version
+ validations:
+ required: true
+ - type: input
+ attributes:
+ label: Alfred version
+ description: In the top left corner of Alfred Preferences → General
+ validations:
+ required: true
+ - type: input
+ attributes:
+ label: macOS version
+ description: Click on the menubar → About This Mac → Overview
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: Debugger output
+ description: Open the Workflow in Alfred Preferences → click the bug icon → perform the failing action
+ render: alfred_debugger
+ validations:
+ required: true
+ - type: textarea
+ attributes:
+ label: More details
+ description: Explain what you did, what happened, and what you expected to happen
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a9b687
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 2021, Running with Crayons Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9b2805d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,21 @@
+# TinyPNG Alfred Workflow
+
+Compress images via TinyPNG
+
+## Instructions
+
+Use the File Action, File Filter, or External Trigger to compress PNG, JPEG, and WebP images via [TinyPNG](https://tinypng.com/).
+
+![](https://i.imgur.com/mlX713P.png)
+
+Before using the Workflow, [request an API key](https://tinypng.com/developers) and add it to the `api_key` Workflow Environment Variable.
+
+`backup_originals` takes a directory path to save the unmodified images. Use `~` to reference your home directory. Leave it empty to not save backups.
+
+## Download
+
+[Get the latest release.](https://github.com/alfredapp/tinypng-workflow/releases/latest/download/TinyPNG.alfredworkflow)
+
+## License
+
+3-Clause BSD
diff --git a/Workflow/icon.png b/Workflow/icon.png
new file mode 100644
index 0000000..29c1337
Binary files /dev/null and b/Workflow/icon.png differ
diff --git a/Workflow/info.plist b/Workflow/info.plist
new file mode 100644
index 0000000..9b31725
--- /dev/null
+++ b/Workflow/info.plist
@@ -0,0 +1,608 @@
+
+
+
+
+ bundleid
+ com.alfredapp.tinypng
+ connections
+
+ 392049EC-35AB-4C54-8A20-5565FBF67B32
+
+
+ destinationuid
+ F2C7428D-06C2-4B70-8C70-8348AE71B137
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ 72DE7DEE-B771-41EA-9A7C-F10F4F7A5DC5
+
+ 7EA1CA9C-97FC-4B89-999A-7CFE3D8CB0AE
+
+
+ destinationuid
+ F2C7428D-06C2-4B70-8C70-8348AE71B137
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ C22E0CBD-0683-4E3A-80E5-6532D8E5440E
+
+
+ destinationuid
+ 4D6432C7-2943-4DB6-A25F-B0AC57D3C6F2
+ modifiers
+ 0
+ modifiersubtext
+
+ sourceoutputuid
+ 2402FAA7-E693-486D-84EA-BF27BF8E4AFD
+ vitoclose
+
+
+
+ destinationuid
+ 6D56D157-3EBC-41F6-A030-1C9A9E4490DF
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ E4256C3E-6831-43F5-805C-F667A3D7E726
+
+
+ destinationuid
+ 72DE7DEE-B771-41EA-9A7C-F10F4F7A5DC5
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ destinationuid
+ C22E0CBD-0683-4E3A-80E5-6532D8E5440E
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ F2C7428D-06C2-4B70-8C70-8348AE71B137
+
+
+ destinationuid
+ DB3CBD80-5AFA-4CF7-97B8-A9AAB1651C88
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ destinationuid
+ E4256C3E-6831-43F5-805C-F667A3D7E726
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+ FD60CC76-3DD5-45D7-A184-5A5580E0790F
+
+
+ destinationuid
+ F2C7428D-06C2-4B70-8C70-8348AE71B137
+ modifiers
+ 0
+ modifiersubtext
+
+ vitoclose
+
+
+
+
+ createdby
+ Vítor Galvão
+ description
+ Compress images via TinyPNG
+ disabled
+
+ name
+ TinyPNG
+ objects
+
+
+ config
+
+ concurrently
+
+ escaping
+ 0
+ script
+ # THESE VARIABLES MUST BE SET. SEE THE ONEUPDATER README FOR AN EXPLANATION OF EACH.
+readonly remote_info_plist="https://raw.githubusercontent.com/alfredapp/tinypng-workflow/master/Workflow/info.plist"
+readonly workflow_url="alfredapp/tinypng-workflow"
+readonly download_type='github_release'
+readonly frequency_check='4'
+
+# FROM HERE ON, CODE SHOULD BE LEFT UNTOUCHED!
+function abort {
+ echo "${1}" >&2
+ exit 1
+}
+
+function url_exists {
+ curl --silent --location --output /dev/null --fail --range 0-0 "${1}"
+}
+
+function notification {
+ local -r notificator="$(find . -type d -name 'Notificator.app')"
+ if [[ -n "${notificator}" ]]; then
+ "${notificator}/Contents/Resources/Scripts/notificator" --message "${1}" --title "${alfred_workflow_name}" --subtitle 'A new version is available'
+ return
+ fi
+
+ local -r terminal_notifier="$(find . -type f -name 'terminal-notifier')"
+ if [[ -n "${terminal_notifier}" ]]; then
+ "${terminal_notifier}" -title "${alfred_workflow_name}" -subtitle 'A new version is available' -message "${1}"
+ return
+ fi
+
+ osascript -e "display notification \"${1}\" with title \"${alfred_workflow_name}\" subtitle \"A new version is available\""
+}
+
+# Local sanity checks
+readonly local_info_plist='info.plist'
+readonly local_version="$(/usr/libexec/PlistBuddy -c 'print version' "${local_info_plist}")"
+
+[[ -n "${local_version}" ]] || abort 'You need to set a workflow version in the configuration sheet.'
+[[ "${download_type}" =~ ^(direct|page|github_release)$ ]] || abort "'download_type' (${download_type}) needs to be one of 'direct', 'page', or 'github_release'."
+[[ "${frequency_check}" =~ ^[0-9]+$ ]] || abort "'frequency_check' (${frequency_check}) needs to be a number."
+
+# Check for updates
+if [[ $(find "${local_info_plist}" -mtime +"${frequency_check}"d) ]]; then
+ if ! url_exists "${remote_info_plist}"; then abort "'remote_info_plist' (${remote_info_plist}) appears to not be reachable."; fi # Remote sanity check
+
+ readonly tmp_file="$(mktemp)"
+ curl --silent --location --output "${tmp_file}" "${remote_info_plist}"
+ readonly remote_version="$(/usr/libexec/PlistBuddy -c 'print version' "${tmp_file}")"
+
+ if [[ "${local_version}" == "${remote_version}" ]]; then
+ touch "${local_info_plist}" # Reset timer by touching local file
+ exit 0
+ fi
+
+ if [[ "${download_type}" == 'page' ]]; then
+ notification 'Opening download page…'
+ open "${workflow_url}"
+ exit 0
+ fi
+
+ download_url="$([[ "${download_type}" == 'github_release' ]] && curl --silent "https://api.github.com/repos/${workflow_url}/releases/latest" | grep 'browser_download_url' | head -1 | sed -E 's/.*browser_download_url": "(.*)"/\1/' || echo "${workflow_url}")"
+
+ if url_exists "${download_url}"; then
+ notification 'Downloading and installing…'
+ curl --silent --location --output "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow" "${download_url}"
+ open "${HOME}/Downloads/${alfred_workflow_name}.alfredworkflow"
+ else
+ abort "'workflow_url' (${download_url}) appears to not be reachable."
+ fi
+fi
+ scriptargtype
+ 1
+ scriptfile
+
+ type
+ 0
+
+ type
+ alfred.workflow.action.script
+ uid
+ DB3CBD80-5AFA-4CF7-97B8-A9AAB1651C88
+ version
+ 2
+
+
+ config
+
+ concurrently
+
+ escaping
+ 102
+ script
+ # Constants
+readonly backup_dir="${backup_originals/#\~/${HOME}}/$(date -u +'%Y.%m.%d.%H%M%S')"
+
+# Helpers
+function get_json_key {
+ local -r json="${1}" # JSON string
+ local -r key="${2}" # Of the form ['first']['second'] or .first.second
+
+ local -r result="$(osascript -l 'JavaScript' -e "function run(argv) { return JSON.parse(argv[0])${key} }" "${json}")"
+
+ [[ -n "${key}" && -n "${result}" ]] || return 1 # If no valid result
+
+ echo -n "${result}"
+}
+
+function error {
+ echo "${1}" >&2 # For Alfred's debugger
+ echo "error|${1}" # For the notification and sound
+ exit 1
+}
+
+# Main
+# Abort if backups are desirable but cannot create directory
+if [[ -n "${backup_originals}" ]] && ! mkdir -p "${backup_dir}"; then
+ error "Could not create backups directory at ${backup_originals}!"
+fi
+
+# Loop over images
+for image in "${@}"; do
+ json_response="$(curl --silent --user api:"${api_key}" --data-binary @"${image}" -i 'https://api.tinify.com/shrink' | tail -1)"
+
+ # On error
+ if [[ -n "$(get_json_key "${json_response}" '.error')" ]]; then
+ error "$(get_json_key "${json_response}" '.message')"
+ fi
+
+ # Backup image
+ if [[ -d "${backup_dir}" ]]; then
+ cp "${image}" "${backup_dir}"
+ fi
+
+ # Save result
+ compressed_url="$(get_json_key "${json_response}" '.output.url')"
+ curl --silent "${compressed_url}" --output "${image}"
+done
+
+echo 'success|Finished compressing'
+ scriptargtype
+ 1
+ scriptfile
+
+ type
+ 0
+
+ type
+ alfred.workflow.action.script
+ uid
+ F2C7428D-06C2-4B70-8C70-8348AE71B137
+ version
+ 2
+
+
+ config
+
+ acceptsmulti
+ 1
+ filetypes
+
+ org.webmproject.webp
+ public.jpeg
+ public.png
+
+ name
+ TinyPNG Compression
+
+ type
+ alfred.workflow.trigger.action
+ uid
+ 7EA1CA9C-97FC-4B89-999A-7CFE3D8CB0AE
+ version
+ 1
+
+
+ config
+
+ lastpathcomponent
+
+ onlyshowifquerypopulated
+
+ removeextension
+
+ text
+ {var:split2}
+ title
+ TinyPNG
+
+ type
+ alfred.workflow.output.notification
+ uid
+ 72DE7DEE-B771-41EA-9A7C-F10F4F7A5DC5
+ version
+ 1
+
+
+ config
+
+ anchorfields
+
+ argumenttrimmode
+ 0
+ argumenttype
+ 0
+ daterange
+ 0
+ fields
+
+
+ field
+ kMDItemDisplayName
+ not
+
+ split
+
+ value
+ {query}
+ words
+
+
+
+ field
+ kMDItemAlternateNames
+ not
+
+ split
+
+ value
+ {query}
+ words
+
+
+
+ field
+ kMDItemFinderComment
+ not
+
+ split
+
+ value
+ {query}
+ words
+
+
+
+ includesystem
+
+ keyword
+ tinypng
+ limit
+ 0
+ runningsubtext
+
+ scopes
+
+ sortmode
+ 0
+ subtext
+ Filter for images to compress
+ title
+ TinyPNG Compression
+ types
+
+ org.webmproject.webp
+ public.jpeg
+ public.png
+
+ withspace
+
+
+ type
+ alfred.workflow.input.filefilter
+ uid
+ 392049EC-35AB-4C54-8A20-5565FBF67B32
+ version
+ 2
+
+
+ config
+
+ delimiter
+ |
+ trimarguments
+
+ variableprefix
+ split
+
+ type
+ alfred.workflow.utility.split
+ uid
+ E4256C3E-6831-43F5-805C-F667A3D7E726
+ version
+ 1
+
+
+ config
+
+ triggerid
+ compress
+
+ type
+ alfred.workflow.trigger.external
+ uid
+ FD60CC76-3DD5-45D7-A184-5A5580E0790F
+ version
+ 1
+
+
+ config
+
+ soundname
+ Glass
+ systemsound
+
+
+ type
+ alfred.workflow.output.playsound
+ uid
+ 4D6432C7-2943-4DB6-A25F-B0AC57D3C6F2
+ version
+ 1
+
+
+ config
+
+ conditions
+
+
+ inputstring
+ {var:split1}
+ matchcasesensitive
+
+ matchmode
+ 0
+ matchstring
+ success
+ outputlabel
+ Success
+ uid
+ 2402FAA7-E693-486D-84EA-BF27BF8E4AFD
+
+
+ elselabel
+ Error
+
+ type
+ alfred.workflow.utility.conditional
+ uid
+ C22E0CBD-0683-4E3A-80E5-6532D8E5440E
+ version
+ 1
+
+
+ config
+
+ soundname
+ Basso
+ systemsound
+
+
+ type
+ alfred.workflow.output.playsound
+ uid
+ 6D56D157-3EBC-41F6-A030-1C9A9E4490DF
+ version
+ 1
+
+
+ readme
+ Use the File Action, File Filter, or External Trigger to compress PNG, JPEG, and WebP images via [TinyPNG](https://tinypng.com/).
+
+![](https://i.imgur.com/mlX713P.png)
+
+Before using the Workflow, [request an API key](https://tinypng.com/developers) and add it to the `api_key` Workflow Environment Variable.
+
+`backup_originals` takes a directory path to save the unmodified images. Use `~` to reference your home directory. Leave it empty to not save backups.
+ uidata
+
+ 392049EC-35AB-4C54-8A20-5565FBF67B32
+
+ xpos
+ 35
+ ypos
+ 190
+
+ 4D6432C7-2943-4DB6-A25F-B0AC57D3C6F2
+
+ xpos
+ 575
+ ypos
+ 320
+
+ 6D56D157-3EBC-41F6-A030-1C9A9E4490DF
+
+ xpos
+ 575
+ ypos
+ 455
+
+ 72DE7DEE-B771-41EA-9A7C-F10F4F7A5DC5
+
+ xpos
+ 575
+ ypos
+ 190
+
+ 7EA1CA9C-97FC-4B89-999A-7CFE3D8CB0AE
+
+ xpos
+ 35
+ ypos
+ 40
+
+ C22E0CBD-0683-4E3A-80E5-6532D8E5440E
+
+ xpos
+ 455
+ ypos
+ 355
+
+ DB3CBD80-5AFA-4CF7-97B8-A9AAB1651C88
+
+ colorindex
+ 12
+ note
+ OneUpdater
+ xpos
+ 455
+ ypos
+ 40
+
+ E4256C3E-6831-43F5-805C-F667A3D7E726
+
+ xpos
+ 415
+ ypos
+ 220
+
+ F2C7428D-06C2-4B70-8C70-8348AE71B137
+
+ xpos
+ 235
+ ypos
+ 40
+
+ FD60CC76-3DD5-45D7-A184-5A5580E0790F
+
+ xpos
+ 35
+ ypos
+ 320
+
+
+ variables
+
+ api_key
+
+ backup_originals
+ /tmp/com.alfredapp.tinypng_backups
+
+ variablesdontexport
+
+ api_key
+
+ version
+ 2021.1
+ webaddress
+ https://github.com/alfredapp/tinypng-workflow
+
+