Attaching images from the phone in org mode

I recently developed the following workflow to attach images from my phone in emacs org mode.
First, I created the folder NEXTCLOUD_FOLDER on my nextcloud instance and used the nextcloud app on my phone to create photos in that folder.
Then I execute iko/org-attach-image-from-phone in an org mode document where I want the image to be attached using org-attach/org-download.
Emacs then first syncs NEXTCLOUD_FOLDER with ATTACHMENTS_CACHE_DIR. On success I am prompted with yad to choose the attachment among the files in ATTACHMENTS_CACHE_DIR. It is then sized down to 720p with imagemagick; otherwise the preview in emacs is too slow. Finally, the image is attached to the current node and a link to it is written into the file at point.

To use it, you need to install yad, nextcloudcmd and imagemagick on your machine, as well as org-download, s.el and f.el in your emacs. Finally, you need to configure ATTACHMENTS_CACHE_DIR, YOUR_USERNAME, YOUR_PASSWORD, YOUR_SERVER and NEXTCLOUD_FOLDER.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
(cl-defun iko/run-process-async (name program &key stdin args on-exit)
  "Run PROGRAM asynchronously with ARGS, outputting to BUFFER-NAME."
  (with-current-buffer (get-buffer-create (concat "*" name "*"))
    (let ((b (current-buffer))
          (p (apply #'start-process name (current-buffer) program args)))
      (set-process-sentinel
       p
       (lambda (proc event)
         (when (eq (process-status proc) 'exit)
           (when on-exit
             (funcall on-exit
                      (process-exit-status proc)
                      (with-current-buffer b (buffer-string))
                      proc
                      event))
           (kill-buffer b))))
      (when stdin
        (process-send-string p (if (string-suffix-p "\n" stdin)
                                   stdin
                                 (concat stdin "\n")))
        (process-send-eof p))
      p)))

(defun iko/scale-image-to-max-size (input-file output-file &optional max-width max-height)
  "Downscale INPUT-FILE to fit within MAX-WIDTH x MAX-HEIGHT (default 1280x720) and save as OUTPUT-FILE."
  (let ((max-width (or max-width 1280))
        (max-height (or max-height 720)))
    ;; Call ImageMagick 'convert' to resize while keeping aspect ratio
    (call-process
     "convert" nil nil nil
     input-file
     "-resize" (format "%dx%d>" max-width max-height)
     output-file)
    (message "Saved scaled image to %s" output-file)))


(defun iko/yad-select-img-from-dir (dir)
  "Use yad to select a file from DIR. Returns the selected file path."
  (let* ((command (s-join " "
                          `("yad"
                            "--file"
                            "--filter=\"Images | *.png *.jpg *.jpeg *.gif *.webp *.bmp\""
                            "--title=\"Select Image\""
                            "--add-preview"
                            "--large-preview"
                            ,(format "--workdir=%s" dir))))
         (output (string-trim (shell-command-to-string command))))
    (if (and (not (string-empty-p output))
             (file-exists-p output))
        output
      (user-error "No file selected or yad failed"))))

(defun iko/org-attach-image-from-phone ()
  (interactive)
  (let* ((attachDir ATTACHMENTS_CACHE_DIR)
         (user YOUR_USERNAME)
         (password YOUR_PASSWORD)
         (server YOUR_SERVER)
         (args
          `("-u" ,user
            "--path" ,NEXTCLOUD_FOLDER
            ,attachDir
            ,server)))
    (f-mkdir-full-path attachDir)
    (iko/run-process-async
     "nextcloudcmd-download"
     "nextcloudcmd"
     :args args
     :stdin password
     :on-exit
     (lambda (status-code _output _proc _event)
       (when (/= status-code 0)
         (user-error "nextcloudcmd failed with code %s" status-code))
       (let ((img-file (iko/yad-select-img-from-dir attachDir)))
         (message "selected %s" img-file)
         (when img-file
           (let ((tmpfile (make-temp-file "scaled-img-" nil ".png")))
             (message "buffer file: %s" tmpfile)
             (iko/scale-image-to-max-size img-file tmpfile)
             (org-download-image (concat "file://" tmpfile))
             (message "attached %s" img-file))))))))
Licensed under CC BY-NC-SA 4.0
Last updated on Nov 28, 2025 14:05 CET
Built with Hugo
Theme Stack designed by Jimmy