branch: elpa/mastodon commit 75b0e3cc1cdc2c7330532dd6d52c6b5c33d188b9 Merge: b3a8a1c733 d28b023f39 Author: martianh <marti...@noreply.codeberg.org> Commit: martianh <marti...@noreply.codeberg.org>
Merge pull request 'Fix #675 use url.el for posting attachments' (#677) from rahguzar/mastodon.el:develop into develop --- lisp/mastodon-http.el | 125 ++++++++++++++++++++++++++------------------------ lisp/mastodon.el | 2 +- 2 files changed, 67 insertions(+), 60 deletions(-) diff --git a/lisp/mastodon-http.el b/lisp/mastodon-http.el index 2f150ff01c..a55097dff2 100644 --- a/lisp/mastodon-http.el +++ b/lisp/mastodon-http.el @@ -31,7 +31,6 @@ ;;; Code: (require 'json) -(require 'request) ; for attachments upload (require 'url) (require 'url-http) (require 'shr) @@ -42,6 +41,7 @@ (autoload 'mastodon-auth--access-token "mastodon-auth") (autoload 'mastodon-toot--update-status-fields "mastodon-toot") +(autoload 'url-insert "url-handlers") (defvar mastodon-http--api-version "v1") @@ -347,73 +347,80 @@ PARAMS is an alist of any extra parameters to send with the request." Then run function CALLBACK with arguements CBARGS. Authorization header is included by default unless UNAUTHENTICED-P is non-nil." (mastodon-http--authorized-request "POST" - (let (;(request-timeout 5) ; this is from request.el no url.el! - (url-request-data (when params + (let ((url-request-data (when params (mastodon-http--build-params-string params)))) (with-temp-buffer (url-retrieve url callback cbargs))))) -;; TODO: test for curl first? +(defun mastodon-http--get-cb-data (status) + "Return data using `json-read' after a successful async request. +If STATUS includes an error, emit a message describing it and return nil." + (let* ((buf (current-buffer)) + (data (with-temp-buffer + (url-insert buf) + (goto-char (point-min)) + (json-read)))) + (if-let* ((error-thrown (plist-get status :error))) + ;; not necessarily a user error, but we want its functionality: + (user-error "%S %s" error-thrown (alist-get 'error data)) + data))) + +(defun mastodon-http--post-media-callback (status file caption buffer) + "Callback function called after posting FILE as an attachment with CAPTION. +The toot is being composed in BUFFER. See `url-retrieve' for STATUS." + (unwind-protect + (when-let* ((data (mastodon-http--get-cb-data status))) + (with-current-buffer buffer + (let ((id (alist-get 'id data))) + ;; update ids: + (if (not mastodon-toot--media-attachment-ids) + ;; add first id: + (push id mastodon-toot--media-attachment-ids) + ;; add new id to end of list to preserve order: + (push id (cdr + (last mastodon-toot--media-attachment-ids)))) + ;; pleroma, PUT the description: + ;; this is how the mangane akkoma web client does it + ;; and it seems easier than the other options! + (when (and caption + (not (string= caption (alist-get 'description data)))) + (let ((url (mastodon-http--api (format "media/%s" id)))) + ;; (message "PUTting image description") + (mastodon-http--put url `(("description" . ,caption))))) + (message "Uploading %s... (done)" file) + (mastodon-toot--update-status-fields)))) + (kill-buffer (current-buffer)))) + +(defun mastodon-http--post-media-prep-file (filename) + "Return the request data to upload FILENAME." + (with-temp-buffer + (set-buffer-multibyte nil) + (insert-file-contents-literally filename) + (let ((boundary (buffer-hash))) + (goto-char (point-min)) + (insert "--" boundary "\r\n" + (format "Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n\r\n" + (file-name-nondirectory filename))) + (goto-char (point-max)) + (insert "\r\n" "--" boundary "--" "\r\n") + `(,boundary . ,(buffer-substring-no-properties (point-min) (point-max)))))) + (defun mastodon-http--post-media-attachment (url filename caption) "Make POST request to upload FILENAME with CAPTION to the server's media URL. The upload is asynchronous. On succeeding, `mastodon-toot--media-attachment-ids' is set to the id(s) of the item uploaded, and `mastodon-toot--update-status-fields' is run." - (let* ((file (file-name-nondirectory filename)) - (request-backend 'curl) - (desc `(("description" . ,caption))) - (cb (cl-function - (lambda (&key data &allow-other-keys) - (when data - (let* ((id (alist-get 'id data))) - ;; update ids: - (if (not mastodon-toot--media-attachment-ids) - ;; add first id: - (push id mastodon-toot--media-attachment-ids) - ;; add new id to end of list to preserve order: - (push id (cdr - (last mastodon-toot--media-attachment-ids)))) - ;; pleroma, PUT the description: - ;; this is how the mangane akkoma web client does it - ;; and it seems easier than the other options! - (when (and caption - (not (string= caption (alist-get 'description data)))) - (let ((url (mastodon-http--api (format "media/%s" id)))) - ;; (message "PUTting image description") - (mastodon-http--put url desc))) - (message "Uploading %s... (done)" file) - (mastodon-toot--update-status-fields))))))) - (request - url - :type "POST" - :params desc - :files `(("file" . (,file :file ,filename - :mime-type "multipart/form-data"))) - :parser 'json-read - :headers `(("Authorization" . ,(concat "Bearer " - (mastodon-auth--access-token)))) - :sync nil - :success (apply-partially cb) - :error (cl-function - (lambda (&key error-thrown &allow-other-keys) - (cond - ;; handle curl errors first (eg 26, can't read file/path) - ;; because the '=' test below fails for them - ;; they have the form (error . error message 24) - ((not (proper-list-p error-thrown)) ; not dotted list - (message "Got error: %s. Shit went south." (cdr error-thrown))) - ;; handle mastodon api errors - ;; they have the form (error http 401) - ((= (car (last error-thrown)) 401) - (message "Got error: %s Unauthorized: The access token is invalid" - error-thrown)) - ((= (car (last error-thrown)) 422) - (message "Got error: %s Unprocessable entity: file or file\ - type is unsupported or invalid" - error-thrown)) - (t - (message "Got error: %s Shit went south" - error-thrown)))))))) + (mastodon-http--authorized-request "POST" + (let* ((data (mastodon-http--post-media-prep-file filename)) + (url-request-extra-headers + (append url-request-extra-headers ; auth set in macro + `(("Content-Type" . ,(format "multipart/form-data; boundary=%s" + (car data)))))) + (url-request-data (cdr data)) + (params `(("description" . ,caption))) + (url (mastodon-http--concat-params-to-url url params))) + (url-retrieve url #'mastodon-http--post-media-callback + `(,filename ,caption ,(current-buffer)))))) (provide 'mastodon-http) ;;; mastodon-http.el ends here diff --git a/lisp/mastodon.el b/lisp/mastodon.el index 4ac0136df8..a60961abb1 100644 --- a/lisp/mastodon.el +++ b/lisp/mastodon.el @@ -7,7 +7,7 @@ ;; Marty Hiatt <mouse...@disroot.org> ;; Maintainer: Marty Hiatt <mouse...@disroot.org> ;; Version: 1.1.12 -;; Package-Requires: ((emacs "28.1") (request "0.3.0") (persist "0.4") (tp "0.7")) +;; Package-Requires: ((emacs "28.1") (persist "0.4") (tp "0.7")) ;; Homepage: https://codeberg.org/martianh/mastodon.el ;; This file is not part of GNU Emacs.