Einar's blog

Elfeed with useful scripts

Elfeed is an RSS feed reader package for GNU Emacs. RSS is a standard for getting content like podcasts, video channels from LBRY and Youtube, Mastodon and Twitter, blog entries, news articles etc from the web without using a browser. Before I started using elfeed, I used Newsboat, a terminal program, as my RSS reader. I was inspired by Napoleon Wils0n to add scripts to Newsboat so I could launch videos from RSS feeds directly in mpv or download them with youtube-dl (and later yt-dlp). After I started using Emacs, I wanted to try to integrate most of my computing into it and I looked at the two built in feed readers before trying the highly recommended elfeed package. I liked it, so the next step was to find a way to get the same functionality I had in Newsboat in elfeed.

I don't know Elisp well enough to script this myself yet, so I looked online. I read up on the documentation for elfeed on GitHub and also found some inspiration on Reddit that I tweaked to make some elisp scripts to be able to hit m if I want to watch a video in mpv and M if I want to download it with yt-dlp to my "Nedlastinger" (Downloads in Norwegian) folder. I tried a couple of different solutions before I was able to get what I wanted. The suggestion from Reddit was to use start-process to launch an external process with the url from the entry as its argument. That worked well with mpv. For yt-dlp that was less successful and I also wanted to change directory to ~/Nedlastinger before launching the yt-dlp process so I read up on how to run shell-commands asyncronously and used that functionality instead. I also made certain that any shell command run asyncronously doesn't display a new window. (A "window" is a split within an Emacs "frame" (an X11 window) in Emacs parlance. Emacs is from before the Macintosh introduced the desktop metaphor's now common vocabulary (that Microsoft later copied in Windows), so it uses its own vocab for these concepts.) The reason why there are two scripts for each functionality is that I can use those keyboard shortucts both inside the view where you show the specific entry and in the view where you see all entries while one is selected.

;; Dette gjør at Async Shell Command ikke vises hver gang man kjører en shell kommando med & fra inne i Emacs 
(add-to-list 'display-buffer-alist
  (cons "\\*Async Shell Command\\*.*" (cons #'display-buffer-no-window nil)))

;; Gir meg muligheten til å bruke m i elfeed for å åpne en rss entry i mpv
(defun browse-url-mpv (url &optional single)
  (start-process "mpv" "*mpv*" "mpv" url))

(defun elfeed-show-mpv-open (&optional use-generic-p)
  "open with mpv"
  (interactive "P")
  (let ((browse-url-browser-function #'browse-url-mpv))
    (elfeed-show-visit use-generic-p)))

(defun elfeed-search-mpv-open (&optional use-generic-p)
  "open with mpv"
  (interactive "P")
  (let ((browse-url-browser-function #'browse-url-mpv))
    (elfeed-search-browse-url use-generic-p)))

(define-key elfeed-show-mode-map (kbd "m") 'elfeed-show-mpv-open)
(define-key elfeed-search-mode-map (kbd "m") 'elfeed-search-mpv-open)

;; Gir meg muligheten til å bruke M i elfeed for å åpne en rss entry i yt-dlp
(defun browse-url-ytdlp (url &optional single)
  (shell-command (concat "cd ~/Nedlastinger &&" "yt-dlp -f 'bestvideo[height<=720]+bestaudio/best' " url " &")))

(defun elfeed-show-ytdlp-open (&optional use-generic-p)
  "open with ytdlp"
  (interactive "P")
  (let ((browse-url-browser-function #'browse-url-ytdlp))
    (elfeed-show-visit use-generic-p)))

(defun elfeed-search-ytdlp-open (&optional use-generic-p)
  "open with ytdlp"
  (interactive "P")
  (let ((browse-url-browser-function #'browse-url-ytdlp))
    (elfeed-search-browse-url use-generic-p)))

(define-key elfeed-show-mode-map (kbd "M") 'elfeed-show-ytdlp-open)
(define-key elfeed-search-mode-map (kbd "M") 'elfeed-search-ytdlp-open)

Those scripts worked fine for video channels from LBRY and Youtube, but not for podcasts which use an enclosure url to give the url to the actual episode's video or audio while the general url field in the RSS entry is used for show notes. I tried searching for variables within elfeed with the built in self-documenting features of Emacs, but did not fully understand how to find the enclosure url. After some searching aorund the internet, I found a way to get the enclosure-url and recycled most of my previous code again with the new url to make two functions making it possible for me to listen/watch a podcast with mpv by hitting P and downloading it with yt-dlp with K. I only implemented this for one of the two views.

;; Gir meg muligheten til å bruke P i elfeed for å åpne en podcast (enclosure) i mpv
(defun elfeed-show-play-enclosure (enclosure-index)
  (interactive (list (elfeed--enclosure-maybe-prompt-index elfeed-show-entry)))
  (let ((url (car
	      (elt
	       (elfeed-entry-enclosures elfeed-show-entry)
	       (- enclosure-index 1)))))
    (start-process "mpv" "*mpv*" "mpv" url)))

(define-key elfeed-show-mode-map (kbd "P") 'elfeed-show-play-enclosure)

;; Gir meg muligheten til å bruke K i elfeed for å laste ned en podcast (enclosure) med yt-dlp
(defun elfeed-show-dl-enclosure (enclosure-index)
  (interactive (list (elfeed--enclosure-maybe-prompt-index elfeed-show-entry)))
  (let ((url (car
	      (elt
	       (elfeed-entry-enclosures elfeed-show-entry)
	       (- enclosure-index 1)))))
    (shell-command (concat "cd ~/Nedlastinger &&" "yt-dlp -f 'bestvideo[height<=720]+bestaudio/best' " url " &"))))

(define-key elfeed-show-mode-map (kbd "K") 'elfeed-show-dl-enclosure)

I have set my default browser with my environment variables to Firefox and even though I like Emacs' built in browser eww a lot for text-based content with the occasional picture, I want to keep Firefox as my default browser for now. However, when I read an RSS entry in elfeed and it does not supply the full blog post, it is nice to stay within Emacs and read the linked blog post with eww instead of launching a heavy external browser. So I had to make two functions for the two view modes for that as well. So for those RSS feeds that do not supply the full content, I just hit B and the content opens up in eww. There is built in functionality to open the linked content in the default browser by hitting b in elfeed, so whenever I want or need to open the content in Firefox, I just hit b. For some content that is dependent on JavaScript or is full of media, I sometimes use that built in function, but usually, I use B instead.

;; Gir meg muligheten til å bruk B i elfeed for å åpne en rss entry i eww
(defun elfeed-show-eww-open (&optional use-generic-p)
  "open with eww"
  (interactive "P")
  (let ((browse-url-browser-function #'eww-browse-url))
    (elfeed-show-visit use-generic-p)))

(defun elfeed-search-eww-open (&optional use-generic-p)
  "open with eww"
  (interactive "P")
  (let ((browse-url-browser-function #'eww-browse-url))
    (elfeed-search-browse-url use-generic-p)))

(define-key elfeed-show-mode-map (kbd "B") 'elfeed-show-eww-open)
(define-key elfeed-search-mode-map (kbd "B") 'elfeed-search-eww-open)

Comparing my present elfeed setup with my former Newsboat setup, one thing that is much nicer with these scripts and elfeed is that I don't have to use an external program for podcasts. Newsboat comes with podboat which is intended for use as a podcatcher after you have qued up the content within Newsboat. That process always felt cumbersome and I actually didn't use it because of that. With elfeed and a few scripts, I have all kinds of feeds within the one reader and use a few different keyboard shortcuts to get everything done that I want. Another nice thing with elfeed is its search functionality which is very powerful.

My Emacs journey is still in its infancy, but the more I delve into Emacs, the more I like it. It's powerful, personal and pleasant. Emacs extends the hackability of GNU/Linux further by allowing you to tweak not only your DE or WM through configuration and shell scripts, but also the program you do most of your tasks inside. Integrating more functionality into Emacs means less friction and context switching between different tasks. Thus far, I have only integrated RSS reader, email, pdf document creation (org mode with latex export to pdf), blogging (org mode and org-static-blog), some web browsing (Firefox is still my default browser, but I use eww more and more), note-taking in org mode (I used to use vim and markdown) and of course general text-editing.

It's interesting how much faster for instance navigating around my emails is in mu4e within Emacs than in Thunderbird which was my previous email program. I think the reason is that Emacs is both text and keybord-centric (while also allowing for mouse use if you are so inclined). Elfeed has also become more useful than Newsboat used to be for me since I now also use it for podcasts. The hackability of Emacs means you can add functionality you want or customize things to your own preferences easily. Emacs also uses less system resources than other programs while doing more. Fewer resources means less power use and longer battery life on laptops. You can save money and possibly also CO2 emissions if your electicity is produced in environmentally unfriendly ways.

I see Emacs as the ultimate step in my gradual move towards less resource usage for my computing that started when I switched from Mac OS X to GNU/Linux in 2011, moved to a light-weight desktop environment (LXDE and later LXQt), then to even lighter weight window managers, gradually switched some GUI programs with terminal programs and scripts and then in the end the gradual integration of many tasks into Emacs. (I did this mainly out of curiosity, but I have reaped other rewards like less resource usage and more freedom as well.) If you see Emacs as only a text editor, then it is more resource hungry than vim (but less than VSCode), but if you see it as what it is: an integrated, hackable computing environment, then it is less resource hungry than the combination of programs you would use to do the same tasks. It is also the ultimate expression of the FSF's ideal of empowering users through the use of free software. I am definitively going to delve deeper into Emacs in the months to come.

All content is shared under the terms of the Creative Commons Attribution-ShareAlike license.