Browse Source

Add img-chon.sh, an image tagging script

Frans Bergman 5 years ago
parent
commit
ef4677f051
4 changed files with 191 additions and 0 deletions
  1. 3 0
      .config/sxhkd/sxhkdrc
  2. 21 0
      .config/sxiv/exec/image-info
  3. 36 0
      .config/sxiv/exec/key-handler
  4. 131 0
      .scripts/img-chon.sh

+ 3 - 0
.config/sxhkd/sxhkdrc

@@ -11,6 +11,9 @@ Print
 alt + shift + e
     ~/.scripts/exit_menu.sh
 
+alt + shift + x
+    ~/.scripts/img-chon.sh query-dmenu
+
 # Launch programs
 alt + shift + s
     spotify

+ 21 - 0
.config/sxiv/exec/image-info

@@ -0,0 +1,21 @@
+#!/bin/sh
+
+# Example for $XDG_CONFIG_HOME/sxiv/exec/image-info
+# Called by sxiv(1) whenever an image gets loaded.
+# The output is displayed in sxiv's status bar.
+# Arguments:
+#   $1: path to image file
+#   $2: image width
+#   $3: image height
+
+s="  " # field separator
+
+exec 2>/dev/null
+
+filename=$(basename -- "$1")
+filesize=$(du -Hh -- "$1" | cut -f 1)
+geometry="${2}x${3}"
+tags=$(img-chon.sh tags "$1")
+
+echo "${filesize}${s}${geometry}${s}${filename}${s}${tags}"
+

+ 36 - 0
.config/sxiv/exec/key-handler

@@ -0,0 +1,36 @@
+#!/bin/sh
+
+# Example for $XDG_CONFIG_HOME/sxiv/exec/key-handler
+# Called by sxiv(1) after the external prefix key (C-x by default) is pressed.
+# The next key combo is passed as its first argument. Passed via stdin are the
+# images to act upon, one path per line: all marked images, if in thumbnail
+# mode and at least one image has been marked, otherwise the current image.
+# sxiv(1) blocks until this script terminates. It then checks which images
+# have been modified and reloads them.
+
+# The key combo argument has the following form: "[C-][M-][S-]KEY",
+# where C/M/S indicate Ctrl/Meta(Alt)/Shift modifier states and KEY is the X
+# keysym as listed in /usr/include/X11/keysymdef.h without the "XK_" prefix.
+
+rotate() {
+	degree="$1"
+	tr '\n' '\0' | xargs -0 realpath | sort | uniq | while read file; do
+		case "$(file -b -i "$file")" in
+		image/jpeg*) jpegtran -rotate "$degree" -copy all -outfile "$file" "$file" ;;
+		*)           mogrify  -rotate "$degree" "$file" ;;
+		esac
+	done
+}
+
+case "$1" in
+"C-x")      xclip -in -filter | tr '\n' ' ' | xclip -in -selection clipboard ;;
+"C-c")      while read file; do xclip -selection clipboard -target image/png "$file"; done ;;
+"C-e")      while read file; do urxvt -bg "#444" -fg "#eee" -sl 0 -title "$file" -e sh -c "exiv2 pr -q -pa '$file' | less" & done ;;
+"C-g")      tr '\n' '\0' | xargs -0 gimp & ;;
+"C-r")      while read file; do rawtherapee "$file" & done ;;
+"C-comma")  rotate 270 ;;
+"C-period") rotate  90 ;;
+"C-slash")  rotate 180 ;;
+"C-t")      tr '\n' '\0' | xargs -0 img-chon.sh tag-dmenu
+esac
+

+ 131 - 0
.scripts/img-chon.sh

@@ -0,0 +1,131 @@
+#!/bin/bash
+
+IMG_BASE="$HOME"
+DATABASE="$HOME/Pictures/image_tags.sqlite"
+SQLITE="sqlite3"
+
+SQL="$SQLITE $DATABASE"
+SXIV="sxiv -t -"
+
+join_by () {
+    local IFS="$1"
+    shift
+    echo "$*"
+}
+
+sp="/-\|"
+sc=0
+spin() {
+   printf "\r%s [%s/%s] %s" "${sp:sc++:1}" "$1" "$2" "$3"
+   if ((sc==${#sp})); then
+       sc=0
+   fi
+}
+endspin() {
+   printf "\r\n"
+}
+
+get_hash() {
+    [ -f "$1" ] || exit 1
+    read -ra sha < <(sha256sum "$1")
+    echo "${sha[0]}"
+}
+
+case $1 in
+    "init")
+        $SQL "CREATE TABLE image (
+                  path STRING NOT NULL UNIQUE ON CONFLICT REPLACE,
+                  hash STRING PRIMARY KEY NOT NULL UNIQUE ON CONFLICT ABORT
+              );"
+        $SQL "CREATE TABLE image_tag (
+                  hash STRING NOT NULL,
+                  tag STRING NOT NULL,
+                  UNIQUE (hash, tag) ON CONFLICT ABORT
+              );"
+        $SQL "CREATE TRIGGER image_exists
+              BEFORE INSERT ON image_tag
+              BEGIN
+                  SELECT CASE
+                      WHEN (SELECT COUNT(*) FROM image WHERE image.hash = NEW.hash) = 0 THEN
+                          RAISE(FAIL, 'image not in database')
+                      END;
+              END;"
+        ;;
+    "add")
+        total="${#@}"
+        i=0
+        for f in "${@:2}"; do
+            spin "$i" "$total" "$f"
+            i=$((i+1))
+            hsh="$(get_hash "$f")"
+            path="$(realpath --relative-to="$IMG_BASE" "$f")"
+            $SQL "INSERT INTO image (path, hash) VALUES ('$path', '$hsh');"
+        done
+        endspin
+        ;;
+    "tag")
+        tag=$2
+        for f in "${@:3}"; do
+            hsh="$(get_hash "$f")"
+            $SQL "INSERT INTO image_tag (hash, tag) VALUES ('$hsh','$tag');"
+        done
+        ;;
+    "untag")
+        tag=$2
+        for f in "${@:3}"; do
+            hsh="$(get_hash "$f")"
+            $SQL "DELETE FROM image_tag WHERE (hash, tag) = ('$hsh','$tag');"
+        done
+        ;;
+    "query")
+        tags=()
+        anti_tags=()
+        for t in "${@:2}"; do
+            if [[ "$t" == -* ]]; then
+                anti_tags+=( "'${t:1}'" )
+            else
+                tags+=( "'$t'" )
+            fi
+        done
+        tags_str=$(join_by , "${tags[@]}")
+        anti_tags_str=$(join_by , "${anti_tags[@]}")
+        $SQL "SELECT path FROM image WHERE ${#tags[@]} = (SELECT COUNT(*) FROM image_tag WHERE image.hash = image_tag.hash AND image_tag.tag IN ($tags_str)) AND (0 = (SELECT COUNT(*) FROM image_tag WHERE image.hash = image_tag.hash AND image_tag.tag IN ($anti_tags_str)));" | awk "{ print \"$IMG_BASE/\" \$0 }"
+        ;;
+    "tags")
+        if [ -f "$2" ]; then
+            hsh="$(get_hash "$2")"
+            $SQL "SELECT DISTINCT tag FROM image_tag WHERE hash = '$hsh';"
+        else
+            $SQL "SELECT DISTINCT tag FROM image_tag;"
+        fi
+        ;;
+    "query-dmenu")
+        selected_tags=()
+        while selection=$($0 tags | dmenu -p "Select tags, <esc> when done.") && [ -n "$selection" ]; do
+            selected_tags+=("$selection")
+        done
+        $0 query "${selected_tags[@]}" | $SXIV
+        ;;
+    "tag-dmenu")
+        while selection=$($0 tags | dmenu -p "Select tags, <esc> when done.") && [ -n "$selection" ]; do
+            $0 tag "$selection" "${@:2}"
+        done
+        ;;
+    "stats")
+        echo "Number of images: $($SQL "SELECT COUNT(*) FROM image;")"
+        echo "Number of distinct tags: $($SQL "SELECT COUNT(DISTINCT tag) FROM image_tag;")"
+        ;;
+    "")
+        name="$(basename "$0")"
+        echo -e "Usage:"
+        echo -e "  $name init                Create a new database."
+        echo -e "  $name add IMAGE...        Add new image(s) to the database."
+        echo -e "  $name tag TAG IMAGE...    Tag image(s) with TAG."
+        echo -e "  $name untag TAG IMAGE...  Remove tag from image(s)."
+        echo -e "  $name query TAG...        List images with specified tags."
+        echo -e "  $name tags [IMAGE]        Show all tags, or tags for IMAGE."
+        echo -e "  $name query-dmenu         Interactively construct a query using dmenu and opens the result in sxiv."
+        echo -e "  $name tag-dmenu IMAGE...  Interactively tag specified image(s) using dmenu."
+        echo -e "  $name stats               Show database statistics."
+        ;;
+esac