| 1 | ;;; uguu.el --- Upload files to uguu.se -*- lexical-binding: t; -*- |
| 2 | ;; |
| 3 | ;;; Commentary: |
| 4 | ;; `uguu-upload-file' and `uguu-upload-clipboard' upload to uguu.se and put |
| 5 | ;; the resulting link in the kill ring. In an ERC buffer the link is also |
| 6 | ;; inserted at the prompt. A prefix argument flips that behavior. |
| 7 | ;; |
| 8 | ;;; Code: |
| 9 | |
| 10 | (require 'subr-x) |
| 11 | |
| 12 | (defgroup uguu nil |
| 13 | "Upload files to uguu.se." |
| 14 | :group 'tools) |
| 15 | |
| 16 | (defcustom uguu-endpoint "https://uguu.se/upload?output=text" |
| 17 | "Upload URL. `output=text' makes the response just the file link." |
| 18 | :type 'string) |
| 19 | |
| 20 | (defcustom uguu-insert-modes '(erc-mode) |
| 21 | "Major modes where the link is inserted at point by default." |
| 22 | :type '(repeat symbol)) |
| 23 | |
| 24 | (defun uguu--finish (url insert) |
| 25 | "Copy URL to the kill ring, and insert it when INSERT is non-nil." |
| 26 | (kill-new url) |
| 27 | (when insert |
| 28 | (when (derived-mode-p 'erc-mode) |
| 29 | (goto-char (point-max))) |
| 30 | (insert url)) |
| 31 | (message "Uploaded: %s%s" url (if insert "" " (copied)"))) |
| 32 | |
| 33 | (defun uguu--upload (file insert &optional cleanup) |
| 34 | "Upload FILE asynchronously, then call `uguu--finish' with INSERT. |
| 35 | When CLEANUP is non-nil, delete FILE afterwards." |
| 36 | (let ((buf (current-buffer)) |
| 37 | (out (generate-new-buffer " *uguu*"))) |
| 38 | (message "Uploading %s..." (file-name-nondirectory file)) |
| 39 | (make-process |
| 40 | :name "uguu" :buffer out :noquery t |
| 41 | :command (list "curl" "-sS" "--fail-with-body" |
| 42 | "-F" (concat "files[]=@" (expand-file-name file)) |
| 43 | uguu-endpoint) |
| 44 | :sentinel |
| 45 | (lambda (proc _event) |
| 46 | (when (memq (process-status proc) '(exit signal)) |
| 47 | (let ((resp (with-current-buffer out (string-trim (buffer-string))))) |
| 48 | (kill-buffer out) |
| 49 | (when cleanup (ignore-errors (delete-file file))) |
| 50 | (if (and (zerop (process-exit-status proc)) |
| 51 | (string-prefix-p "http" resp)) |
| 52 | (if (buffer-live-p buf) |
| 53 | (with-current-buffer buf (uguu--finish resp insert)) |
| 54 | (uguu--finish resp nil)) |
| 55 | (message "Uguu upload failed: %s" resp)))))))) |
| 56 | |
| 57 | (defun uguu--insert-p (arg) |
| 58 | "Whether to insert the link, given prefix ARG." |
| 59 | (let ((default (apply #'derived-mode-p uguu-insert-modes))) |
| 60 | (if arg (not default) default))) |
| 61 | |
| 62 | ;;;###autoload |
| 63 | (defun uguu-upload-file (file &optional arg) |
| 64 | "Upload FILE to uguu.se. Copy the link; insert it too in ERC. |
| 65 | With prefix ARG, flip whether the link is inserted." |
| 66 | (interactive (list (read-file-name "Upload file: " nil nil t) current-prefix-arg)) |
| 67 | (uguu--upload file (uguu--insert-p arg))) |
| 68 | |
| 69 | (defun uguu--clipboard-file () |
| 70 | "Return the path of a file copied in Finder/CleanShot, or nil. |
| 71 | Checked before the image data, which for a copied file is just its icon." |
| 72 | (with-temp-buffer |
| 73 | (when (zerop (call-process |
| 74 | "osascript" nil '(t nil) nil |
| 75 | "-e" "POSIX path of (the clipboard as «class furl»)")) |
| 76 | (let ((path (string-trim (buffer-string)))) |
| 77 | (and (file-regular-p path) path))))) |
| 78 | |
| 79 | (defun uguu--clipboard-image () |
| 80 | "Write the clipboard image to a temp PNG and return its path, or nil." |
| 81 | (let ((tmp (make-temp-file "uguu-" nil ".png"))) |
| 82 | (if (zerop (call-process |
| 83 | "osascript" nil nil nil |
| 84 | "-e" (format "set f to open for access POSIX file %S with write permission" |
| 85 | tmp) |
| 86 | "-e" "set eof f to 0" |
| 87 | "-e" "write (the clipboard as «class PNGf») to f" |
| 88 | "-e" "close access f")) |
| 89 | tmp |
| 90 | (delete-file tmp) |
| 91 | nil))) |
| 92 | |
| 93 | ;;;###autoload |
| 94 | (defun uguu-upload-clipboard (&optional arg) |
| 95 | "Upload the clipboard (an image, else text) to uguu.se. |
| 96 | With prefix ARG, flip whether the link is inserted." |
| 97 | (interactive "P") |
| 98 | (let* ((insert (uguu--insert-p arg)) |
| 99 | (file (uguu--clipboard-file)) |
| 100 | (img (and (not file) (uguu--clipboard-image)))) |
| 101 | (cond |
| 102 | (file (uguu--upload file insert)) |
| 103 | (img (uguu--upload img insert t)) |
| 104 | (t |
| 105 | (let ((text (or (ignore-errors (gui-get-selection 'CLIPBOARD 'STRING)) |
| 106 | (car kill-ring))) |
| 107 | (tmp (make-temp-file "uguu-" nil ".txt"))) |
| 108 | (unless (and text (not (string-empty-p text))) |
| 109 | (user-error "Clipboard is empty")) |
| 110 | (write-region text nil tmp nil 'silent) |
| 111 | (uguu--upload tmp insert t)))))) |
| 112 | |
| 113 | (provide 'uguu) |
| 114 | ;;; uguu.el ends here |