aboutsummaryrefslogtreecommitdiff
path: root/modules/calibredb-epub-config.el
diff options
context:
space:
mode:
Diffstat (limited to 'modules/calibredb-epub-config.el')
-rw-r--r--modules/calibredb-epub-config.el134
1 files changed, 74 insertions, 60 deletions
diff --git a/modules/calibredb-epub-config.el b/modules/calibredb-epub-config.el
index 1e6437d26..38aa0de05 100644
--- a/modules/calibredb-epub-config.el
+++ b/modules/calibredb-epub-config.el
@@ -6,46 +6,17 @@
;; Layer: 4 (Optional).
;; Category: O/D/P.
;; Load shape: eager.
-;; Eager reason: none; optional ebook workflow, a command-loaded deferral
-;; candidate for Phase 4.
-;; Top-level side effects: one add-hook, one advice-add, package config.
-;; Runtime requires: user-constants, subr-x.
+;; Eager reason: none; ebook commands can load by command.
+;; Top-level side effects: one hook, one advice, package config.
+;; Runtime requires: user-constants, subr-x, transient.
;; Direct test load: yes.
;;
-;; This module provides a comprehensive ebook management and reading experience
-;; within Emacs, integrating CalibreDB for library management and Nov for EPUB
-;; reading.
+;; CalibreDB and Nov integration for browsing the Calibre library and reading
+;; EPUBs inside Emacs. The module adds a curated CalibreDB transient, filter
+;; helpers, Nov typography, image centering, and reader-to-library navigation.
;;
-;; FEATURES:
-;; - CalibreDB integration for managing your Calibre ebook library
-;; - Nov mode for reading EPUB files with customized typography and layout
-;; - Seamless navigation between Nov reading buffers and CalibreDB entries
-;; - Image centering in EPUB documents without modifying buffer text
-;; - Quick filtering and searching within your ebook library
-;;
-;; KEY BINDINGS:
-;; - M-B: Open CalibreDB library browser
-;; - In CalibreDB search mode:
-;; - l: Filter by tag
-;; - L: Clear all filters
-;; - In Nov mode:
-;; - z: Open current EPUB in external viewer (zathura)
-;; - C-c C-b: Jump to CalibreDB entry for current book
-;; - m: Set bookmark
-;; - b: List bookmarks
-;;
-;; WORKFLOW:
-;; 1. Press M-B to browse your Calibre library
-;; 2. Use filters (l for tags, L to clear) to narrow results
-;; 3. Open an EPUB to read it in Nov with optimized typography
-;; 4. While reading, use C-c C-b to jump back to the book's metadata
-;; 5. Use z to open in external reader when needed
-;;
-;; CONFIGURATION NOTES:
-;; - Prefers EPUB format when available, falls back to PDF
-;; - Centers images in EPUB documents using display properties
-;; - Applies custom typography with larger fonts for comfortable reading
-;; - Uses visual-fill-column for centered text with appropriate margins
+;; EPUB is preferred when available; external opening remains available for
+;; formats or workflows better handled outside Emacs.
;;; Code:
@@ -62,6 +33,15 @@
;; calibredb commands the curated menu drives (all autoloaded by calibredb)
(declare-function calibredb-switch-library "calibredb" ())
+(declare-function calibredb-search-keyword-filter "calibredb-search")
+
+;; calibredb's filter-scope flags (set in `cj/--calibredb-open-to-favorites');
+;; declared special so the assignments compile clean when calibredb is absent.
+(defvar calibredb-tag-filter-p)
+(defvar calibredb-favorite-filter-p)
+(defvar calibredb-author-filter-p)
+(defvar calibredb-date-filter-p)
+(defvar calibredb-format-filter-p)
(declare-function calibredb-filter-by-book-format "calibredb" ())
(declare-function calibredb-filter-by-author-sort "calibredb" ())
(declare-function calibredb-search-clear-filter "calibredb" ())
@@ -116,6 +96,26 @@ which re-applies `calibredb-search-filter' instead."
(setq calibredb-sort-by field)
(calibredb-search-refresh-or-resume))
+(defun cj/--calibredb-open-to-favorites (&rest _)
+ "Filter the calibredb search to books tagged `calibredb-favorite-keyword'.
+Advice (:after) on `calibredb' so every launch lands on the favorite-keyword
+books (Craig's \"in-progress\" reading list); clear with L / x to see the
+whole library. Scopes to the tag field (sets `calibredb-tag-filter-p',
+clears the other filter-scope flags), because a bare keyword filter matches
+the keyword in any field -- title, author, or the description -- and would
+surface books that merely mention it. No-op unless a non-empty string
+keyword is set."
+ (when (and (boundp 'calibredb-favorite-keyword)
+ (stringp calibredb-favorite-keyword)
+ (not (string-empty-p calibredb-favorite-keyword))
+ (fboundp 'calibredb-search-keyword-filter))
+ (setq calibredb-tag-filter-p t
+ calibredb-favorite-filter-p nil
+ calibredb-author-filter-p nil
+ calibredb-date-filter-p nil
+ calibredb-format-filter-p nil)
+ (calibredb-search-keyword-filter calibredb-favorite-keyword)))
+
(use-package calibredb
:commands calibredb
:bind
@@ -184,7 +184,10 @@ which re-applies `calibredb-search-filter' instead."
(setq calibredb-order "asc")
(setq calibredb-id-width 7)
(setq calibredb-favorite-icon "🔖")
- (setq calibredb-favorite-keyword "in-progress"))
+ (setq calibredb-favorite-keyword "in-progress")
+ ;; Open every calibredb launch (dashboard, M-x, elsewhere) filtered to the
+ ;; in-progress favorites; L / x clears to the whole library.
+ (advice-add 'calibredb :after #'cj/--calibredb-open-to-favorites))
;; ------------------------------ Nov Epub Reader ------------------------------
@@ -207,7 +210,6 @@ Adjust it live with `cj/nov-widen-text' and `cj/nov-narrow-text'.")
(if (and buffer-file-name
(string-match-p "\\.epub\\'" buffer-file-name))
(progn
- ;; Load nov if not already loaded
(unless (featurep 'nov)
(require 'nov nil t))
;; Call nov-mode if available, otherwise fallback to default behavior
@@ -404,6 +406,12 @@ Try to use the Calibre book id from the parent folder name (for example,
(calibredb-search-keyword-filter "")
(message "CalibreDB: no metadata; showing all"))))))
+(require 'system-lib)
+;; nov renders epub via shr, which paints with manual `face' properties. Left in
+;; `global-font-lock-mode' font-lock overwrites them and the book loses its
+;; colors, the same issue as elfeed-show and mu4e-view. Exclude nov-mode.
+(cj/exclude-from-global-font-lock 'nov-mode)
+
(use-package nov
:mode
("\\.epub\\'" . nov-mode)
@@ -432,14 +440,15 @@ Try to use the Calibre book id from the parent folder name (for example,
;; ------------------------- Nov bookmark naming -------------------------------
;; In a nov buffer "m" is bound to `bookmark-set' (above). nov's
-;; `nov-bookmark-make-record' names the record after `(buffer-name)' -- the EPUB
-;; filename, extension and all. Rebuild it as "Author, Title" parsed from the
-;; filename: under Calibre's "<Title> - <Author>.epub" naming the filename is
-;; more complete than the EPUB's embedded metadata (which carries truncated
-;; titles and author-sort "Last, First" forms).
-
-(defun cj/--nov-clean-title (s)
- "Clean a title or author S parsed from an EPUB filename, or nil when blank.
+;; Both nov (EPUB) and pdf-view (PDF) name a new bookmark after the buffer --
+;; the file's name, extension and all. Rebuild it as "Author, Title" parsed
+;; from the filename: under Calibre's "<Title> - <Author>.<ext>" naming the
+;; filename is more complete than the file's embedded metadata (which carries
+;; truncated titles and author-sort "Last, First" forms). One :filter-return
+;; advice serves both record functions; the parser is extension-agnostic.
+
+(defun cj/--reading-clean-title (s)
+ "Clean a title or author S parsed from a book filename, or nil when blank.
Restores a colon where Calibre sanitized \":\" to \"_\" (\"Frege_ A Guide\"
-> \"Frege: A Guide\"), turns any leftover underscore into a space, and
collapses runs of whitespace."
@@ -449,34 +458,39 @@ collapses runs of whitespace."
(out (string-trim (replace-regexp-in-string "[ \t]+" " " spaced))))
(and (not (string-empty-p out)) out))))
-(defun cj/--nov-bookmark-name-from-file (path)
- "Return \"Author, Title\" derived from an EPUB PATH's filename, or nil.
+(defun cj/--reading-bookmark-name-from-file (path)
+ "Return \"Author, Title\" derived from a book PATH's filename, or nil.
Splits the filename (sans extension) on its last \" - \" into title and
author per Calibre's \"<Title> - <Author>\" convention, restoring colons and
reordering to \"Author, Title\". Falls back to the cleaned whole name when
-there is no \" - \" separator."
+there is no \" - \" separator. Extension-agnostic, so it serves EPUB and PDF."
(when (and (stringp path) (not (string-empty-p path)))
(let ((base (file-name-sans-extension (file-name-nondirectory path))))
(if (string-match "\\`\\(.+\\) - \\(.+\\)\\'" base)
- (let ((title (cj/--nov-clean-title (match-string 1 base)))
- (author (cj/--nov-clean-title (match-string 2 base))))
+ (let ((title (cj/--reading-clean-title (match-string 1 base)))
+ (author (cj/--reading-clean-title (match-string 2 base))))
(cond ((and author title) (format "%s, %s" author title))
(title title)
(author author)
(t nil)))
- (cj/--nov-clean-title base)))))
-
-(defun cj/--nov-bookmark-rename-record (record)
- "Replace RECORD's bookmark name with \"Author, Title\" from its EPUB filename.
-Advice (:filter-return) on `nov-bookmark-make-record'. RECORD is
-\(NAME . ALIST) carrying a `filename'; left unchanged when no name derives."
- (let ((name (cj/--nov-bookmark-name-from-file
+ (cj/--reading-clean-title base)))))
+
+(defun cj/--reading-bookmark-rename-record (record)
+ "Replace RECORD's bookmark name with \"Author, Title\" from its filename.
+Advice (:filter-return) on `nov-bookmark-make-record' and
+`pdf-view-bookmark-make-record'. RECORD is (NAME . ALIST) carrying a
+`filename'; left unchanged when no name derives."
+ (let ((name (cj/--reading-bookmark-name-from-file
(alist-get 'filename (cdr record)))))
(if name (cons name (cdr record)) record)))
(with-eval-after-load 'nov
(advice-add 'nov-bookmark-make-record :filter-return
- #'cj/--nov-bookmark-rename-record))
+ #'cj/--reading-bookmark-rename-record))
+
+(with-eval-after-load 'pdf-view
+ (advice-add 'pdf-view-bookmark-make-record :filter-return
+ #'cj/--reading-bookmark-rename-record))
(defun cj/--nov-image-padding-cols (col-width img-px font-width-px)
"Return left-padding columns to center an IMG-PX-wide image in COL-WIDTH cols.