aboutsummaryrefslogtreecommitdiff
path: root/emacs
diff options
context:
space:
mode:
authorYuchen Pei <id@ypei.org>2025-02-07 09:52:11 +1100
committerYuchen Pei <id@ypei.org>2025-02-07 09:52:11 +1100
commit1302f96df75f4f93247beaafee0b0a780829be18 (patch)
tree7ae569f3ce8d16d386bd02882491b0e02ff8f2af /emacs
parent2f880075e7adc9c8070e5b0e606af7fbf78eaa0a (diff)
[emacs] Flesh out bookshelf mode a bitHEADmaster
Add follow mode. Display book cover in infobox. Render html description in infobox.
Diffstat (limited to 'emacs')
-rw-r--r--emacs/.emacs.d/init/ycp-markup.el3
-rw-r--r--emacs/.emacs.d/lisp/my/belf.el89
-rw-r--r--emacs/.emacs.d/lisp/my/infobox.el19
3 files changed, 99 insertions, 12 deletions
diff --git a/emacs/.emacs.d/init/ycp-markup.el b/emacs/.emacs.d/init/ycp-markup.el
index 5f21da7..0c7bbb2 100644
--- a/emacs/.emacs.d/init/ycp-markup.el
+++ b/emacs/.emacs.d/init/ycp-markup.el
@@ -113,7 +113,8 @@
(my-override nov-render-title)
(my-override nov-scroll-up)
(my-keybind nov-mode-map
- "Q" #'my-nov-copy-buffer-file-with-staging)
+ "Q" #'my-nov-copy-buffer-file-with-staging
+ "i" #'imenu)
)
;;; json-mode
diff --git a/emacs/.emacs.d/lisp/my/belf.el b/emacs/.emacs.d/lisp/my/belf.el
index 035f44d..89f27c0 100644
--- a/emacs/.emacs.d/lisp/my/belf.el
+++ b/emacs/.emacs.d/lisp/my/belf.el
@@ -31,9 +31,14 @@
(defvar-keymap belf-mode-map
:parent tabulated-list-mode-map
- "i" #'belf-book-infobox
+ "F" #'belf-toggle-follow-mode
+ "d" #'belf-show-in-dired
+ "i" #'belf-book-infobox-at-point
+ "n" #'belf-next-line
+ "o" #'belf-open-book-other-window
+ "p" #'belf-previous-line
"RET" #'belf-open-book
- "o" #'belf-open-book-other-window)
+ )
(define-derived-mode belf-mode tabulated-list-mode "Bookshelf"
"Major mode for browsing a list of books."
@@ -77,9 +82,85 @@
(title . ,(match-string 2 base))
(year . ,(match-string 3 base))))))
-(defun belf-book-infobox ()
+(defun belf-book-infobox (file-name)
(interactive)
- (infobox-exiftool (tabulated-list-get-id)))
+ (belf-book-render-info (belf-exiftool-info file-name) file-name))
+
+(defvar belf-exiftool-program "exiftool" "The exiftool program.")
+
+(defun belf-exiftool-info (file-name)
+ "Given a video URL, return an alist of its properties."
+ (with-temp-buffer
+ (call-process belf-exiftool-program nil t nil "-j" file-name)
+ (let ((start (point)))
+ (call-process-region
+ nil nil "jq" nil t nil
+ ".[0]|pick(.Title, .Author, .Creator, .Keywords, .Subject, .Publisher, .Identifier, .Series, .Title_sort, .Author_sort, .PageCount, .ISBN, .Language, .FileType, .Description)")
+ (goto-char start)
+ (json-read)))
+ )
+
+(defun belf-book-cover (file-name)
+ "Get book cover.
+
+First look for an image file with the same file name.
+Then for PDF, extract the first page.
+For EPUB, looks for a cover image in the file. If not found, extract the first page."
+ (cond ((file-exists-p (file-name-with-extension file-name "jpg"))
+ (format "file://%s" (file-name-with-extension file-name "jpg")))
+ (t nil)))
+
+(defun belf-book-infobox-at-point ()
+ (interactive)
+ (let ((help-window-select (not belf-follow-mode)))
+ (belf-book-infobox (tabulated-list-get-id)))
+ )
+
+(defun belf-book-render-info (info file-name)
+ (setf (alist-get 'Title info)
+ (concat (alist-get 'Title info)
+ " -- "
+ (buttonize
+ "xdg-open"
+ (lambda (_) (call-process "xdg-open" nil 0 nil file-name)))
+ " " (buttonize "find-file" (lambda (_) (find-file file-name))))
+ (alist-get 'Thumbnail info)
+ (belf-book-cover file-name)
+ (alist-get 'Description info)
+ (when-let ((text (alist-get 'Description info)))
+ (with-temp-buffer
+ (insert text)
+ (shr-render-region (point-min) (point-max))
+ (goto-char (point-min))
+ (insert "\n")
+ (buffer-string))))
+ (infobox-render
+ (infobox-translate info (infobox-default-specs info))
+ `(belf-book-infobox ,file-name)
+ (called-interactively-p 'interactive)))
+
+(defvar belf-follow-mode nil "Whether follow mode is on.")
+
+(defun belf-toggle-follow-mode ()
+ (interactive)
+ (setq belf-follow-mode (not belf-follow-mode)))
+
+
+(defun belf-previous-line ()
+ (interactive)
+ (previous-line)
+ (when belf-follow-mode
+ (belf-book-infobox-at-point)))
+
+(defun belf-next-line ()
+ (interactive)
+ (next-line)
+ (when belf-follow-mode
+ (belf-book-infobox-at-point)))
+
+(defun belf-show-in-dired ()
+ (interactive)
+ (dired-jump-other-window (tabulated-list-get-id)))
(defun belf-open-book ()
(interactive)
diff --git a/emacs/.emacs.d/lisp/my/infobox.el b/emacs/.emacs.d/lisp/my/infobox.el
index 9c5c7b1..2a17dc9 100644
--- a/emacs/.emacs.d/lisp/my/infobox.el
+++ b/emacs/.emacs.d/lisp/my/infobox.el
@@ -31,7 +31,11 @@
(cond ((stringp v) v)
((eq v t) "YES")
((eq v :json-false) "NO")
- ((seqp v) (mapconcat #'identity v ", "))
+ ((seqp v)
+ (mapconcat
+ (lambda (x) (if (stringp x) x (prin1-to-string x)))
+ v
+ ", "))
(t (format "%s" v))))
(defun infobox-default-specs (info)
@@ -66,14 +70,15 @@ something like
;; TODO: use a more standard function than
;; `my-make-filename-from-url'
(when-let* ((thumb-url (alist-get "Thumbnail" info nil nil 'equal))
- (file-name (file-name-concat
- "/tmp"
- (my-make-filename-from-url thumb-url))))
- (url-copy-file (message thumb-url) file-name t)
+ (file-name (make-temp-name "/tmp/infobox-")))
+ (url-copy-file thumb-url file-name t)
(insert-image (create-image file-name nil nil
- :max-width (window-width nil t)))
+ :max-width (window-pixel-width)
+ :max-height (/ (window-pixel-height) 2)))
(insert "\n")
- (setq n-rows (1+ n-rows)))
+ (setq n-rows (1+ n-rows))
+ (setq info (assoc-delete-all "Thumbnail" info))
+ )
(seq-do
(lambda (pair)
(when pair