summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvin <git@vineetk.net>2025-12-08 16:51:59 -0500
committervin <git@vineetk.net>2025-12-08 16:55:05 -0500
commitaf9627b43eaf4e40fadc9be82ccc2f10f33746a3 (patch)
treed66c4aed05c37f159718e4a8af6b8b80134f4116
parent2113d0279b035f183c0c05627bbd2b0779e98f35 (diff)
attempt to make the plugin more async-y
-rw-r--r--elfeed-custom-feeds-github.el76
-rw-r--r--elfeed-custom-feeds-huggingface.el83
-rw-r--r--elfeed-custom-feeds.el3
3 files changed, 106 insertions, 56 deletions
diff --git a/elfeed-custom-feeds-github.el b/elfeed-custom-feeds-github.el
index 660350d..a857963 100644
--- a/elfeed-custom-feeds-github.el
+++ b/elfeed-custom-feeds-github.el
@@ -57,15 +57,15 @@
57 (funcall cb r dn items)))) 57 (funcall cb r dn items))))
58 nil t))))) 58 nil t)))))
59 59
60(defun elfeed-custom-feeds-github--prs-to-elfeed (repo display-name prs) 60(defun elfeed-custom-feeds-github--prs-to-elfeed (repo display-name prs completion-callback)
61 "Convert GitHub PRS to elfeed entries for REPO with DISPLAY-NAME." 61 "Convert GitHub PRS to elfeed entries for REPO with DISPLAY-NAME.
62Call COMPLETION-CALLBACK with (repo display-name new-entries) when done."
62 (let ((feed-url (format "https://github.com/%s/pulls" repo)) 63 (let ((feed-url (format "https://github.com/%s/pulls" repo))
63 (new-count 0)
64 (new-entries ())) 64 (new-entries ()))
65 (let ((feed (elfeed-db-get-feed feed-url))) 65 (let ((feed (elfeed-db-get-feed feed-url)))
66 (setf (elfeed-feed-url feed) feed-url 66 (setf (elfeed-feed-url feed) feed-url
67 (elfeed-feed-title feed) (format "GitHub: %s [%s]" repo display-name))) 67 (elfeed-feed-title feed) (format "GitHub: %s [%s]" repo display-name)))
68 68
69 (dolist (pr prs) 69 (dolist (pr prs)
70 (let* ((number (alist-get 'number pr)) 70 (let* ((number (alist-get 'number pr))
71 (title-text (alist-get 'title pr)) 71 (title-text (alist-get 'title pr))
@@ -86,7 +86,7 @@
86 (format "<p><strong>Labels:</strong> %s</p>" 86 (format "<p><strong>Labels:</strong> %s</p>"
87 (mapconcat 'identity labels ", ")) 87 (mapconcat 'identity labels ", "))
88 "") 88 "")
89 (replace-regexp-in-string "<" "&lt;" 89 (replace-regexp-in-string "<" "&lt;"
90 (replace-regexp-in-string ">" "&gt;" body))))) 90 (replace-regexp-in-string ">" "&gt;" body)))))
91 (unless (elfeed-db-get-entry entry-id) 91 (unless (elfeed-db-get-entry entry-id)
92 (let ((entry (elfeed-entry--create 92 (let ((entry (elfeed-entry--create
@@ -98,27 +98,59 @@
98 :content-type 'html 98 :content-type 'html
99 :feed-id feed-url 99 :feed-id feed-url
100 :tags '(unread github)))) 100 :tags '(unread github))))
101 (push entry new-entries) 101 (push entry new-entries)))))
102 (setq new-count (1+ new-count)))))) 102
103 103 ;; Call completion callback with results
104 (when new-entries 104 (funcall completion-callback repo display-name (nreverse new-entries))))
105 (elfeed-db-add (nreverse new-entries)))
106
107 (message "GitHub feed %s [%s]: %d new entries" repo display-name new-count)
108 (when (get-buffer "*elfeed-search*")
109 (with-current-buffer "*elfeed-search*"
110 (elfeed-search-update--force)))))
111 105
112;;;###autoload 106;;;###autoload
113(defun elfeed-custom-feeds-github-update () 107(defun elfeed-custom-feeds-github-update ()
114 "Update all GitHub PR feeds." 108 "Update all GitHub PR feeds asynchronously.
109Batches all entries and updates UI once at the end to avoid blocking."
115 (interactive) 110 (interactive)
116 (cl-loop for (repo query display-name) in elfeed-custom-github-pr-queries 111 (let* ((total (length elfeed-custom-github-pr-queries))
117 for i from 0 112 (processed 0)
118 for delay = (* i elfeed-custom-github-request-delay) 113 (all-new-entries '())
119 do (elfeed-custom-feeds-github--fetch-prs repo query display-name 114 (results '()))
120 #'elfeed-custom-feeds-github--prs-to-elfeed 115
121 delay))) 116 (when (zerop total)
117 (message "No GitHub PR queries configured")
118 (cl-return-from elfeed-custom-feeds-github-update nil))
119
120 ;; Completion callback that batches everything
121 (let ((completion-callback
122 (lambda (repo display-name new-entries)
123 (setq processed (1+ processed))
124 (when new-entries
125 (setq all-new-entries (append all-new-entries new-entries)))
126 (push (list repo display-name (length new-entries)) results)
127
128 ;; When all queries are done, batch add to DB and update UI once
129 (when (= processed total)
130 (let ((total-count (length all-new-entries)))
131 (when (> total-count 0)
132 (elfeed-db-add all-new-entries))
133 ;; Report results for each feed
134 (dolist (result (nreverse results))
135 (message "GitHub feed %s [%s]: %d new entries"
136 (nth 0 result) (nth 1 result) (nth 2 result)))
137 ;; Defer UI update to avoid blocking
138 (run-with-idle-timer 0.1 nil
139 (lambda ()
140 (when (get-buffer "*elfeed-search*")
141 (with-current-buffer "*elfeed-search*"
142 (elfeed-search-update--force))))))))))
143
144 ;; Start all async fetches
145 (cl-loop for (repo query display-name) in elfeed-custom-github-pr-queries
146 for i from 0
147 for delay = (* i elfeed-custom-github-request-delay)
148 do (elfeed-custom-feeds-github--fetch-prs
149 repo query display-name
150 (lambda (r dn items)
151 (elfeed-custom-feeds-github--prs-to-elfeed r dn items completion-callback))
152 delay)))
153 nil))
122 154
123(provide 'elfeed-custom-feeds-github) 155(provide 'elfeed-custom-feeds-github)
124 156
diff --git a/elfeed-custom-feeds-huggingface.el b/elfeed-custom-feeds-huggingface.el
index a3bfdb0..43f64c8 100644
--- a/elfeed-custom-feeds-huggingface.el
+++ b/elfeed-custom-feeds-huggingface.el
@@ -84,7 +84,8 @@
84 nil t))) 84 nil t)))
85 85
86(defun elfeed-custom-feeds-huggingface--create-entry (org-name model model-card-html) 86(defun elfeed-custom-feeds-huggingface--create-entry (org-name model model-card-html)
87 "Create an elfeed entry for MODEL with model card HTML content." 87 "Create an elfeed entry for MODEL with model card HTML content.
88Returns the entry if it's new, nil otherwise."
88 (let* ((feed-url (format "https://huggingface.co/%s" org-name)) 89 (let* ((feed-url (format "https://huggingface.co/%s" org-name))
89 (model-id (alist-get 'id model)) 90 (model-id (alist-get 'id model))
90 (last-modified (alist-get 'lastModified model)) 91 (last-modified (alist-get 'lastModified model))
@@ -96,64 +97,80 @@
96 (title (format "[%s] %s" org-name model-id)) 97 (title (format "[%s] %s" org-name model-id))
97 (date (float-time (date-to-time last-modified))) 98 (date (float-time (date-to-time last-modified)))
98 (metadata (format "<h2>%s</h2>\n<p><strong>Type:</strong> %s</p>\n<p><strong>Downloads:</strong> %d</p>\n<p><strong>Last Modified:</strong> %s</p>\n%s\n<hr>\n" 99 (metadata (format "<h2>%s</h2>\n<p><strong>Type:</strong> %s</p>\n<p><strong>Downloads:</strong> %d</p>\n<p><strong>Last Modified:</strong> %s</p>\n%s\n<hr>\n"
99 model-id 100 model-id
100 pipeline 101 pipeline
101 downloads 102 downloads
102 last-modified 103 last-modified
103 (if tags 104 (if tags
104 (format "<p><strong>Tags:</strong> %s</p>" 105 (format "<p><strong>Tags:</strong> %s</p>"
105 (mapconcat 'identity (cl-subseq tags 0 (min 10 (length tags))) ", ")) 106 (mapconcat 'identity (cl-subseq tags 0 (min 10 (length tags))) ", "))
106 ""))) 107 "")))
107 (content (concat metadata 108 (content (concat metadata
108 (or model-card-html "<p><em>Model card not available</em></p>")))) 109 (or model-card-html "<p><em>Model card not available</em></p>"))))
109 (unless (elfeed-db-get-entry entry-id) 110 (unless (elfeed-db-get-entry entry-id)
110 (let ((entry (elfeed-entry--create 111 (elfeed-entry--create
111 :id entry-id 112 :id entry-id
112 :title title 113 :title title
113 :link link 114 :link link
114 :date date 115 :date date
115 :content content 116 :content content
116 :content-type 'html 117 :content-type 'html
117 :feed-id feed-url 118 :feed-id feed-url
118 :tags '(unread huggingface)))) 119 :tags '(unread huggingface)))))
119 (elfeed-db-add (list entry))
120 (when (get-buffer "*elfeed-search*")
121 (with-current-buffer "*elfeed-search*"
122 (elfeed-search-update--force)))
123 entry))))
124 120
125(defun elfeed-custom-feeds-huggingface--models-to-elfeed (org-name models) 121(defun elfeed-custom-feeds-huggingface--models-to-elfeed (org-name models)
126 "Convert HuggingFace MODELS to elfeed entries for ORG-NAME." 122 "Convert HuggingFace MODELS to elfeed entries for ORG-NAME.
127 (let ((feed-url (format "https://huggingface.co/%s" org-name)) 123Batches all entries and updates UI once at the end to avoid blocking."
128 (new-count 0) 124 (let* ((feed-url (format "https://huggingface.co/%s" org-name))
129 (total (length models)) 125 (total (length models))
130 (org org-name)) 126 (new-entries '())
127 (processed 0))
128 ;; Set up feed metadata
131 (let ((feed (elfeed-db-get-feed feed-url))) 129 (let ((feed (elfeed-db-get-feed feed-url)))
132 (setf (elfeed-feed-url feed) feed-url 130 (setf (elfeed-feed-url feed) feed-url
133 (elfeed-feed-title feed) (format "HuggingFace: %s" org-name))) 131 (elfeed-feed-title feed) (format "HuggingFace: %s" org-name)))
134 132
133 ;; Return immediately if no models
134 (when (zerop total)
135 (message "HuggingFace feed %s: no models to process" org-name)
136 (cl-return-from elfeed-custom-feeds-huggingface--models-to-elfeed nil))
137
138 ;; Fetch model cards asynchronously and batch entries
135 (cl-loop for model in models 139 (cl-loop for model in models
136 for i from 0 140 for i from 0
137 for model-id = (alist-get 'id model) 141 for model-id = (alist-get 'id model)
138 for delay = (* i elfeed-custom-huggingface-request-delay) 142 for delay = (* i elfeed-custom-huggingface-request-delay)
139 do (elfeed-custom-feeds-huggingface--fetch-model-card 143 do (elfeed-custom-feeds-huggingface--fetch-model-card
140 model-id 144 model-id
141 (let ((m model)) 145 (let ((m model))
142 (lambda (html) 146 (lambda (html)
143 (when (elfeed-custom-feeds-huggingface--create-entry org m html) 147 (let ((entry (elfeed-custom-feeds-huggingface--create-entry org-name m html)))
144 (setq new-count (1+ new-count))) 148 (when entry
145 (when (= new-count total) 149 (push entry new-entries)))
146 (message "HuggingFace feed %s: %d new entries" org new-count)))) 150 (setq processed (1+ processed))
151 ;; When all are processed, batch add to DB and update UI once
152 (when (= processed total)
153 (let ((count (length new-entries)))
154 (when (> count 0)
155 (elfeed-db-add (nreverse new-entries)))
156 (message "HuggingFace feed %s: %d new entries" org-name count)
157 ;; Defer UI update to avoid blocking
158 (run-with-idle-timer 0.1 nil
159 (lambda ()
160 (when (get-buffer "*elfeed-search*")
161 (with-current-buffer "*elfeed-search*"
162 (elfeed-search-update--force)))))))))
147 delay)) 163 delay))
148
149 nil)) 164 nil))
150 165
151;;;###autoload 166;;;###autoload
152(defun elfeed-custom-feeds-huggingface-update () 167(defun elfeed-custom-feeds-huggingface-update ()
153 "Update all HuggingFace organization feeds." 168 "Update all HuggingFace organization feeds asynchronously.
169Returns immediately while updates happen in the background."
154 (interactive) 170 (interactive)
155 (dolist (org elfeed-custom-huggingface-orgs) 171 (dolist (org elfeed-custom-huggingface-orgs)
156 (elfeed-custom-feeds-huggingface--fetch-models org #'elfeed-custom-feeds-huggingface--models-to-elfeed))) 172 (elfeed-custom-feeds-huggingface--fetch-models org #'elfeed-custom-feeds-huggingface--models-to-elfeed))
173 nil)
157 174
158(provide 'elfeed-custom-feeds-huggingface) 175(provide 'elfeed-custom-feeds-huggingface)
159 176
diff --git a/elfeed-custom-feeds.el b/elfeed-custom-feeds.el
index a64cc69..0e8ff6e 100644
--- a/elfeed-custom-feeds.el
+++ b/elfeed-custom-feeds.el
@@ -31,7 +31,8 @@
31 (interactive) 31 (interactive)
32 (elfeed-custom-feeds-huggingface-update) 32 (elfeed-custom-feeds-huggingface-update)
33 (elfeed-custom-feeds-github-update) 33 (elfeed-custom-feeds-github-update)
34 (message "Updating custom feeds...")) 34 (message "Updating custom feeds...")
35 nil)
35 36
36(provide 'elfeed-custom-feeds) 37(provide 'elfeed-custom-feeds)
37 38