Elfeed - mac.into.sh

Elfeed

Introduction

Usually people start these things out by explaining what RSS is and all that. I don’t think I’ll be doing that. I like RSS because I like knowing when new things happen, and I don’t want to check a bunch of different services all the time. Beyond that, I also really like the idea of being able to filter out feed items that don’t appeal to me. I don’t mind if I can only read stuff on my computer, so I haven’t set up any sort of sync with my phone, although it should be possible to do that.

I have Elfeed set up in a single use-package declaration, and I’ve pulled all the individual functions out into their own bits so as to talk about them separately.

(use-package elfeed
  :straight t
  :bind
  (("C-x w" . actuator-elfeed-load-db-and-open)
   :map elfeed-search-mode-map
   ("A" . actuator-elfeed-show-all)
   ("U" . actuator-elfeed-show-unread)
   ("q" . actuator-elfeed-save-db-and-bury)
   ("R" . actuator-elfeed-mark-all-as-read))
  :custom
  (elfeed-search-filter "@6-months-ago +unread ")
  :config
  <<shortcuts>>
  <<faces>>
  <<filters>>
  <<feeds>>
  <<load-quit>>
  <<mark-all-as-read>>)

Shortcuts

I built a few shortcuts to switch between different tag views that I commonly use. Elfeed has support for Emacs’ bookmarks, so I just needed to make bookmarks for the views I wanted. I set up the search how I like it (s) then made a bookmark entry (C-x r m) called, say elfeed-all. I can call that bookmark from anywhere in Emacs to go to that elfeed view, but I also decided to steal some functions from Pragmatic Emacs to make single-letter keybindings in elfeed.

(defun actuator-elfeed-show-all ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-all"))
(defun actuator-elfeed-show-unread ()
  (interactive)
  (bookmark-maybe-load-default-file)
  (bookmark-jump "elfeed-unread"))

Filters

Filters are kind of the star of Elfeed. I mostly use them to remove items that I don’t want to see (or already see in other contexts—podcasts for example). I think it’s all pretty straightforward. The only thing of note that I do is adding a debug tag to each hook that hides things. That way I can tell which filter it is that’s causing problems when I make a stupid typo and suddenly a specific filter matches all entries.

(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :entry-title "sponsor\\|revenue\\|financial"
                              :add '(junk debug1)
                              :remove 'unread))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :before "2 weeks ago"
                              :add 'debug2
                              :remove 'unread))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-title "MacSparky"
                              :entry-title "focused\\|Mac Power Users\\|jazz\\|automators\\|podcast"
                              :add '(junk debug3)
                              :remove 'unread))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-title "Six Colors"
                              :entry-title "podcast\\|macworld"
                              :add '(junk debug4)
                              :remove 'unread))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-title "Longreads"
                              :entry-title "longreads"
                              :add '(junk debug5)
                              :remove 'unread))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-url "youtube\\.com"
                              :add '(video youtube)))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-url "twitchrss"
                              :add '(video twitch)))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-url "kijiji\\.ca"
                              :add '(shop kijiji)))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-url "reddit"
                              :add 'reddit))
(add-hook 'elfeed-new-entry-hook
          (elfeed-make-tagger :feed-url "ikea"
                              :entry-title "Q\\:"
                              :remove 'unread
                              :add '(junk debug6)))

Text Width

Elfeed uses the built-in package shr—Simple HTML Renderer—to display articles. That’s true of a bunch of Emacs’ packages I expect, but this is the only case where I (knowingly) make use of it so far, so I’m configuring it here for now.

(use-package shr
  :straight nil
  :custom
  (shr-width 75))

Load and quit Elfeed nicely

You don’t need to do anything special to load Elfeed. You can set up a keybinding that runs (elfeed) and it should work. I took this function from Pragmatic Emacs when I first set up Elfeed a few years ago because I wanted to keep the database in sync between multiple computers. These helper functions ensure that the database is loaded and saved at the appropriate moments. I’m not sure there’s any benefit to these if you only use them on one computer (as I do now) but I can’t find any downsides either, so they stay.

(defun actuator-elfeed-load-db-and-open ()
      "Wrapper to load the elfeed database from disk before
      opening. Taken from Pragmatic Emacs."
      (interactive)
      (window-configuration-to-register :elfeed-fullscreen)
      (delete-other-windows)
      (elfeed)
      (elfeed-db-load)
      (elfeed-search-update 1)
      (elfeed-update))
(defun actuator-elfeed-save-db-and-bury ()
  "Wrapper to save the Elfeed database to disk before burying
  buffer. Taken from Pragmatic Emacs."
  (interactive)
  (elfeed-db-save)
  (quit-window)
  (garbage-collect)
  (jump-to-register :elfeed-fullscreen))

Mark all as read

You can just go post-by-post and use r to mark individual posts as read. I stole this function from Mike Zamansky because it seemed like a nice addition.

(defun actuator-elfeed-mark-all-as-read ()
    "Mark all feeds in search as read. Taken from Mike Zamansky"
    (interactive)
    (mark-whole-buffer)
    (elfeed-search-untag-all-unread))

Faces

Changing the colours of an entry is neat, but not that useful. I mostly have this set up in order to learn how to do it, and as a vague novelty.

(add-to-list 'elfeed-search-face-alist
             '(video actuator-elfeed-video-face))
(add-to-list 'elfeed-search-face-alist
             '(image actuator-elfeed-image-face))
(add-to-list 'elfeed-search-face-alist
             '(comic actuator-elfeed-comic-face))
(defface actuator-elfeed-video-face
  `((t . (:background "gray90" :foreground "blue")))
  "Face for elfeed video entry."
  :group 'actuator-elfeed)
(defface actuator-elfeed-image-face
  `((t . (:background "gray90" :foreground "blue")))
  "Face for elfeed image entry."
  :group 'actuator-elfeed)
(defface actuator-elfeed-comic-face
  `((t . (:background "gray90" :foreground "blue")))
  "Face for elfeed comic entry."
  :group 'actuator-elfeed)

Feeds

I’m actually surprised I don’t use the excellent Elfeed-org package. I have used it in the past, but I don’t anymore. I don’t like Org-mode documents where headlines are also links, and I don’t value having much of a hiearchy for tags. I keep considering setting it up just so I can nicely rename all my feeds to be consistent, but I just haven’t bothered.

(setq elfeed-feeds
      '(("https://www.youtube.com/feeds/videos.xml?channel_id=UCwBbuLWaIhxGuA6THzAqqIQ")
        ("http://approachingpavonis.blogspot.com/feeds/posts/default")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCVdQKW6fmfBmhz4t5k8Dq5w")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCkcODH4P9o3ovGWCRV5kJkA")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UC8tThli1ZY7LW5Dxqr3Y0jA")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCbpMy0Fg74eXXkvxJrtEn3w")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCJps2S5PiabUY3yZv3iq0tw")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCbJ1WFUdC4ImBlFReGNHjKQ")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCvETBL47UPZVMBdIW-gFpPQ")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCcGoqh8kLlACkFFpqXm6eSw")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UC224ep4hRGF54CFcwqapb4A")
        ("https://twitchrss.appspot.com/vod/dragonfriends")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UC8tThli1ZY7LW5Dxqr3Y0jA")
        ("https://sewmuchblack.de/feed/")
        ("https://www.tchwr.com/feed/")
        ("https://shellzine.net/feed/")
        ("https://notmyhostna.me/atom.xml")
        ("https://www.g-central.com/feed/")
        ("https://weather.gc.ca/rss/warning/nl-24_e.xml")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UCY3Nryf55m0yn48jLezBhlw")
        ("https://blog.jethro.dev/index.xml")
        ("https://www.kijiji.ca/rss-srp-buy-sell/st-johns/g-shock/k0c10l1700113")
        ("https://www.kijiji.ca/rss-srp-clothing-men/st-johns/small/c278l1700113a15183001?ad=offering")
        ("https://www.kijiji.ca/rss-srp-mens-shoes/st-johns/size+8__size+8+5/c15117001l1700113a15117001?ad=offering")
        ("https://www.kijiji.ca/rss-srp-buy-sell-desks/st-johns/desk/k0c239l1700113?ad=offering&for-sale-by=ownr")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UC1XDekTJ0jp24_aw4MncIsg")
        ("https://medium.com/feed/@ghostlux")
        ("https://idiotreport.substack.com/feed/")
        ("https://backstage.1blocker.com/feed")
        ("https://www.youtube.com/feeds/videos.xml?channel_id=UC8TjnmfivUw4bLB-VEn0_Sw")
        ("https://formerf1doc.wordpress.com/feed/")
        ("http://anaffordablewardrobe.blogspot.com/feeds/posts/default")
        ("http://feedpress.me/apt2024")
        ("https://sam217pa.github.io/index.xml")
        ("https://blog.blankbaby.com/atom.xml")
        ("https://cestlaz.github.io/rss.xml" emacs)
        ("http://blog.binchen.org/rss.xml" emacs)
        ("https://css-tricks.com/feed/")
        ("https://deathtrashgame.tumblr.com/rss")
        ("https://dieworkwear.com/rss")
        ("https://emacsredux.com/atom.xml" emacs)
        ("http://emacsrocks.com/atom.xml" emacs)
        ("https://fastmail.blog/rss/")
        ("https://epsalt.ca/rss" blog)
        ("https://hk-devblog.com/feed/")
        ("http://www.howardism.org/index.xml" emacs)
        ("http://feeds.feedburner.com/Ikeahacker")
        ("http://irreal.org/blog/?feed=rss2" emacs)
        ("https://www.kinowear.com/feed/")
        ("https://longreads.com/feed/")
        ("https://www.macsparky.com/blog?format=rss")
        ("http://mbork.pl/?action=rss" emacs)
        ("https://www.masteringemacs.org/feed" emacs)
        ("https://fuco1.github.io/rss.xml" emacs)
        ("https://oremacs.com/atom.xml")
        ("http://xenodium.com/rss.xml")
        ("https://mcmansionhell.com/rss")
        ("http://www.modernemacs.com/index.xml" emacs)
        ("https://nefariousreviews.com/feed/")
        ("https://genehack.blog/atom.xml")
        ("https://scifiinterfaces.com/feed/")
        ("https://updates.nonissue.org/rss")
        ("https://nullprogram.com/feed/" emacs)
        ("https://scripter.co/posts/index.xml" emacs)
        ("http://pragmaticemacs.com/feed/" emacs)
        ("http://www.lunaryorn.com/feed.atom" emacs)
        ("http://endlessparentheses.com/atom.xml" emacs)
        ("https://karl-voit.at/feeds/lazyblorg-all.atom_1.0.links-and-content.xml")
        ("https://sachachua.com/blog/feed/" emacs)
        ("https://feedpress.me/sixcolors")
        ("https://strattondelany.com/feed/" blog)
        ("https://www.stylesofman.com/feed/")
        ("http://takingnotenow.blogspot.com/feeds/posts/default")
        ("https://journal.styleforum.net/feed/")
        ("https://culturedcode.com/things/blog/feed/rss.xml")
        ("https://tungodies.com/feed/")
        ("https://manuel-uberti.github.io/feed" emacs)
        ("http://usuallywhatimdressed.in/feed/")
        ("https://zettelkasten.de/feed.atom")
        ("https://zzamboni.org/index.xml")
        ("https://eightiesandninetiesanime.tumblr.com/rss" image)
        ("https://1041uuu.tumblr.com/rss" image)
        ("https://bubblegumcrash.tumblr.com/rss" comic)
        ("https://cyberianpunks.tumblr.com/rss" image)
        ("https://www.drugsandwires.fail/feed/" comic)
        ("http://feeds.feedburner.com/Explosm" comic)
        ("https://www.foxtrot.com/feed/" comic)
        ("http://feeds.feedburner.com/PoorlyDrawnLines" comic)
        ("https://rekall.me/rss" image)
        ("http://collet66.blog52.fc2.com/?xml")
        ("https://reddit-top-rss.herokuapp.com/?subreddit=deusex&averagePostsPerDay=2&view=rss")
        ("https://reddit-top-rss.herokuapp.com/?subreddit=cyberpunk&averagePostsPerDay=2&view=rss")
        ("https://reddit-top-rss.herokuapp.com/?subreddit=emacs&averagePostsPerDay=2&view=rss" emacs)
        ("https://reddit-top-rss.herokuapp.com/?subreddit=orgmode&averagePostsPerDay=2&view=rss" emacs)
        ("https://reddit-top-rss.herokuapp.com/?subreddit=techwearclothing&averagePostsPerDay=2&view=rss")
        ("https://reddit-top-rss.herokuapp.com/?subreddit=techwear&averagePostsPerDay=2&view=rss")
        ("https://reddit-top-rss.herokuapp.com/?subreddit=formula1&averagePostsPerDay=1&view=rss")
        ("https://reddit-top-rss.herokuapp.com/?subreddit=malefashionadvice&averagePostsPerDay=1&view=rss")
        ("https://noonker.github.io/index.xml" emacs)))

Honestly, it feels weird to share my entire collection of feeds in public. Like I’m sharing something very personal. Anyway, that’s it. That’s my Elfeed.

The future

There are a bunch of things I’d like to add to my Elfeed setup that I haven’t yet.

TODO Make the interface prettier

Obviously I’m going to need to fix the custom face, but here are the basic faces that Elfeed uses:

TODO Look into sync options

I don’t know if I care enough about reading RSS on my phone, but maybe I’d like it if I tried it. There seem to be two main ways. Elfeed-web is a sub-package that is part of Elfeed proper, which creates a single webpage using an Emacs HTML server that could be read by a phone I assume. Seems like I might have to do some fiddling, and it would only work if the computer running Elfeed is on—which is currently my laptop. The other option is Elfeed-protocol combined with a web-based RSS reader that’s compatible. That would probably require paying money for either a webhost or a subscription-based feed reader, and checking to see how my elfeed filters work with it.

TODO Set up video integration

A portion of my use of Elfeed involves watching Youtube videos. There are a bunch of ways to hook Elfeed into various video-playing tools like MPV, or even the Emacs music thing called EMMS. I should see if I can make one of those work.

TODO Look into Elfeed-score

Elfeed-score seems like a neat way to enhance my on-or-off filters. I like using the filters to kill stuff that I absolutely don’t want, but a ranking system might be neat.

TODO Set up EWW’s readability features for even more Emacs

I could set up a keybinding to auto-open feeds in EWW so I don’t need to use Safari for stuff that doesn’t need Safari. I actually can’t see a reason to do this, but I kind of want to.

Additional resources