├── 15A2426A-6614-4325-9014-873CE2FCAEEE.png
├── 8C1ABC6C-1ED1-4959-BAE0-E1027F44CBEF.png
├── 9D05394A-A5F5-4A2B-8AA7-3C06528411D3.png
├── LICENSE
├── README.md
├── alfred_validate_cache.sh
├── cleanshot.sh
├── clipsaver.png
├── clock.png
├── cs.py
├── error.png
├── generic.png
├── get_image_hashes.py
├── grid.png
├── icon.png
├── info.plist
├── process_clip.bash
├── save_multi.sh
├── save_multi_time.sh
├── save_single.sh
└── wf_common.py
/15A2426A-6614-4325-9014-873CE2FCAEEE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/15A2426A-6614-4325-9014-873CE2FCAEEE.png
--------------------------------------------------------------------------------
/8C1ABC6C-1ED1-4959-BAE0-E1027F44CBEF.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/8C1ABC6C-1ED1-4959-BAE0-E1027F44CBEF.png
--------------------------------------------------------------------------------
/9D05394A-A5F5-4A2B-8AA7-3C06528411D3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/9D05394A-A5F5-4A2B-8AA7-3C06528411D3.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Luke Hamburg
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ClipSaver
2 |
3 | This workflow is triggered with the `cs` keyword by default. You can change the keyword if desired via the configuration panel.
4 |
5 | It presents images from Alfred's clipboard history via a Script Filter, and allows selecting images to be extracted, converted to a format of your choice (default is PNG) and placed in a folder of your choosing (default is on the Desktop, in a folder called `saved_clips`). You can optionally set the `save_to_current` environment variable to have the workflow save to the currently active Finder window.
6 |
7 | Upon success, the original clipboard entry as well as the .tiff from Alfred's database will be removed if you have `delete_after_convert` set to true (File List objects in the clipboard history will remain untouched).
8 |
9 | Some features require [CleanShot](https://cleanshot.com/) to be installed. If you don't have CleanShot, you can still use the other features of the workflow.
10 | - Holding ⇧shift while actioning from the Grid View will Pin (float) the selected images to the screen
11 | - Holding ⌥option will open the images in the Annotation (edit) tool.
12 |
13 | ## Grid View (Alfred 5.5 only)
14 |
15 | As of version 2.4, the workflow also supports Alfred 5.5's new "Grid View" layout for previewing and actioning images. You can drag and drop images out of the grid with the mouse. While in Grid mode, press ⌘0 to toggle a larger view. You can use the File Buffer to select multiple images and action them.
16 |
17 | > _If the File Buffer contains contents, they will be used instead of the "last selection"._
18 |
19 | ## Workflow Configuration
20 |
21 | All configuration is optional. The workflow should work "out of the box" without configuring any of these items. They are here for advanced users only.
22 |
23 | - `db_name` - override to specify a custom clipboard database file if needed
24 | - `db_path` - override the default clipboard database path
25 | - `dest_dir` where to save images if `save_to_current == false`, defaults to `~/Desktop/saved_clips`
26 | - `sf_clip_limit` (default: empty) an optional limit to constrain the number of results displayed in the Script Filter
27 | - `save_to_current` (default: false) - set to `true` if you want the workflow to put saved images in the directory of the "frontmost" Finder window
28 | - `reveal_after` (default: true) - set to `false` if you do not want the Finder to reveal the saved clips after the workflow runs.
29 | - `default_format` (default: png) - set to e.g. `jpg` etc. You can override per invocation by passing as an argument (use `sips --formats` to see all available formats)
30 | - `delete_after_convert` (default: false) - set to `true` if you want the source images deleted after successful conversion
31 |
32 | ## Usage Tips
33 |
34 | - Tap `⇧SHIFT` while navigating through Script Filter results to get a QuickLook preview of the image. While QL is displayed, you can use the Arrow keys to flip through results.
35 | - Tap `⌥OPTION` to show the path to the app that created that image in the subtitle
36 | - Hold `⌘CMD` when actioning the item to Reveal the original image in Alfred's db folder
37 | - Pass a number as an argument to the script to save X number of clips in bulk
38 | - Pass an integer followed by `s|m|h|d` (sec,min,hrs,days) e.g. `90m` to save the clips captured in the last 90 minutes
39 | - Pass an image format e.g. `jpeg` as an argument to override your default format
40 |
41 | ## Screenshot:
42 |
43 |
44 | ## Download:
45 | https://github.com/luckman212/alfred_clipsaver_workflow/releases/latest/
46 |
47 | ## Forum topic:
48 | https://www.alfredforum.com/topic/14400-clipsaver-save-images-from-clipboard-history-to-png-files/
49 |
--------------------------------------------------------------------------------
/alfred_validate_cache.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # cleans up orphan image files that no longer exist in Alfred's db
4 |
5 | if [[ -z $db_path ]]; then
6 | db_path='Library/Application Support/Alfred/Databases/'
7 | fi
8 |
9 | if [[ -z $db_name ]]; then
10 | db_name='clipboard.alfdb'
11 | fi
12 |
13 | orphan_dir=$HOME/Desktop/orphan_images
14 |
15 | function open_orphan_dir() {
16 | if [[ -d $orphan_dir ]]; then
17 | open -a Finder "$orphan_dir"
18 | fi
19 | }
20 |
21 | case $1 in
22 | -o|--open) #open dirs
23 | open -a Finder "$db_path/$db_name.data"
24 | open_orphan_dir
25 | exit
26 | ;;
27 | esac
28 |
29 | # populate array with list of image clips from database
30 | clipArray=()
31 | while IFS= read -r line; do clipArray+=("$line"); done < <(/usr/bin/sqlite3 "$db_path/$db_name" "SELECT dataHash FROM clipboard WHERE dataType = 1;")
32 |
33 | # loop thru all tiffs in database storage folder, and validate
34 | cd "$db_path/$db_name.data" || { echo "could not cd to db storage folder"; exit 1; }
35 |
36 | shopt -s nullglob # enable
37 | for i in *.tiff *.png ; do
38 | if [[ ! " ${clipArray[@]} " =~ " ${i} " ]]; then
39 | echo "orphan: $i"
40 | if [[ ! -d $orphan_dir ]]; then
41 | if ! mkdir -p "$orphan_dir"; then
42 | echo "failed to create $orphan_dir"
43 | exit 1
44 | fi
45 | fi
46 | mv "$i" "$orphan_dir/"
47 | fi
48 | done
49 | shopt -u nullglob # disable
50 |
51 | open_orphan_dir
52 |
--------------------------------------------------------------------------------
/cleanshot.sh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh --no-rcs
2 |
3 | # URI scheme: https://cleanshot.com/docs-api
4 |
5 | # ensure deps
6 | if ! hash pngpaste &>/dev/null; then brew install --quiet pngpaste; fi
7 |
8 | # workaround for CleanShot bug
9 | # open-annotate verb does not work for TIFFs (bugreport has been filed!)
10 | _convert_to_png() {
11 | temp_dir=${temp_dir:-/private/tmp/converted_images}
12 | if [[ ! -d ${temp_dir} ]]; then
13 | mkdir -p "${temp_dir}"
14 | else
15 | find "${temp_dir}" -type f -ctime +10m -delete 2>/dev/null
16 | fi
17 | for img_pathname in "$@"; do
18 | [[ -r ${img_pathname} ]] || continue
19 | [[ ${(L)img_pathname} == *.png ]] && continue
20 | img_basename=${img_pathname##*/}
21 | img_noext="${img_basename%.*}"
22 | out_fname="$temp_dir/${img_noext}.png"
23 | if /usr/bin/sips 2>&1 \
24 | --setProperty format png \
25 | --out "${out_fname}" \
26 | "${img_pathname}"; then
27 | EXCLUDE+=( "$img_pathname" )
28 | FILEARRAY+=( "$out_fname" )
29 | fi
30 | done
31 | }
32 |
33 | _process() {
34 | echo 1>&2 "processing: $1"
35 | open "cleanshot://${VERB}?filepath=$1"
36 | }
37 |
38 | if [[ -z $alfred_workflow_uid ]]; then
39 | echo "this script is meant to be used from within an Alfred workflow"
40 | exit
41 | fi
42 |
43 | #normalize input
44 | IFS=$'\t' read -A FILEARRAY <<<"$1"
45 | shift
46 | for a in $@ ; do
47 | FILEARRAY+=( $a )
48 | done
49 |
50 | action=${action:-edit}
51 | #echo 1>&2 "action: $action"
52 | EXCLUDE=()
53 |
54 | case "$action" in
55 | clipboard)
56 | imgfile='/private/tmp/clipboard.png'
57 | VERB='open-annotate'
58 | if /opt/homebrew/bin/pngpaste 2>/dev/null $imgfile ; then
59 | _process $imgfile
60 | else
61 | /usr/bin/afplay /System/Library/Sounds/Bottle.aiff
62 | fi
63 | exit
64 | ;;
65 | float|pin)
66 | VERB='pin'
67 | ;;
68 | edit|annotate)
69 | VERB='open-annotate'
70 | _convert_to_png "${FILEARRAY[@]}"
71 | ;;
72 | esac
73 |
74 | #about the zsh ${FOO:|BAR} syntax (array subtraction expression)
75 | #returns elements of $FILEARRAY that are not present in $EXCLUDE
76 | #https://zsh.sourceforge.io/Doc/Release/Expansion.html#Parameter-Expansion
77 | #https://chatgpt.com/c/177a7413-03a8-47a0-a3d4-92660eee188a
78 | for f in ${FILEARRAY:|EXCLUDE}; do
79 | _process "$f"
80 | done
81 |
--------------------------------------------------------------------------------
/clipsaver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/clipsaver.png
--------------------------------------------------------------------------------
/clock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/clock.png
--------------------------------------------------------------------------------
/cs.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import re
5 | import sys
6 | import json
7 | import sqlite3
8 | import plistlib
9 | import datetime as dt
10 | from contextlib import contextmanager
11 | from wf_common import *
12 | #from pathlib import Path
13 |
14 | @contextmanager
15 | def database(path):
16 | db = sqlite3.connect(path)
17 | yield db
18 | db.close()
19 |
20 | def append_item(fn, img, title, sub, srcapp, ctime):
21 | global uidSeed, fmt, items
22 | if not os.path.exists(img):
23 | return
24 | il = img.lower()
25 | if not any(i['arg'] == il for i in items):
26 | items.append({
27 | "uid": ''.join([ uidSeed, '.', fn ]),
28 | "variables": { "uidSeed": uidSeed },
29 | "title": title,
30 | "subtitle": f'{ctime} ↩ save as {fmt.upper()}',
31 | "arg": il,
32 | "type": "file:skipcheck",
33 | "variables": {
34 | "format": fmt.lower(),
35 | "dataHash": fn,
36 | "action": 'single'
37 | },
38 | "icon": { "path": img },
39 | "mods": {
40 | "ctrl": {
41 | "arg": img,
42 | "subtitle": f'{ctime} ↩ copy to clipboard',
43 | "variables": { "action": 'copy' }
44 | },
45 | "alt": {
46 | "arg": il,
47 | "subtitle": f'from: {sub}'
48 | },
49 | "cmd": {
50 | "arg": img,
51 | "subtitle": f'{ctime} ↩ reveal in Finder',
52 | "variables": { "action": 'reveal' }
53 | }
54 | },
55 | "quicklookurl": img
56 | })
57 |
58 | def listitems(fmt='png', num=1, since=None, human=None):
59 | if since and human:
60 | atleast = int(dt.datetime.now().timestamp()) - since - 978307200
61 | items.append({
62 | "title": f'↩ save time-filtered clips below (last {human}) as {fmt.upper()}',
63 | "arg": atleast,
64 | "icon": { "path": "clock.png" },
65 | "variables": {
66 | "action": 'multisave_time',
67 | "format": fmt.lower()
68 | },
69 | "quicklookurl": ''
70 | })
71 | else:
72 | atleast = 0
73 | if num > 1:
74 | items.append({
75 | "title": f'↩ save last {num} clips as {fmt.upper()}',
76 | "arg": num,
77 | "variables": {
78 | "action": 'multisave',
79 | "format": fmt.lower()
80 | },
81 | "quicklookurl": ''
82 | })
83 | with database(db_res) as db:
84 | #0=filename, 1=title, 2=src app, 3=time, 4=type (1=image from db,2=files)
85 | rows = db.execute("SELECT dataHash,item,apppath,ts,dataType from clipboard WHERE dataType IN (1,2) AND ts >= ? ORDER BY rowid DESC LIMIT ?", [atleast, sf_clip_limit])
86 | for r in rows:
87 | (fn, title, srcapp, ts, dtype) = r
88 | ts += 978307200
89 | ctime = dt.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M')
90 | img = os.path.join(i_path, fn)
91 | if dtype == 2:
92 | try:
93 | with open(img, "rb") as fp:
94 | plistobj = plistlib.load(fp)
95 | except:
96 | continue
97 | imgfiles = [i for i in plistobj if os.path.exists(i) and i.split(os.extsep)[-1].lower() in img_exts]
98 | for f in imgfiles:
99 | title = ' '.join([ "File:", f ])
100 | sub = f"File List {fn}"
101 | append_item(fn,f,title,sub,srcapp,ctime)
102 | else:
103 | sub = srcapp or "(unknown)"
104 | append_item(fn,img,title,sub,srcapp,ctime)
105 |
106 | try:
107 | args = sys.argv[1].split()
108 | except:
109 | args = []
110 | pass
111 |
112 | items = []
113 | arg = fmt = sr_n = sr_str = None
114 | num = 1
115 |
116 | # poor man's arg parser
117 | for a in args:
118 | try:
119 | num = int(a)
120 | continue
121 | except:
122 | pass
123 | try:
124 | sr = re.match(r'([0-9]+)([smhd])', a, re.IGNORECASE)
125 | sr_str = sr.group(0)
126 | sr_n = int(sr.group(1))
127 | sr_u = sr.group(2).lower()
128 | if sr_u == 'm':
129 | sr_n *= 60
130 | if sr_u == 'h':
131 | sr_n *= 3600
132 | if sr_u == 'd':
133 | sr_n *= 86400
134 | continue
135 | except:
136 | pass
137 | fmt = a.lower()
138 |
139 | if num < 1:
140 | num = 1
141 | if fmt is None or len(str(fmt)) < 3:
142 | fmt = default_format
143 | if fmt and fmt.upper() == 'JPG':
144 | fmt = 'jpeg'
145 |
146 | """
147 | print(f'fmt:{fmt}',file=sys.stderr)
148 | print(f'num:{num}',file=sys.stderr)
149 | print(f'sr_n:{sr_n}',file=sys.stderr)
150 | """
151 |
152 | listitems(fmt=fmt, num=num, since=sr_n, human=sr_str)
153 |
154 | if not items:
155 | items = [{
156 | "title": "No image clips found",
157 | "subtitle": "clipboard history may be empty",
158 | "icon": { "path": "error.png" },
159 | "valid": False
160 | }]
161 |
162 | variables = {
163 | 'uidSeed': uidSeed,
164 | 'delete_after_convert': delete_after_convert,
165 | 'save_to_current': save_to_current,
166 | 'db_name': db_name,
167 | 'db_path': db_path,
168 | 'dest_dir': dest_dir,
169 | 'default_format': default_format
170 | }
171 |
172 | items.insert(0, {
173 | "title": "Grid View",
174 | "subtitle": "Display results in enlarged Grid View",
175 | "arg": "",
176 | "icon": { "path": "grid.png" },
177 | "variables": { "action": "grid" }
178 | })
179 |
180 | output = {
181 | "variables": variables,
182 | "skipknowledge": True,
183 | "items": items
184 | }
185 |
186 | json.dump(output, sys.stdout)
187 |
--------------------------------------------------------------------------------
/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/error.png
--------------------------------------------------------------------------------
/generic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/generic.png
--------------------------------------------------------------------------------
/get_image_hashes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import os
4 | import sys
5 | import sqlite3
6 | import plistlib
7 | import datetime as dt
8 | from contextlib import contextmanager
9 | from wf_common import *
10 |
11 | @contextmanager
12 | def database(path):
13 | db = sqlite3.connect(path)
14 | yield db
15 | db.close()
16 |
17 | def get_image_hashes(atleast=0, num_clips=None):
18 | with database(db_res) as db:
19 | rows = db.execute("SELECT dataHash,dataType from clipboard WHERE dataType IN (1,2) AND ts >= ? ORDER BY rowid DESC", [atleast])
20 | rc = 0
21 | for r in rows:
22 | if num_clips and rc >= num_clips:
23 | break
24 | (fn, dtype) = r
25 | img = os.path.join(i_path, fn)
26 | if dtype == 2:
27 | try:
28 | with open(img, "rb") as fp:
29 | plistobj = plistlib.load(fp)
30 | except:
31 | continue
32 | imgfiles = [i for i in plistobj if os.path.exists(i) and i.split(os.extsep)[-1].lower() in img_exts]
33 | for f in imgfiles:
34 | rc += 1
35 | print(f)
36 | if num_clips and rc >= num_clips:
37 | break
38 | else:
39 | rc += 1
40 | print(fn)
41 | if num_clips and rc >= num_clips:
42 | break
43 |
44 | if __name__ == '__main__':
45 | args = sys.argv[1:]
46 | if args[0] == '--atleast':
47 | get_image_hashes(atleast=int(args[1]))
48 | elif args[0] == '--num_clips':
49 | get_image_hashes(num_clips=int(args[1]))
50 | else:
51 | exit(1)
52 |
--------------------------------------------------------------------------------
/grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/grid.png
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/luckman212/alfred_clipsaver_workflow/4960833959aa35c7d612f3a3cf1e8851672cc905/icon.png
--------------------------------------------------------------------------------
/info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bundleid
6 | com.alfredapp.clipsaver
7 | connections
8 |
9 | 0C8B45F4-FEAE-493C-8531-48AE675F5DE1
10 |
11 |
12 | destinationuid
13 | FF2B9F42-D9A9-42D6-ABDB-8691AC88D747
14 | modifiers
15 | 0
16 | modifiersubtext
17 |
18 | vitoclose
19 |
20 |
21 |
22 | 15A2426A-6614-4325-9014-873CE2FCAEEE
23 |
24 |
25 | destinationuid
26 | 4E6033E8-BC67-4836-B299-F64154D52F1A
27 | modifiers
28 | 0
29 | modifiersubtext
30 |
31 | vitoclose
32 |
33 |
34 |
35 | 2601B1DD-8EAB-4B3C-AEE2-5BFFE45ED5E5
36 |
37 |
38 | destinationuid
39 | CCD4FC35-EA9E-47A0-97D7-123CE12B9F47
40 | modifiers
41 | 0
42 | modifiersubtext
43 |
44 | vitoclose
45 |
46 |
47 |
48 | 2C3159FB-317C-4678-993E-1BBB9141095B
49 |
50 |
51 | destinationuid
52 | FF2B9F42-D9A9-42D6-ABDB-8691AC88D747
53 | modifiers
54 | 0
55 | modifiersubtext
56 |
57 | vitoclose
58 |
59 |
60 |
61 | 330B881A-5B6F-4415-B32E-4F62B9A7864E
62 |
63 |
64 | destinationuid
65 | 33CD6F02-65A3-4497-9C85-E38FDE97FC8D
66 | modifiers
67 | 0
68 | modifiersubtext
69 |
70 | vitoclose
71 |
72 |
73 |
74 | destinationuid
75 | AB178842-6707-4C5B-88D1-FC317394D8F9
76 | modifiers
77 | 1048576
78 | modifiersubtext
79 | Reveal
80 | vitoclose
81 |
82 |
83 |
84 | destinationuid
85 | A3FBF3CE-3218-4FC1-A8AC-9D056C1C948B
86 | modifiers
87 | 262144
88 | modifiersubtext
89 | Copy image to clipboard
90 | vitoclose
91 |
92 |
93 |
94 | destinationuid
95 | 4E6033E8-BC67-4836-B299-F64154D52F1A
96 | modifiers
97 | 131072
98 | modifiersubtext
99 | Float (Pin) this image using CleanShot
100 | vitoclose
101 |
102 |
103 |
104 | 33CD6F02-65A3-4497-9C85-E38FDE97FC8D
105 |
106 |
107 | destinationuid
108 | 519C25B7-FDE4-4EA5-AF9B-7460EEDC70CE
109 | modifiers
110 | 0
111 | modifiersubtext
112 |
113 | vitoclose
114 |
115 |
116 |
117 | 395E6EB2-E24E-42B0-891C-2AE7E16160F2
118 |
119 |
120 | destinationuid
121 | 7D984E52-7568-4C03-B70F-92EFA7067021
122 | modifiers
123 | 0
124 | modifiersubtext
125 |
126 | vitoclose
127 |
128 |
129 |
130 | 40ABDE7E-4F25-4424-94E4-9489906D568D
131 |
132 |
133 | destinationuid
134 | 4F366E5F-E737-440C-B9A3-FC859781F3DE
135 | disabled
136 |
137 | modifiers
138 | 0
139 | modifiersubtext
140 |
141 | vitoclose
142 |
143 |
144 |
145 | 510F5A09-C26E-49C1-8C80-4AE3C0411E36
146 |
147 |
148 | destinationuid
149 | 70A3AE8F-5B75-4470-89FC-73298C06596C
150 | modifiers
151 | 0
152 | modifiersubtext
153 |
154 | vitoclose
155 |
156 |
157 |
158 | destinationuid
159 | F47F1B80-82C9-49BA-9CF2-DBD52631E160
160 | disabled
161 |
162 | modifiers
163 | 0
164 | modifiersubtext
165 |
166 | vitoclose
167 |
168 |
169 |
170 | 519C25B7-FDE4-4EA5-AF9B-7460EEDC70CE
171 |
172 |
173 | destinationuid
174 | E3B4F7E0-C8D4-4C51-A42A-25FF8B353E84
175 | modifiers
176 | 0
177 | modifiersubtext
178 |
179 | sourceoutputuid
180 | 264D1DEE-5F29-4719-BF23-EE32ACFA308E
181 | vitoclose
182 |
183 |
184 |
185 | destinationuid
186 | C8999852-47D6-4E54-ACBA-03F7186AF696
187 | modifiers
188 | 0
189 | modifiersubtext
190 |
191 | sourceoutputuid
192 | AAF941DB-A595-4072-BD25-6BCC55A93AE2
193 | vitoclose
194 |
195 |
196 |
197 | 5CC78342-FA2A-459C-A878-E2DC3621154B
198 |
199 |
200 | destinationuid
201 | EE17BA6D-5748-4F20-BE74-602EB8B9849F
202 | modifiers
203 | 1048576
204 | modifiersubtext
205 | Reveal in Finder
206 | vitoclose
207 |
208 |
209 |
210 | destinationuid
211 | 330B881A-5B6F-4415-B32E-4F62B9A7864E
212 | modifiers
213 | 0
214 | modifiersubtext
215 |
216 | vitoclose
217 |
218 |
219 |
220 | destinationuid
221 | E30762D4-CD07-433B-8B2D-AD06552A2225
222 | modifiers
223 | 262144
224 | modifiersubtext
225 | Copy image to clipboard
226 | vitoclose
227 |
228 |
229 |
230 | destinationuid
231 | 0C8B45F4-FEAE-493C-8531-48AE675F5DE1
232 | modifiers
233 | 524288
234 | modifiersubtext
235 | Press ↵ to edit (annotate) selected images using CleanShot
236 | vitoclose
237 |
238 |
239 |
240 | destinationuid
241 | 2C3159FB-317C-4678-993E-1BBB9141095B
242 | modifiers
243 | 131072
244 | modifiersubtext
245 | Press ↵ to float (pin) selected images using CleanShot
246 | vitoclose
247 |
248 |
249 |
250 | 5E9A0126-8C80-41E7-AD49-590FE0777A91
251 |
252 |
253 | destinationuid
254 | A8AB7CFC-4F7B-436E-8EAD-C48244230F84
255 | modifiers
256 | 0
257 | modifiersubtext
258 |
259 | vitoclose
260 |
261 |
262 |
263 | 68014C82-BAD9-4CFC-A6C1-A10A58F10E79
264 |
265 |
266 | destinationuid
267 | 4E6033E8-BC67-4836-B299-F64154D52F1A
268 | modifiers
269 | 0
270 | modifiersubtext
271 |
272 | vitoclose
273 |
274 |
275 |
276 | 6C55B9DC-6A05-4228-AD55-2311DD125885
277 |
278 |
279 | destinationuid
280 | 40ABDE7E-4F25-4424-94E4-9489906D568D
281 | modifiers
282 | 0
283 | modifiersubtext
284 |
285 | vitoclose
286 |
287 |
288 |
289 | destinationuid
290 | C8999852-47D6-4E54-ACBA-03F7186AF696
291 | modifiers
292 | 0
293 | modifiersubtext
294 |
295 | vitoclose
296 |
297 |
298 |
299 | 70A3AE8F-5B75-4470-89FC-73298C06596C
300 |
301 |
302 | destinationuid
303 | A0D95AE6-FB7D-4C9D-BD5E-835FA2DC88F0
304 | modifiers
305 | 0
306 | modifiersubtext
307 |
308 | sourceoutputuid
309 | F6522B23-6C5B-497A-AE5B-26E913E3BD50
310 | vitoclose
311 |
312 |
313 |
314 | destinationuid
315 | 2601B1DD-8EAB-4B3C-AEE2-5BFFE45ED5E5
316 | modifiers
317 | 0
318 | modifiersubtext
319 |
320 | vitoclose
321 |
322 |
323 |
324 | 71A0EF86-42D5-4C0A-8656-CB6FEAD6F857
325 |
326 |
327 | destinationuid
328 | F8D8F9B0-A655-474F-9D3C-68866A8D85E6
329 | modifiers
330 | 0
331 | modifiersubtext
332 |
333 | vitoclose
334 |
335 |
336 |
337 | 7D984E52-7568-4C03-B70F-92EFA7067021
338 |
339 |
340 | destinationuid
341 | 5CC78342-FA2A-459C-A878-E2DC3621154B
342 | modifiers
343 | 0
344 | modifiersubtext
345 |
346 | vitoclose
347 |
348 |
349 |
350 | 9D05394A-A5F5-4A2B-8AA7-3C06528411D3
351 |
352 |
353 | destinationuid
354 | 68014C82-BAD9-4CFC-A6C1-A10A58F10E79
355 | modifiers
356 | 0
357 | modifiersubtext
358 |
359 | vitoclose
360 |
361 |
362 |
363 | A02D5916-1E4A-4207-80F9-AE36DF096158
364 |
365 |
366 | destinationuid
367 | A8AB7CFC-4F7B-436E-8EAD-C48244230F84
368 | modifiers
369 | 0
370 | modifiersubtext
371 |
372 | vitoclose
373 |
374 |
375 |
376 | A0D95AE6-FB7D-4C9D-BD5E-835FA2DC88F0
377 |
378 |
379 | destinationuid
380 | CCD4FC35-EA9E-47A0-97D7-123CE12B9F47
381 | modifiers
382 | 0
383 | modifiersubtext
384 |
385 | vitoclose
386 |
387 |
388 |
389 | A8AB7CFC-4F7B-436E-8EAD-C48244230F84
390 |
391 |
392 | destinationuid
393 | 6C55B9DC-6A05-4228-AD55-2311DD125885
394 | modifiers
395 | 0
396 | modifiersubtext
397 |
398 | sourceoutputuid
399 | 57CB54B3-8B1E-4F86-8390-AC626FA235F7
400 | vitoclose
401 |
402 |
403 |
404 | destinationuid
405 | E3B4F7E0-C8D4-4C51-A42A-25FF8B353E84
406 | modifiers
407 | 0
408 | modifiersubtext
409 |
410 | sourceoutputuid
411 | 2A316F1C-9FE9-43A3-A61D-F5CEFDF2420D
412 | vitoclose
413 |
414 |
415 |
416 | destinationuid
417 | C8999852-47D6-4E54-ACBA-03F7186AF696
418 | modifiers
419 | 0
420 | modifiersubtext
421 |
422 | sourceoutputuid
423 | AAF941DB-A595-4072-BD25-6BCC55A93AE2
424 | vitoclose
425 |
426 |
427 |
428 | C8999852-47D6-4E54-ACBA-03F7186AF696
429 |
430 |
431 | destinationuid
432 | 40ABDE7E-4F25-4424-94E4-9489906D568D
433 | modifiers
434 | 0
435 | modifiersubtext
436 |
437 | sourceoutputuid
438 | 49A1510D-1004-4FAD-B281-0A8703BD274F
439 | vitoclose
440 |
441 |
442 |
443 | destinationuid
444 | F34B0F25-D875-426D-8071-3FACA31F0AE1
445 | modifiers
446 | 0
447 | modifiersubtext
448 |
449 | sourceoutputuid
450 | 49A1510D-1004-4FAD-B281-0A8703BD274F
451 | vitoclose
452 |
453 |
454 |
455 | CCD4FC35-EA9E-47A0-97D7-123CE12B9F47
456 |
457 |
458 | destinationuid
459 | 4E6033E8-BC67-4836-B299-F64154D52F1A
460 | modifiers
461 | 0
462 | modifiersubtext
463 |
464 | vitoclose
465 |
466 |
467 |
468 | E30762D4-CD07-433B-8B2D-AD06552A2225
469 |
470 |
471 | destinationuid
472 | A3FBF3CE-3218-4FC1-A8AC-9D056C1C948B
473 | modifiers
474 | 0
475 | modifiersubtext
476 |
477 | vitoclose
478 |
479 |
480 |
481 | E3B4F7E0-C8D4-4C51-A42A-25FF8B353E84
482 |
483 |
484 | destinationuid
485 | 40ABDE7E-4F25-4424-94E4-9489906D568D
486 | modifiers
487 | 0
488 | modifiersubtext
489 |
490 | vitoclose
491 |
492 |
493 |
494 | EE17BA6D-5748-4F20-BE74-602EB8B9849F
495 |
496 |
497 | destinationuid
498 | AB178842-6707-4C5B-88D1-FC317394D8F9
499 | modifiers
500 | 0
501 | modifiersubtext
502 |
503 | vitoclose
504 |
505 |
506 |
507 | F34B0F25-D875-426D-8071-3FACA31F0AE1
508 |
509 | F8D8F9B0-A655-474F-9D3C-68866A8D85E6
510 |
511 |
512 | destinationuid
513 | A02D5916-1E4A-4207-80F9-AE36DF096158
514 | modifiers
515 | 0
516 | modifiersubtext
517 |
518 | sourceoutputuid
519 | B3345938-CA67-4E6A-B476-62E1ADB3B7EF
520 | vitoclose
521 |
522 |
523 |
524 | destinationuid
525 | 5E9A0126-8C80-41E7-AD49-590FE0777A91
526 | modifiers
527 | 0
528 | modifiersubtext
529 |
530 | sourceoutputuid
531 | 0D12874B-03FB-467B-BCBB-6BECAB99484A
532 | vitoclose
533 |
534 |
535 |
536 | destinationuid
537 | 33CD6F02-65A3-4497-9C85-E38FDE97FC8D
538 | modifiers
539 | 0
540 | modifiersubtext
541 |
542 | sourceoutputuid
543 | 133259D6-46D2-493B-9A52-1D6DA9ACA303
544 | vitoclose
545 |
546 |
547 |
548 | destinationuid
549 | EE17BA6D-5748-4F20-BE74-602EB8B9849F
550 | modifiers
551 | 0
552 | modifiersubtext
553 |
554 | sourceoutputuid
555 | 951960C5-0D7C-4BE6-8488-FE4C72278C90
556 | vitoclose
557 |
558 |
559 |
560 | destinationuid
561 | 395E6EB2-E24E-42B0-891C-2AE7E16160F2
562 | modifiers
563 | 0
564 | modifiersubtext
565 |
566 | sourceoutputuid
567 | 2F385C67-1D18-4D25-8AA7-EF492F9A4E7B
568 | vitoclose
569 |
570 |
571 |
572 | destinationuid
573 | E30762D4-CD07-433B-8B2D-AD06552A2225
574 | modifiers
575 | 0
576 | modifiersubtext
577 |
578 | sourceoutputuid
579 | B9B2E13B-5FFF-4E6B-98AA-4A849E116285
580 | vitoclose
581 |
582 |
583 |
584 | FF2B9F42-D9A9-42D6-ABDB-8691AC88D747
585 |
586 |
587 | destinationuid
588 | 510F5A09-C26E-49C1-8C80-4AE3C0411E36
589 | modifiers
590 | 0
591 | modifiersubtext
592 |
593 | vitoclose
594 |
595 |
596 |
597 |
598 | createdby
599 | luckman212
600 | description
601 | Saves images from your clipboard history
602 | disabled
603 |
604 | name
605 | ClipSaver
606 | objects
607 |
608 |
609 | config
610 |
611 | concurrently
612 |
613 | escaping
614 | 0
615 | script
616 |
617 | scriptargtype
618 | 1
619 | scriptfile
620 | save_multi_time.sh
621 | type
622 | 8
623 |
624 | type
625 | alfred.workflow.action.script
626 | uid
627 | A02D5916-1E4A-4207-80F9-AE36DF096158
628 | version
629 | 2
630 |
631 |
632 | config
633 |
634 | lastpathcomponent
635 |
636 | onlyshowifquerypopulated
637 |
638 | removeextension
639 |
640 | text
641 | {var:errmsg}
642 | title
643 | Warning
644 |
645 | type
646 | alfred.workflow.output.notification
647 | uid
648 | 6C55B9DC-6A05-4228-AD55-2311DD125885
649 | version
650 | 1
651 |
652 |
653 | config
654 |
655 | concurrently
656 |
657 | escaping
658 | 0
659 | script
660 |
661 | scriptargtype
662 | 1
663 | scriptfile
664 | save_multi.sh
665 | type
666 | 8
667 |
668 | type
669 | alfred.workflow.action.script
670 | uid
671 | 5E9A0126-8C80-41E7-AD49-590FE0777A91
672 | version
673 | 2
674 |
675 |
676 | config
677 |
678 | conditions
679 |
680 |
681 | inputstring
682 | {var:exitcode}
683 | matchcasesensitive
684 |
685 | matchmode
686 | 0
687 | matchstring
688 | 1
689 | outputlabel
690 | warning
691 | uid
692 | 57CB54B3-8B1E-4F86-8390-AC626FA235F7
693 |
694 |
695 | inputstring
696 | {var:exitcode}
697 | matchcasesensitive
698 |
699 | matchmode
700 | 2
701 | matchstring
702 | 1
703 | outputlabel
704 | failure
705 | uid
706 | 2A316F1C-9FE9-43A3-A61D-F5CEFDF2420D
707 |
708 |
709 | inputstring
710 | {var:exitcode}
711 | matchcasesensitive
712 |
713 | matchmode
714 | 0
715 | matchstring
716 | 0
717 | outputlabel
718 | success
719 | uid
720 | AAF941DB-A595-4072-BD25-6BCC55A93AE2
721 |
722 |
723 | elselabel
724 |
725 | hideelse
726 |
727 |
728 | type
729 | alfred.workflow.utility.conditional
730 | uid
731 | A8AB7CFC-4F7B-436E-8EAD-C48244230F84
732 | version
733 | 1
734 |
735 |
736 | config
737 |
738 | conditions
739 |
740 |
741 | inputstring
742 | {var:action}
743 | matchcasesensitive
744 |
745 | matchmode
746 | 0
747 | matchstring
748 | multisave_time
749 | outputlabel
750 | multisave_time
751 | uid
752 | B3345938-CA67-4E6A-B476-62E1ADB3B7EF
753 |
754 |
755 | inputstring
756 | {var:action}
757 | matchcasesensitive
758 |
759 | matchmode
760 | 0
761 | matchstring
762 | multisave
763 | outputlabel
764 | multisave
765 | uid
766 | 0D12874B-03FB-467B-BCBB-6BECAB99484A
767 |
768 |
769 | inputstring
770 | {var:action}
771 | matchcasesensitive
772 |
773 | matchmode
774 | 0
775 | matchstring
776 | single
777 | outputlabel
778 | single
779 | uid
780 | 133259D6-46D2-493B-9A52-1D6DA9ACA303
781 |
782 |
783 | inputstring
784 | {var:action}
785 | matchcasesensitive
786 |
787 | matchmode
788 | 0
789 | matchstring
790 | reveal
791 | outputlabel
792 | reveal
793 | uid
794 | 951960C5-0D7C-4BE6-8488-FE4C72278C90
795 |
796 |
797 | inputstring
798 | {var:action}
799 | matchcasesensitive
800 |
801 | matchmode
802 | 0
803 | matchstring
804 | copy
805 | outputlabel
806 | copy
807 | uid
808 | B9B2E13B-5FFF-4E6B-98AA-4A849E116285
809 |
810 |
811 | inputstring
812 | {var:action}
813 | matchcasesensitive
814 |
815 | matchmode
816 | 0
817 | matchstring
818 | grid
819 | outputlabel
820 | grid
821 | uid
822 | 2F385C67-1D18-4D25-8AA7-EF492F9A4E7B
823 |
824 |
825 | elselabel
826 |
827 | hideelse
828 |
829 |
830 | type
831 | alfred.workflow.utility.conditional
832 | uid
833 | F8D8F9B0-A655-474F-9D3C-68866A8D85E6
834 | version
835 | 1
836 |
837 |
838 | config
839 |
840 | alfredfiltersresults
841 |
842 | alfredfiltersresultsmatchmode
843 | 2
844 | argumenttreatemptyqueryasnil
845 |
846 | argumenttrimmode
847 | 0
848 | argumenttype
849 | 1
850 | escaping
851 | 0
852 | keyword
853 | {var:kw_trigger}
854 | queuedelaycustom
855 | 3
856 | queuedelayimmediatelyinitially
857 |
858 | queuedelaymode
859 | 0
860 | queuemode
861 | 1
862 | runningsubtext
863 | Fetching clipboard items
864 | script
865 |
866 | scriptargtype
867 | 1
868 | scriptfile
869 | cs.py
870 | subtext
871 |
872 | title
873 | Save images from clipboard history
874 | type
875 | 8
876 | withspace
877 |
878 |
879 | type
880 | alfred.workflow.input.scriptfilter
881 | uid
882 | 71A0EF86-42D5-4C0A-8656-CB6FEAD6F857
883 | version
884 | 3
885 |
886 |
887 | config
888 |
889 | lastpathcomponent
890 |
891 | onlyshowifquerypopulated
892 |
893 | removeextension
894 |
895 | text
896 | Clipboard history image conversion failed {var:errmsg}
897 | title
898 | Error
899 |
900 | type
901 | alfred.workflow.output.notification
902 | uid
903 | E3B4F7E0-C8D4-4C51-A42A-25FF8B353E84
904 | version
905 | 1
906 |
907 |
908 | config
909 |
910 | concurrently
911 |
912 | escaping
913 | 0
914 | script
915 |
916 | scriptargtype
917 | 1
918 | scriptfile
919 | save_single.sh
920 | type
921 | 8
922 |
923 | type
924 | alfred.workflow.action.script
925 | uid
926 | 33CD6F02-65A3-4497-9C85-E38FDE97FC8D
927 | version
928 | 2
929 |
930 |
931 | config
932 |
933 | argument
934 | '{query}', {variables}
935 | cleardebuggertext
936 |
937 | processoutputs
938 |
939 |
940 | type
941 | alfred.workflow.utility.debug
942 | uid
943 | 4F366E5F-E737-440C-B9A3-FC859781F3DE
944 | version
945 | 1
946 |
947 |
948 | type
949 | alfred.workflow.utility.junction
950 | uid
951 | 40ABDE7E-4F25-4424-94E4-9489906D568D
952 | version
953 | 1
954 |
955 |
956 | config
957 |
958 | conditions
959 |
960 |
961 | inputstring
962 | {var:exitcode}
963 | matchcasesensitive
964 |
965 | matchmode
966 | 2
967 | matchstring
968 | 0
969 | outputlabel
970 | failure
971 | uid
972 | 264D1DEE-5F29-4719-BF23-EE32ACFA308E
973 |
974 |
975 | inputstring
976 | {var:exitcode}
977 | matchcasesensitive
978 |
979 | matchmode
980 | 0
981 | matchstring
982 | 0
983 | outputlabel
984 | success
985 | uid
986 | AAF941DB-A595-4072-BD25-6BCC55A93AE2
987 |
988 |
989 | elselabel
990 |
991 | hideelse
992 |
993 |
994 | type
995 | alfred.workflow.utility.conditional
996 | uid
997 | 519C25B7-FDE4-4EA5-AF9B-7460EEDC70CE
998 | version
999 | 1
1000 |
1001 |
1002 | config
1003 |
1004 | openwith
1005 | /System/Library/CoreServices/Finder.app
1006 | sourcefile
1007 | {var:dest_dir}
1008 |
1009 | type
1010 | alfred.workflow.action.openfile
1011 | uid
1012 | F34B0F25-D875-426D-8071-3FACA31F0AE1
1013 | version
1014 | 3
1015 |
1016 |
1017 | config
1018 |
1019 | conditions
1020 |
1021 |
1022 | inputstring
1023 | {var:reveal_after}
1024 | matchcasesensitive
1025 |
1026 | matchmode
1027 | 5
1028 | matchstring
1029 |
1030 | outputlabel
1031 | reveal
1032 | uid
1033 | 49A1510D-1004-4FAD-B281-0A8703BD274F
1034 |
1035 |
1036 | elselabel
1037 |
1038 | hideelse
1039 |
1040 |
1041 | type
1042 | alfred.workflow.utility.conditional
1043 | uid
1044 | C8999852-47D6-4E54-ACBA-03F7186AF696
1045 | version
1046 | 1
1047 |
1048 |
1049 | config
1050 |
1051 | path
1052 | {query}
1053 |
1054 | type
1055 | alfred.workflow.action.revealfile
1056 | uid
1057 | AB178842-6707-4C5B-88D1-FC317394D8F9
1058 | version
1059 | 1
1060 |
1061 |
1062 | type
1063 | alfred.workflow.utility.junction
1064 | uid
1065 | EE17BA6D-5748-4F20-BE74-602EB8B9849F
1066 | version
1067 | 1
1068 |
1069 |
1070 | config
1071 |
1072 | imageresizemode
1073 | 0
1074 | stackview
1075 |
1076 |
1077 | type
1078 | alfred.workflow.userinterface.image
1079 | uid
1080 | 330B881A-5B6F-4415-B32E-4F62B9A7864E
1081 | version
1082 | 1
1083 |
1084 |
1085 | config
1086 |
1087 | columncount
1088 | 4
1089 | filterable
1090 |
1091 | fixedorder
1092 |
1093 | imageaspect
1094 | 0
1095 | inputfile
1096 |
1097 | inputtype
1098 | 0
1099 | loadingtext
1100 |
1101 | showsubtitles
1102 |
1103 | showtitles
1104 |
1105 | subtitlesinfooter
1106 |
1107 | titlesinfooter
1108 |
1109 |
1110 | type
1111 | alfred.workflow.userinterface.grid
1112 | uid
1113 | 5CC78342-FA2A-459C-A878-E2DC3621154B
1114 | version
1115 | 1
1116 |
1117 |
1118 | config
1119 |
1120 | concurrently
1121 |
1122 | escaping
1123 | 102
1124 | script
1125 | /usr/bin/sqlite3 \
1126 | "${HOME}/Library/Application Support/Alfred/Databases/clipboard.alfdb" \
1127 | ".parameter set :img_dir '~/Library/Application Support/Alfred/Databases/clipboard.alfdb.data/'" \
1128 | "SELECT JSON_OBJECT('items', JSON_GROUP_ARRAY(JSON_OBJECT(
1129 | -- 978307200 is timeIntervalSinceReferenceDate in Unix time
1130 | 'title', strftime('%Y/%m/%d %H:%M:%S', datetime(ts + 978307200, 'unixepoch')),
1131 | 'subtitle', app,
1132 | 'arg', :img_dir || datahash,
1133 | 'match', app,
1134 | 'type', 'file:skipcheck',
1135 | 'icon', JSON_OBJECT('path', :img_dir || datahash))))
1136 | AS JSON_RESULT FROM (
1137 | SELECT ts, app, datahash
1138 | FROM clipboard
1139 | WHERE dataType IS 1
1140 | ORDER BY ts
1141 | DESC)"
1142 | scriptargtype
1143 | 1
1144 | scriptfile
1145 |
1146 | type
1147 | 11
1148 |
1149 | type
1150 | alfred.workflow.action.script
1151 | uid
1152 | 395E6EB2-E24E-42B0-891C-2AE7E16160F2
1153 | version
1154 | 2
1155 |
1156 |
1157 | config
1158 |
1159 | tasksettings
1160 |
1161 | taskuid
1162 | com.alfredapp.automation.core/macOS/clipboard.set.image.data
1163 |
1164 | type
1165 | alfred.workflow.automation.task
1166 | uid
1167 | A3FBF3CE-3218-4FC1-A8AC-9D056C1C948B
1168 | version
1169 | 1
1170 |
1171 |
1172 | config
1173 |
1174 | json
1175 | {
1176 | "alfredworkflow" : {
1177 | "arg": "{query}",
1178 | "config" : {
1179 | "columncount" : "{var:grid_size}"
1180 | }
1181 | }
1182 | }
1183 |
1184 | type
1185 | alfred.workflow.utility.json
1186 | uid
1187 | 7D984E52-7568-4C03-B70F-92EFA7067021
1188 | version
1189 | 1
1190 |
1191 |
1192 | config
1193 |
1194 | acceptsfiles
1195 |
1196 | acceptsmulti
1197 | 1
1198 | acceptstext
1199 |
1200 | acceptsurls
1201 |
1202 | name
1203 | Edit in CleanShot
1204 |
1205 | type
1206 | alfred.workflow.trigger.universalaction
1207 | uid
1208 | 15A2426A-6614-4325-9014-873CE2FCAEEE
1209 | version
1210 | 1
1211 |
1212 |
1213 | type
1214 | alfred.workflow.utility.junction
1215 | uid
1216 | E30762D4-CD07-433B-8B2D-AD06552A2225
1217 | version
1218 | 1
1219 |
1220 |
1221 | config
1222 |
1223 | concurrently
1224 |
1225 | escaping
1226 | 0
1227 | script
1228 |
1229 | scriptargtype
1230 | 1
1231 | scriptfile
1232 | ~/Sync/Scripts/cleanshot.sh
1233 | type
1234 | 8
1235 |
1236 | type
1237 | alfred.workflow.action.script
1238 | uid
1239 | 4E6033E8-BC67-4836-B299-F64154D52F1A
1240 | version
1241 | 2
1242 |
1243 |
1244 | config
1245 |
1246 | json
1247 | {
1248 | "alfredworkflow" : {
1249 | "arg" : "",
1250 | "config" : {
1251 | },
1252 | "variables" : {
1253 | "selection": "{query}",
1254 | "buffer": "",
1255 | "action": "edit"
1256 | }
1257 | }
1258 | }
1259 |
1260 | type
1261 | alfred.workflow.utility.json
1262 | uid
1263 | 0C8B45F4-FEAE-493C-8531-48AE675F5DE1
1264 | version
1265 | 1
1266 |
1267 |
1268 | config
1269 |
1270 | addfilestobuffer
1271 |
1272 | clearbuffer
1273 |
1274 | outputtype
1275 | 0
1276 |
1277 | type
1278 | alfred.workflow.action.buffer
1279 | uid
1280 | FF2B9F42-D9A9-42D6-ABDB-8691AC88D747
1281 | version
1282 | 1
1283 |
1284 |
1285 | type
1286 | alfred.workflow.utility.junction
1287 | uid
1288 | CCD4FC35-EA9E-47A0-97D7-123CE12B9F47
1289 | version
1290 | 1
1291 |
1292 |
1293 | config
1294 |
1295 | json
1296 | { "alfredworkflow" : { "arg" : "{var:selection}" } }
1297 |
1298 | type
1299 | alfred.workflow.utility.json
1300 | uid
1301 | A0D95AE6-FB7D-4C9D-BD5E-835FA2DC88F0
1302 | version
1303 | 1
1304 |
1305 |
1306 | config
1307 |
1308 | conditions
1309 |
1310 |
1311 | inputstring
1312 | {var:buffer}
1313 | matchcasesensitive
1314 |
1315 | matchmode
1316 | 6
1317 | matchstring
1318 |
1319 | outputlabel
1320 | selection
1321 | uid
1322 | F6522B23-6C5B-497A-AE5B-26E913E3BD50
1323 |
1324 |
1325 | elselabel
1326 | buffer
1327 | hideelse
1328 |
1329 |
1330 | type
1331 | alfred.workflow.utility.conditional
1332 | uid
1333 | 70A3AE8F-5B75-4470-89FC-73298C06596C
1334 | version
1335 | 1
1336 |
1337 |
1338 | config
1339 |
1340 | json
1341 | {
1342 | "alfredworkflow" : {
1343 | "arg" : "",
1344 | "config" : {
1345 | },
1346 | "variables" : {
1347 | "buffer": "{query}"
1348 | }
1349 | }
1350 | }
1351 |
1352 | type
1353 | alfred.workflow.utility.json
1354 | uid
1355 | 510F5A09-C26E-49C1-8C80-4AE3C0411E36
1356 | version
1357 | 1
1358 |
1359 |
1360 | config
1361 |
1362 | argument
1363 | {query}
1364 | passthroughargument
1365 |
1366 | variables
1367 |
1368 | action
1369 | pin
1370 |
1371 |
1372 | type
1373 | alfred.workflow.utility.argument
1374 | uid
1375 | 68014C82-BAD9-4CFC-A6C1-A10A58F10E79
1376 | version
1377 | 1
1378 |
1379 |
1380 | config
1381 |
1382 | json
1383 | { "alfredworkflow" : { "arg" : "{var:buffer}" } }
1384 |
1385 | type
1386 | alfred.workflow.utility.json
1387 | uid
1388 | 2601B1DD-8EAB-4B3C-AEE2-5BFFE45ED5E5
1389 | version
1390 | 1
1391 |
1392 |
1393 | config
1394 |
1395 | acceptsfiles
1396 |
1397 | acceptsmulti
1398 | 1
1399 | acceptstext
1400 |
1401 | acceptsurls
1402 |
1403 | name
1404 | Float (Pin) image on screen
1405 |
1406 | type
1407 | alfred.workflow.trigger.universalaction
1408 | uid
1409 | 9D05394A-A5F5-4A2B-8AA7-3C06528411D3
1410 | version
1411 | 1
1412 |
1413 |
1414 | config
1415 |
1416 | json
1417 | {
1418 | "alfredworkflow" : {
1419 | "arg" : "",
1420 | "config" : {
1421 | },
1422 | "variables" : {
1423 | "selection": "{query}",
1424 | "buffer": "",
1425 | "action": "float"
1426 | }
1427 | }
1428 | }
1429 |
1430 | type
1431 | alfred.workflow.utility.json
1432 | uid
1433 | 2C3159FB-317C-4678-993E-1BBB9141095B
1434 | version
1435 | 1
1436 |
1437 |
1438 | config
1439 |
1440 | argument
1441 | '{query}', {variables}
1442 | cleardebuggertext
1443 |
1444 | processoutputs
1445 |
1446 |
1447 | type
1448 | alfred.workflow.utility.debug
1449 | uid
1450 | F47F1B80-82C9-49BA-9CF2-DBD52631E160
1451 | version
1452 | 1
1453 |
1454 |
1455 | readme
1456 | # ClipSaver
1457 |
1458 | This workflow is triggered with the `cs` keyword by default. You can change the keyword if desired via the configuration window.
1459 |
1460 | It presents images from Alfred's clipboard history via a Script Filter, and allows selecting an image to be extracted, converted to a format of your choice (default is PNG) and placed in a folder of your choosing (default is on the Desktop, in a folder called `saved_clips`). You can optionally set the `save_to_current` environment variable to have the workflow save to the currently active Finder window.
1461 |
1462 | Upon success, the original clipboard entry as well as the .tiff from Alfred's database will be removed if you have `delete_after_convert` set to true (File List objects in the clipboard history will remain untouched).
1463 |
1464 | Some features require [CleanShot](https://cleanshot.com/) to be installed. If you don't have CleanShot, you can still use the other features of the workflow.
1465 | - Holding ⇧shift while actioning from the Grid View will Pin (float) the selected images to the screen
1466 | - Holding ⌥option will open the images in the Annotation (edit) tool.
1467 |
1468 | ## Grid View (Alfred 5.5 only)
1469 |
1470 | As of version 2.4, the workflow also supports Alfred 5.5's new "Grid View" layout for previewing and actioning images. You can drag and drop images out of the grid with the mouse. While in Grid mode, press ⌘0 to toggle a larger view. You can use the File Buffer to select multiple images and action them.
1471 |
1472 | > _If the File Buffer contains contents, they will be used instead of the "last selection"._
1473 |
1474 | ## Workflow Configuration
1475 |
1476 | All configuration is optional. The workflow should work "out of the box" without configuring any of these items. They are here for advanced users only.
1477 |
1478 | - `db_name` - override to specify a custom clipboard database file if needed
1479 | - `db_path` - override the default clipboard database path
1480 | - `dest_dir` where to save images if `save_to_current == false`, defaults to `~/Desktop/saved_clips`
1481 | - `sf_clip_limit` (default: empty) an optional limit to constrain the number of results displayed in the Script Filter
1482 | - `save_to_current` (default: false) - set to `true` if you want the workflow to put saved images in the directory of the "frontmost" Finder window
1483 | - `reveal_after` (default: true) - set to `false` if you do not want the Finder to reveal the saved clips after the workflow runs.
1484 | - `default_format` (default: png) - set to e.g. `jpg` etc. You can override per invocation by passing as an argument (use `sips --formats` to see all available formats)
1485 | - `delete_after_convert` (default: false) - set to `true` if you want the source images deleted after successful conversion
1486 |
1487 | ## Usage Tips
1488 |
1489 | - Tap `⇧SHIFT` while navigating through Script Filter results to get a QuickLook preview of the image. While QL is displayed, you can use the Arrow keys to flip through results.
1490 | - Tap `⌥OPTION` to show the path to the app that created that image in the subtitle
1491 | - Hold `⌘CMD` when actioning the item to Reveal the original image in Alfred's db folder
1492 | - Pass a number as an argument to the script to save X number of clips in bulk
1493 | - Pass an integer followed by `s|m|h|d` (sec,min,hrs,days) e.g. `90m` to save the clips captured in the last 90 minutes
1494 | - Pass an image format e.g. `jpeg` as an argument to override your default format
1495 |
1496 | ## Screenshot:
1497 | 
1498 |
1499 | ## Download:
1500 | https://github.com/luckman212/alfred_clipsaver_workflow/releases/latest/
1501 |
1502 | ## Forum topic:
1503 | https://www.alfredforum.com/topic/14400-clipsaver-save-images-from-clipboard-history-to-png-files/
1504 | uidata
1505 |
1506 | 0C8B45F4-FEAE-493C-8531-48AE675F5DE1
1507 |
1508 | note
1509 | edit
1510 | xpos
1511 | 435
1512 | ypos
1513 | 860
1514 |
1515 | 15A2426A-6614-4325-9014-873CE2FCAEEE
1516 |
1517 | xpos
1518 | 1030
1519 | ypos
1520 | 705
1521 |
1522 | 2601B1DD-8EAB-4B3C-AEE2-5BFFE45ED5E5
1523 |
1524 | xpos
1525 | 910
1526 | ypos
1527 | 935
1528 |
1529 | 2C3159FB-317C-4678-993E-1BBB9141095B
1530 |
1531 | note
1532 | float
1533 | xpos
1534 | 435
1535 | ypos
1536 | 940
1537 |
1538 | 330B881A-5B6F-4415-B32E-4F62B9A7864E
1539 |
1540 | xpos
1541 | 690
1542 | ypos
1543 | 580
1544 |
1545 | 33CD6F02-65A3-4497-9C85-E38FDE97FC8D
1546 |
1547 | note
1548 | save_single
1549 | xpos
1550 | 575
1551 | ypos
1552 | 270
1553 |
1554 | 395E6EB2-E24E-42B0-891C-2AE7E16160F2
1555 |
1556 | xpos
1557 | 210
1558 | ypos
1559 | 580
1560 |
1561 | 40ABDE7E-4F25-4424-94E4-9489906D568D
1562 |
1563 | xpos
1564 | 1175
1565 | ypos
1566 | 285
1567 |
1568 | 4E6033E8-BC67-4836-B299-F64154D52F1A
1569 |
1570 | note
1571 | cleanshot actions
1572 | xpos
1573 | 1215
1574 | ypos
1575 | 825
1576 |
1577 | 4F366E5F-E737-440C-B9A3-FC859781F3DE
1578 |
1579 | note
1580 | enable dotted line to debug
1581 | xpos
1582 | 1315
1583 | ypos
1584 | 285
1585 |
1586 | 510F5A09-C26E-49C1-8C80-4AE3C0411E36
1587 |
1588 | xpos
1589 | 700
1590 | ypos
1591 | 900
1592 |
1593 | 519C25B7-FDE4-4EA5-AF9B-7460EEDC70CE
1594 |
1595 | xpos
1596 | 740
1597 | ypos
1598 | 290
1599 |
1600 | 5CC78342-FA2A-459C-A878-E2DC3621154B
1601 |
1602 | xpos
1603 | 450
1604 | ypos
1605 | 580
1606 |
1607 | 5E9A0126-8C80-41E7-AD49-590FE0777A91
1608 |
1609 | note
1610 | save_multi
1611 | xpos
1612 | 575
1613 | ypos
1614 | 145
1615 |
1616 | 68014C82-BAD9-4CFC-A6C1-A10A58F10E79
1617 |
1618 | xpos
1619 | 1145
1620 | ypos
1621 | 905
1622 |
1623 | 6C55B9DC-6A05-4228-AD55-2311DD125885
1624 |
1625 | colorindex
1626 | 3
1627 | xpos
1628 | 940
1629 | ypos
1630 | 130
1631 |
1632 | 70A3AE8F-5B75-4470-89FC-73298C06596C
1633 |
1634 | xpos
1635 | 795
1636 | ypos
1637 | 890
1638 |
1639 | 71A0EF86-42D5-4C0A-8656-CB6FEAD6F857
1640 |
1641 | xpos
1642 | 30
1643 | ypos
1644 | 235
1645 |
1646 | 7D984E52-7568-4C03-B70F-92EFA7067021
1647 |
1648 | note
1649 | set grid size
1650 | xpos
1651 | 370
1652 | ypos
1653 | 610
1654 |
1655 | 9D05394A-A5F5-4A2B-8AA7-3C06528411D3
1656 |
1657 | xpos
1658 | 1000
1659 | ypos
1660 | 935
1661 |
1662 | A02D5916-1E4A-4207-80F9-AE36DF096158
1663 |
1664 | note
1665 | save_multi
1666 | xpos
1667 | 575
1668 | ypos
1669 | 20
1670 |
1671 | A0D95AE6-FB7D-4C9D-BD5E-835FA2DC88F0
1672 |
1673 | xpos
1674 | 910
1675 | ypos
1676 | 885
1677 |
1678 | A3FBF3CE-3218-4FC1-A8AC-9D056C1C948B
1679 |
1680 | xpos
1681 | 1030
1682 | ypos
1683 | 590
1684 |
1685 | A8AB7CFC-4F7B-436E-8EAD-C48244230F84
1686 |
1687 | xpos
1688 | 740
1689 | ypos
1690 | 150
1691 |
1692 | AB178842-6707-4C5B-88D1-FC317394D8F9
1693 |
1694 | xpos
1695 | 940
1696 | ypos
1697 | 470
1698 |
1699 | C8999852-47D6-4E54-ACBA-03F7186AF696
1700 |
1701 | xpos
1702 | 980
1703 | ypos
1704 | 400
1705 |
1706 | CCD4FC35-EA9E-47A0-97D7-123CE12B9F47
1707 |
1708 | xpos
1709 | 1025
1710 | ypos
1711 | 885
1712 |
1713 | E30762D4-CD07-433B-8B2D-AD06552A2225
1714 |
1715 | note
1716 | copy
1717 | xpos
1718 | 735
1719 | ypos
1720 | 725
1721 |
1722 | E3B4F7E0-C8D4-4C51-A42A-25FF8B353E84
1723 |
1724 | colorindex
1725 | 1
1726 | xpos
1727 | 940
1728 | ypos
1729 | 255
1730 |
1731 | EE17BA6D-5748-4F20-BE74-602EB8B9849F
1732 |
1733 | note
1734 | reveal
1735 | xpos
1736 | 735
1737 | ypos
1738 | 500
1739 |
1740 | F34B0F25-D875-426D-8071-3FACA31F0AE1
1741 |
1742 | note
1743 | if reveal_after == true
1744 | xpos
1745 | 1135
1746 | ypos
1747 | 370
1748 |
1749 | F47F1B80-82C9-49BA-9CF2-DBD52631E160
1750 |
1751 | xpos
1752 | 795
1753 | ypos
1754 | 960
1755 |
1756 | F8D8F9B0-A655-474F-9D3C-68866A8D85E6
1757 |
1758 | xpos
1759 | 195
1760 | ypos
1761 | 195
1762 |
1763 | FF2B9F42-D9A9-42D6-ABDB-8691AC88D747
1764 |
1765 | note
1766 | save selection and file buffer into vars
1767 | xpos
1768 | 540
1769 | ypos
1770 | 870
1771 |
1772 |
1773 | userconfigurationconfig
1774 |
1775 |
1776 | config
1777 |
1778 | default
1779 | cs
1780 | placeholder
1781 | cs
1782 | required
1783 |
1784 | trim
1785 |
1786 |
1787 | description
1788 |
1789 | label
1790 | Trigger keyword
1791 | type
1792 | textfield
1793 | variable
1794 | kw_trigger
1795 |
1796 |
1797 | config
1798 |
1799 | default
1800 | ~/Desktop
1801 | filtermode
1802 | 1
1803 | placeholder
1804 | ~/Desktop
1805 | required
1806 |
1807 |
1808 | description
1809 | Folder in which to save converted images
1810 | label
1811 | Destination
1812 | type
1813 | filepicker
1814 | variable
1815 | dest_dir
1816 |
1817 |
1818 | config
1819 |
1820 | default
1821 | png
1822 | pairs
1823 |
1824 |
1825 | PNG
1826 | png
1827 |
1828 |
1829 | JPG
1830 | jpg
1831 |
1832 |
1833 | GIF
1834 | gif
1835 |
1836 |
1837 |
1838 | description
1839 | Default image format to convert to
1840 | label
1841 | Default format
1842 | type
1843 | popupbutton
1844 | variable
1845 | default_format
1846 |
1847 |
1848 | config
1849 |
1850 | default
1851 |
1852 | required
1853 |
1854 | text
1855 | Delete source images after conversion
1856 |
1857 | description
1858 |
1859 | label
1860 |
1861 | type
1862 | checkbox
1863 | variable
1864 | delete_after_convert
1865 |
1866 |
1867 | config
1868 |
1869 | default
1870 |
1871 | required
1872 |
1873 | text
1874 | Save images to current Finder window
1875 |
1876 | description
1877 |
1878 | label
1879 |
1880 | type
1881 | checkbox
1882 | variable
1883 | save_to_current
1884 |
1885 |
1886 | config
1887 |
1888 | default
1889 |
1890 | required
1891 |
1892 | text
1893 | Reveal clips after saving
1894 |
1895 | description
1896 |
1897 | label
1898 |
1899 | type
1900 | checkbox
1901 | variable
1902 | reveal_after
1903 |
1904 |
1905 | config
1906 |
1907 | default
1908 |
1909 | placeholder
1910 | unlimited
1911 | required
1912 |
1913 | trim
1914 |
1915 |
1916 | description
1917 | Enter an integer to limit # of results returned from the database
1918 | label
1919 | Limit
1920 | type
1921 | textfield
1922 | variable
1923 | sf_clip_limit
1924 |
1925 |
1926 | config
1927 |
1928 | defaultvalue
1929 | 5
1930 | markercount
1931 | 12
1932 | maxvalue
1933 | 12
1934 | minvalue
1935 | 1
1936 | onlystoponmarkers
1937 |
1938 | showmarkers
1939 |
1940 |
1941 | description
1942 | Number of columns to display in Grid View
1943 | label
1944 | Grid Size
1945 | type
1946 | slider
1947 | variable
1948 | grid_size
1949 |
1950 |
1951 | config
1952 |
1953 | default
1954 |
1955 | placeholder
1956 | ~/Library/Application Support/Alfred/Databases
1957 | required
1958 |
1959 | trim
1960 |
1961 |
1962 | description
1963 | Path to clipboard database
1964 | label
1965 | Database path
1966 | type
1967 | textfield
1968 | variable
1969 | db_path
1970 |
1971 |
1972 | config
1973 |
1974 | default
1975 |
1976 | placeholder
1977 | clipboard.alfdb
1978 | required
1979 |
1980 | trim
1981 |
1982 |
1983 | description
1984 | Filename of clipboard history database
1985 | label
1986 | Database filename
1987 | type
1988 | textfield
1989 | variable
1990 | db_name
1991 |
1992 |
1993 | variablesdontexport
1994 |
1995 | version
1996 | 2.7.0
1997 | webaddress
1998 | https://github.com/luckman212/alfred_clipsaver_workflow
1999 |
2000 |
2001 |
--------------------------------------------------------------------------------
/process_clip.bash:
--------------------------------------------------------------------------------
1 | #shellcheck shell=bash
2 |
3 | # this is meant to be sourced from save_multi{_time}.sh
4 |
5 | if [[ $src_fname == */* ]]; then
6 | # from plist
7 | src_basename=${src_fname##*/}
8 | src_basename_noext=${src_basename%.*}
9 | if [[ -r $src_fname ]]; then
10 | if ! /usr/bin/sips -s format $fmt "$src_fname" --out "$dest_dir/${src_basename_noext}.$fmt" &>/dev/null; then
11 | errmsg="failed: $src_basename\n$errmsg"
12 | fi
13 | else
14 | errmsg="not found: $src_basename\n$errmsg"
15 | fi
16 | else
17 | # from Alfred db
18 | tiff_name=$db_path/${db_name}.data/$src_fname
19 | src_basename=${src_fname%.tiff}
20 | if [[ -r $tiff_name ]]; then
21 | if /usr/bin/sips -s format $fmt "$tiff_name" --out "$dest_dir/${src_basename}.$fmt" &>/dev/null; then
22 | if [[ ${delete_after_convert} == true ]]; then
23 | /usr/bin/sqlite3 "$db_path/$db_name" "DELETE FROM clipboard WHERE dataHash = \"$src_fname\" AND dataType = 1 LIMIT 1;"
24 | rm "$tiff_name" &>/dev/null
25 | fi
26 | else
27 | errmsg="failed: $src_basename\n$errmsg"
28 | fi
29 | else
30 | errmsg="not found: $src_basename\n$errmsg"
31 | fi
32 | fi
33 |
--------------------------------------------------------------------------------
/save_multi.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | exec 3>&1
4 |
5 | function break_err() {
6 | exec 1>&3
7 | cat </dev/null
21 |
22 | unset errmsg
23 | num_clips=$1
24 | max_clips=$(/usr/bin/sqlite3 "$db_path/$db_name" "SELECT COUNT(dataHash) FROM clipboard WHERE dataType = 1;")
25 |
26 | re='^[0-9]+$'
27 | if ! [[ $num_clips =~ $re ]] ; then
28 | break_err 2 "not a number (NaN)"
29 | fi
30 |
31 | if [[ $num_clips -eq 0 ]] ; then
32 | break_err 2 "num_clips must be >0"
33 | fi
34 |
35 | if [[ $max_clips -eq 0 ]]; then
36 | break_err 2 "no image clips found in the database"
37 | fi
38 |
39 | if [[ $max_clips -lt "$num_clips" ]]; then
40 | errmsg="not enough clips to fulfill request\nrequested: $num_clips, available: $max_clips (adjusted)"
41 | num_clips=$max_clips
42 | fi
43 |
44 | if [[ $save_to_current == "true" ]]; then
45 | if fwin=$(osascript -e 'tell application "Finder" to get POSIX path of (insertion location as alias)' 2>/dev/null); then
46 | dest_dir=$fwin
47 | fi
48 | fi
49 |
50 | if [[ ${dest_dir:0:1} != '/' ]]; then
51 | dest_dir=$(tr '[:upper:]' '[:lower:]' <<<"$HOME/$dest_dir")
52 | fi
53 |
54 | # make sure dest_dir exists
55 | if [[ ! -d $dest_dir ]]; then
56 | if ! /bin/mkdir -p "$dest_dir"; then
57 | break_err 2 "failed to create dest_dir:\n$dest_dir"
58 | fi
59 | fi
60 |
61 | fmt=${format:-png}
62 |
63 | # fetch image clips from database, loop and process
64 | while read -r src_fname ; do
65 | #echo 1>&2 "processing: [$src_fname]"
66 | source ./process_clip.bash
67 | done < <(./get_image_hashes.py --num_clips "$num_clips")
68 |
69 | # if ALL images were processed, run cleanup script
70 | if [[ $num_clips -eq $max_clips ]]; then
71 | ./alfred_validate_cache.sh
72 | fi
73 |
74 | if [[ -n $errmsg ]]; then
75 | break_err 1 "$errmsg"
76 | fi
77 |
78 | break_err 0
79 |
--------------------------------------------------------------------------------
/save_multi_time.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | exec 3>&1
4 |
5 | function break_err() {
6 | exec 1>&3
7 | cat </dev/null
21 |
22 | unset errmsg
23 | atleast=$1
24 |
25 | re='^[0-9]+$'
26 | if ! [[ $atleast =~ $re ]] ; then
27 | break_err 2 "not a number (NaN)"
28 | fi
29 |
30 | if [[ $atleast -eq 0 ]] ; then
31 | break_err 2 "atleast must be >0"
32 | fi
33 |
34 | if [[ $save_to_current == "true" ]]; then
35 | if fwin=$(osascript -e 'tell application "Finder" to get POSIX path of (insertion location as alias)' 2>/dev/null); then
36 | dest_dir=$fwin
37 | fi
38 | fi
39 |
40 | if [[ ${dest_dir:0:1} != '/' ]]; then
41 | dest_dir=$(tr '[:upper:]' '[:lower:]' <<<"$HOME/$dest_dir")
42 | fi
43 |
44 | # make sure dest_dir exists
45 | if [[ ! -d $dest_dir ]]; then
46 | if ! /bin/mkdir -p "$dest_dir"; then
47 | break_err 2 "failed to create dest_dir:\n$dest_dir"
48 | fi
49 | fi
50 |
51 | fmt=${format:-png}
52 |
53 | # fetch image clips from database, loop and process
54 | while read -r src_fname ; do
55 | #echo 1>&2 "processing: [$src_fname]"
56 | source ./process_clip.bash
57 | done < <(./get_image_hashes.py --atleast "$atleast")
58 |
59 | if [[ -n $errmsg ]]; then
60 | break_err 1 "$errmsg"
61 | fi
62 |
63 | break_err 0
64 |
--------------------------------------------------------------------------------
/save_single.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | exec 3>&1
4 |
5 | function break_err() {
6 | exec 1>&3
7 | cat </dev/null
21 |
22 | unset errmsg
23 | img_pathname=$1
24 | img_basename=${img_pathname##*/}
25 | img_noext=${img_basename%.*}
26 |
27 | if [[ ${dest_dir:0:1} != '/' ]]; then
28 | dest_dir=$(tr '[:upper:]' '[:lower:]' <<<"$HOME/$dest_dir")
29 | fi
30 |
31 | if [[ ${save_to_current} == true ]]; then
32 | if fwin=$(osascript -e 'tell application "Finder" to get POSIX path of (insertion location as alias)' 2>/dev/null); then
33 | dest_dir=$fwin
34 | fi
35 | fi
36 |
37 | if [[ -r $img_pathname ]]; then
38 | # make sure dest_dir exists
39 | if [[ ! -d $dest_dir ]]; then
40 | /bin/mkdir -p "$dest_dir" || break_err 1 "failed to create output dir"
41 | fi
42 | fmt=${format:-png}
43 | destfile=$dest_dir/${img_noext}.$fmt
44 | if [[ ${img_pathname} == "${destfile}" ]]; then
45 | break_err 1 "(source and destination filename were the same)"
46 | fi
47 | if /usr/bin/sips -s format $fmt "$img_pathname" --out "$dest_dir/${img_noext}.$fmt"; then
48 | if [[ ${delete_after_convert} == true ]] && [[ -n $dataHash ]]; then
49 | /usr/bin/sqlite3 "$db_path/$db_name" "DELETE FROM clipboard WHERE dataHash = \"$dataHash\" AND dataType = 1 LIMIT 1;"
50 | rm "$img_pathname"
51 | fi
52 | break_err 0
53 | else
54 | break_err 1
55 | fi
56 | else
57 | break_err 1
58 | fi
59 |
--------------------------------------------------------------------------------
/wf_common.py:
--------------------------------------------------------------------------------
1 | """
2 | Common Functions, Vars, Constants
3 | """
4 |
5 | import os
6 | import time
7 |
8 | def envvar(v: str, dv: str) -> str:
9 | return str(os.getenv(v) or dv)
10 |
11 | #for checkboxes - unchecked aka `0` returns `false`
12 | def envvar_to_bool(v: str) -> str:
13 | try:
14 | b = int(os.getenv(v))
15 | except:
16 | b = 0
17 | return str(bool(b)).lower()
18 |
19 | def envvar_to_int(v: str, dv: int=0) -> int:
20 | try:
21 | return int(os.getenv(v))
22 | except:
23 | pass
24 | try:
25 | return int(dv)
26 | except:
27 | return 0
28 |
29 | #wf vars
30 | db_name = envvar('db_name', 'clipboard.alfdb')
31 | db_path = os.path.expanduser(envvar('db_path', '~/Library/Application Support/Alfred/Databases'))
32 | dest_dir = os.path.expanduser(envvar('dest_dir', '~/Desktop/saved_clips')).lower()
33 | default_format = envvar('default_format', 'png').lower()
34 | delete_after_convert = envvar_to_bool('delete_after_convert')
35 | save_to_current = envvar_to_bool('save_to_current')
36 | sf_clip_limit = envvar_to_int('sf_clip_limit', -1)
37 |
38 | #home_path = os.getenv('HOME')
39 | db_res = os.path.join(db_path, db_name)
40 | i_path = f'{db_res}.data'
41 | uidSeed = str(os.getenv('uidSeed', time.time()))
42 | img_exts = [ 'png', 'gif', 'jpg', 'jpeg', 'tiff', 'tif', 'bmp' ]
43 |
--------------------------------------------------------------------------------