remyhonig / elfeed-org

Configure the Elfeed RSS reader with an Orgmode file
338 stars 34 forks source link

Feature request: Read entry properties, assign to feed meta #48

Open alphapapa opened 6 years ago

alphapapa commented 6 years ago

Hi,

It would be nice if elfeed-org would read Org properties on entries using org-entry-get-with-inheritance and assign selected properties to feeds' meta slot. I'm using this with some custom code to set face properties for feeds, which I then draw with some custom code. For example, I have an entry like this in the Org file:

**** Emacs                                                         :Emacs:
:PROPERTIES:
:foreground: white
:background: #7F5AB6
:END:

***** [[http://planet.emacsen.org/atom.xml][Planet Emacs]]                                           :aggregator:
***** [[https://www.reddit.com/r/emacs/.rss][/r/Emacs]]                                                   :Reddit:

Then after reading that Org file with my code, those feeds' meta slots have :background and :foreground properties. Then I draw the feed items, applying the colors to the feed title, which looks something like this: out1

I wanted to add this feature to elfeed-org myself, but I got a bit lost in the code while looking for the right way to add it, so I ended up writing a function to read the Org file myself (without duplicating all of elfeed-org's features). But it would be preferable to have this implemented in elfeed-org itself. :)

In case anyone else finds the code useful, here it is:

(require 'org)

(require 'dash)
(require 'kv)

(require 'elfeed-db)

(cl-defstruct org-link
  protocol path description)

(defun org-match-link (&optional s)
  "Return an `org-link' struct if an Org link is matched in string S or at point.
Matches with `org-bracket-link-analytic-regexp'."
  ;; NOTE: HTTP paths will start with two slashes.
  (cond (s (when (string-match org-bracket-link-analytic-regexp s)
             (make-org-link :protocol (match-string-no-properties 2 s)
                            :path (match-string-no-properties 3 s)
                            :description (match-string-no-properties 5 s))))
        (t (when (looking-at org-bracket-link-analytic-regexp)
             (make-org-link :protocol (match-string-no-properties 2)
                            :path (match-string-no-properties 3)
                            :description (match-string-no-properties 5))))))

(defun ap/elfeed-org--feeds-in (buffer)
  "Return list of feeds in Org BUFFER."
  (with-current-buffer buffer
    (org-with-wide-buffer
     (let ((org-use-tag-inheritance t)
           (org-use-property-inheritance t))
       (goto-char (point-min))
       (when (org-before-first-heading-p)
         (outline-next-heading))
       (cl-loop while (re-search-forward org-complex-heading-regexp nil t)
                collect (save-excursion
                          (goto-char (match-beginning 0))
                          (let* ((heading (substring-no-properties (org-get-heading t t)))
                                 url title tags meta)
                            (when (cond ((string-match (rx bos "http" (optional "s") "://") heading)
                                         (setq url heading))
                                        ((when-let* ((link (org-match-link heading)))
                                           (setq url (concat (org-link-protocol link) ":"
                                                             (org-link-path link))
                                                 title (org-link-description link)))))
                              (setq tags (->> (org-get-tags-at)
                                              (--map (->> it substring-no-properties intern))
                                              (delq 'elfeed))
                                    meta (ap/elfeed-org--entry-properties))
                              (let ((feed (elfeed-db-get-feed url)))
                                (setf (elfeed-feed-meta feed)
                                      (kvplist-merge (elfeed-feed-meta feed) meta))
                                (setf (elfeed-meta feed :title) title)
                                (setf (elfeed-meta feed :tags) tags)
                                feed)))))))))

(defcustom ap/elfeed-org-properties '("background" "foreground" "face")
  "List of properties to read from entries, which will be applied to the feed's metadata."
  :type '(repeat string))

(defun ap/elfeed-org--entry-properties ()
  "Return plist of selected properties in current entry."
  (cl-loop for property in ap/elfeed-org-properties
           for value = (org-entry-get-with-inheritance property)
           for keyword = (intern (concat ":" property))
           append (list keyword value)))

The Org file is read like this:

  (when-let* ((feeds (-non-nil (ap/elfeed-org--feeds-in (get-buffer "elfeed.org")))))
    (setq elfeed-feeds
          (--map (cons (elfeed-feed-url it)
                       (elfeed-meta it :tags))
                 feeds)))
jackmac92 commented 2 years ago

+1 I think something like this is necessary for integration with elfeed-protocol e.g.