about summary refs log tree commit diff stats
path: root/lisp/+elfeed.el
blob: 85d9c3bb43331f7fca41a04034795716094e7218 (plain)
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
;;; +elfeed.el -*- lexical-binding: t; -*-

;;; Code:

(require 'elfeed)

;; https://karthinks.com/software/lazy-elfeed/
(defun +elfeed-scroll-up-command (&optional arg)
  "Scroll up or go to next feed item in Elfeed"
  (interactive "^P")
  (let ((scroll-error-top-bottom nil))
    (condition-case-unless-debug nil
        (scroll-up-command arg)
      (error (elfeed-show-next)))))

(defun +elfeed-scroll-down-command (&optional arg)
  "Scroll up or go to next feed item in Elfeed"
  (interactive "^P")
  (let ((scroll-error-top-bottom nil))
    (condition-case-unless-debug nil
        (scroll-down-command arg)
      (error (elfeed-show-prev)))))

(defun +elfeed-search-browse-generic ()
  "Browse a url with `browse-url-generic-browser'."
  (interactive)
  (elfeed-search-browse-url t))

(defun +elfeed-show-browse-generic ()
  "Browse a url with `browse-url-generic-browser'."
  (interactive)
  (elfeed-show-visit t))

;;; Fetch feeds async
;; https://github.com/skeeto/elfeed/issues/367

(defun +elfeed--update-message ()
  (message "[Elfeed] Update in progress")
  'ignore)

(defvar +elfeed--update-running nil "Whether an update is currently running.")
(defvar +elfeed--update-count 0 "How many times `+elfeed-update-command' has run.")

(defun +elfeed-update-command ()
  (interactive)
  (unless (or +elfeed--update-running
              (derived-mode-p 'elfeed-show-mode 'elfeed-search-mode))
    (let ((script (expand-file-name "/tmp/elfeed-update.el"))
          (update-message-format "[Elfeed] Background update: %s"))
      (setq +elfeed--update-running t)
      (elfeed-db-save)
      (advice-add 'elfeed :override #'+elfeed--update-message)
      (ignore-errors (kill-buffer "*elfeed-search*"))
      (ignore-errors (kill-buffer "*elfeed-log*"))
      (elfeed-db-unload)
      (make-directory (file-name-directory script) :parents)
      (with-temp-buffer
        (insert
         (prin1-to-string ;; Print the following s-expression to a string
          `(progn
             ;; Set up the environment
             (setq lexical-binding t)
             (load (locate-user-emacs-file "early-init"))
             (dolist (pkg '(elfeed elfeed-org))
               (straight-use-package pkg)
               (require pkg))
             ;; Copy variables from current environment
             (progn
               ,@(cl-loop for copy-var in '(rmh-elfeed-org-files
                                            elfeed-db-directory
                                            elfeed-curl-program-name
                                            elfeed-use-curl
                                            elfeed-curl-extra-arguments
                                            elfeed-enclosure-default-dir)
                          collect `(progn (message "%S = %S" ',copy-var ',(symbol-value copy-var))
                                          (setq ,copy-var ',(symbol-value copy-var)))))
             ;; Define new variables for this environment
             (progn
               ,@(cl-loop for (new-var . new-val) in '((elfeed-curl-max-connections . 4))
                          collect `(progn (message "%S = %S" ',new-var ',new-val)
                                          (setq ,new-var ',new-val))))
             ;; Redefine `elfeed-log' to log everything
             (defun elfeed-log (level fmt &rest objects)
               (princ (format "[%s] [%s]: %s\n"
                              (format-time-string "%F %T")
                              level
                              (apply #'format fmt objects))))
             ;; Run elfeed
             (elfeed-org)
             (elfeed)
             (elfeed-db-load)
             (elfeed-update)
             ;; Wait for `elfeed-update' to finish
             (while (> (elfeed-queue-count-total) 0)
               (sleep-for 5)
               (message "%s" (elfeed-queue-count-total))
               (accept-process-output))
             ;; Garbage collect and save the database
             (elfeed-db-gc)
             (elfeed-db-save)
             (princ (format ,update-message-format "done.")))))
        (write-file script))
      (chmod script #o777)
      (message update-message-format "start")
      (set-process-sentinel (start-process-shell-command
                             "Elfeed" "*+elfeed-update-background*"
                             (format "nice -%d %s %s" 15 "emacs -Q --script" script))
                            (lambda (proc stat)
                              (advice-remove 'elfeed #'+elfeed--update-message)
                              (setq +elfeed--update-running nil)
                              (unless (string= stat "killed")
                                (setq +elfeed--update-count (1+ +elfeed--update-count)))
                              (message update-message-format (string-trim stat)))))))

(defvar +elfeed--update-timer nil "Timer for `elfeed-update-command'.")
(defvar +elfeed--update-first-time 6 "How long to wait for the first time.")
(defvar +elfeed--update-repeat (* 60 15) "How long between updates.")

(defun +elfeed--cancel-update-timer ()
  "Cancel `+elfeed--update-timer'."
  (unless +elfeed--update-running
    (ignore-errors (cancel-timer +elfeed--update-timer))
    (setq +elfeed--update-timer nil)))

(defun +elfeed--reinstate-update-timer ()
  "Reinstate `+elfeed--update-timer'."
  ;; First, unload the db
  (elfeed-db-save)
  (elfeed-db-unload)
  (setq +elfeed--update-timer
        (run-at-time +elfeed--update-first-time
                     +elfeed--update-repeat
                     #'+elfeed-update-command)))

(define-minor-mode +elfeed-update-async-mode
  "Minor mode to update elfeed async-style."
  :global t
  (if +elfeed-update-async-mode
      (progn                            ; enable
        (+elfeed--reinstate-update-timer)
        (advice-add 'elfeed :before '+elfeed--cancel-update-timer)
        (advice-add 'elfeed-search-quit-window :after '+elfeed--reinstate-update-timer))
    (progn                              ; disable
      (advice-remove 'elfeed '+elfeed--cancel-update-timer)
      (advice-remove 'elfeed-search-quit-window '+elfeed--reinstate-update-timer)
      (+elfeed--cancel-update-timer))))

(provide '+elfeed)
;;; +elfeed.el ends here