elfeed-tube icon indicating copy to clipboard operation
elfeed-tube copied to clipboard

Youtube integration for Elfeed, the feed reader for Emacs

#+title: Elfeed Tube: Youtube on /your/ terms

:BADGE: [[https://melpa.org/#/elfeed-tube][file:https://melpa.org/packages/elfeed-tube-badge.svg]] :END:

Elfeed Tube is an Emacs package for a richer, interactive, noise-free and fully text-capable interface to your Youtube subscriptions and playlists using Elfeed, the RSS feed reader for Emacs. Think of it as supercharging Elfeed, or perhaps a taste of what the RSS/Atom protocol /could have become today/ if it had continue to evolve.

Elfeed Tube adds video descriptions, metadata and "live" transcripts (see below) for all Youtube video entries in Elfeed. Here is what a video entry looks like:

https://imgur.com/a/taiMOqG

#+html:

([[https://i.imgur.com/nFK0n3m.png][Higher quality version]])

Here is the same Youtube video entry in Elfeed without Elfeed Tube:

#+html:

Elfeed Tube is useful for any Youtube RSS feed, but it works particularly well for channels/playlists with longer form videos, podcasts, technical talks and lectures. For an introduction to Elfeed and RSS feeds, see [[https://www.youtube.com/watch?v=oHfAht9uTx8][this video]] by Protesilaos Stavrou.

  • Contents :TOC:
  • [[#features][Features]]
    • [[#fully-asynchronous-updates][Fully asynchronous updates]]
    • [[#description-and-other-metadata][Description and other metadata]]
  • [[#transcripts-that-are][Transcripts that are...]]
    • [[#noise-free-no-ad-reads][Noise-free (No Ad reads)]]
    • [[#interactive][Interactive]]
    • [[#navigable-with-imenu][Navigable with =imenu=]]
    • [[#live]["Live"]]
    • [[#archivable-and-retrievable][Archivable and retrievable]]
    • [[#searchable-and-org-linkable-its-just-an-emacs-buffer][Searchable and Org-linkable (It's just an Emacs buffer)]]
    • [[#putting-it-together][Putting it together]]
  • [[#setup][Setup]]
    • [[#set-up-with-use-package][Set up with =use-package=]]
    • [[#set-up-without-use-package][Set up without =use-package=]]
  • [[#usage][Usage]]
    • [[#step-i-add-youtube-subscriptions-to-elfeed][Step I: Add Youtube subscriptions to Elfeed]]
    • [[#step-ii-use-elfeed-as-usual][Step II: Use Elfeed as usual]]
    • [[#optional-back-fill-youtube-feeds][(Optional) Back-fill YouTube feeds]]
    • [[#optional-for-live-captions-with-mpv][(Optional) For "Live" captions with MPV]]
    • [[#optional-view-entries-instead-of-auto-saving-them][(Optional) View entries instead of auto-saving them]]
    • [[#optional-fetch-manually][(Optional) Fetch manually]]
    • [[#optional-elfeed-tube-for-any-video][(Optional) Elfeed Tube for any video]]
    • [[#commands-summary][Commands summary]]
  • [[#customization][Customization]]
    • [[#disabling-fields][Disabling fields]]
    • [[#customizing-fields][Customizing fields]]
    • [[#persistence][Persistence]]
  • [[#faq][FAQ]]
  • [[#limitations][Limitations]]
  • [[#planned-features][Planned features]]
  • [[#alternatives][Alternatives]]
  • [[#acknowledgments][Acknowledgments]]
  • Features ** Fully asynchronous updates Like Elfeed, Elfeed Tube fetches all data asynchronously. /Emacs will not block/. (This gets top billing because I /know the pain/.)

** Description and other metadata Elfeed Tube adds the video duration, thumbnail, video description and chapters to each Youtube entry in Elfeed. These elements can be turned off individually or tweaked to your liking.

For example, you may prefer to avoid including video descriptions because they often have linkspam, or prefer larger thumbnails.

  • Transcripts that are...

Youtube transcripts, including auto-generated ones, are automatically downloaded in your language of choice, formatted and displayed in your Elfeed buffers:

#+html:

#+html:

** Noise-free (No Ad reads) There are no sponsor reads, ads or promotions in the transcript. These are automatically recognized and either greyed out or hidden from view. Here's an Ad read section that you now don't have to read:

#+html:

[[https://i.imgur.com/TUn4xmb.jpeg][Here]] is a more extensive version.

https://imgur.com/a/Oyly62g

You can turn this feature off, or add more recognized sections to the strike-list, like intros and outros.

** Interactive Clicking on a transcript segment opens the video at that time in your browser or video player of choice:

#+html:

You can also move your cursor there and press return (~RET~). The tool-tip provides more help. ** Navigable with =imenu= Video chapters, when available, are included as headings in the transcript. The entry can be navigated through =imenu=. Here I jump between descriptions of different movies in this video:

#+html:

** "Live" With MPV integration (optional), you can seek to any time in the video by clicking at the corresponding text in your buffer:

#+html:

https://user-images.githubusercontent.com/8607532/172324761-66a1aed8-2d09-48d4-8673-86b5a13ffc4d.mp4

You can jump in Emacs to the current video position with =elfeed-tube-mpv-where= (~C-c C-w~):

#+html:

Finally, you can also follow along with the video in Emacs:

#+html:

#+html:

#+html:

#+html:

To do this, turn on =elfeed-tube-mpv-follow-mode= (~C-c C-f~). When this mode is active you can pause video playback with ~SPC~. Clicking on any segment in the transcript will still seek to that point.

You can combine =elfeed-tube-mpv-follow-mode= with other Emacs buffer modes, like Emacs' =follow-mode= that shows a contiguous buffer in two windows:

#+html:

This feature does not lock up Emacs. You can work in a different window while tracking updates in this one.

** Archivable and retrievable The information fetched by Elfeed Tube, including the transcripts (with timing information) is associated with the feed and stored in your Elfeed database like any other text entry. You can bookmark it in Emacs, refer to it in your other notes and so on.

The Elfeed database is plain text, optionally gzipped. You can be sure that it will be readable decades from now.

** Searchable and Org-linkable (It's just an Emacs buffer) You can text search your transcripts, Org-link them, quote from them, etc. Tagging support, date-oriented and metadata-based search is included as part of Elfeed.

When transcripts are available in some form, which is most of the time, they can be your primary entry and reference point for the video. This works especially well with technical content, tutorials, talks and podcasts.

/NOTE/: It would be fantastic to be able to search for caption text across your entire Elfeed database instead of inside an entry, but Elfeed does not (yet) support full-text search. You'll have to search for entries by supported fields instead, through a combination of the entry date range, tags, entry title and channel name etc.

** Putting it together Combined with the text manipulation capabilities of Emacs, you can combine these features into a very accessible video interface. You can handle video partially like you handle text:

  • Narrow the buffer to just occurrences of a phrase in the transcript, then jump between where they appear. In this example I use =consult-focus-lines= to show only lines mentioning "Ramanujan", then jump through these points in the video by pressing ~RET~. Full-text video search!

    #+html:

    This search missed one mention (which was spelled "ramonogen"), but Youtube's auto-generated captions do a surprisingly good job of capturing most common English words. Videos with uploaded captions (most talks/technical videos) don't have this problem.

  • The simpler version: Want to jump to where in the episode you heard a podcast host talking about Tunisian history? Search the buffer for Tunisia using Isearch in Emacs, then press ~RET~.

  • Youtube video descriptions for technical videos often have useful links or references. Here I use [[https://github.com/oantolin/embark][Embark]] to collect the (non-Youtube, non-sponsor) links and open them in a browser:

    #+html:

    This works in regular Elfeed too, of course, but for Youtube videos this is much more pleasant than hunting for the tiny "more..." dropdown in the web browser.

  • Watching a lecture or a long video and need a break? Jump to the currently playing position in the transcript with =elfeed-tube-mpv-where= (~C-c C-w~), then bookmark the buffer (=bookmark-set=, ~C-x r m~) and quit Emacs. You can pick up right where you left off in both the transcript and video with =bookmark-jump= (~C-x r b~).

  • Want to focus playback to a certain part of the transcript? Narrow the buffer (~C-x n n~) to the region you want and turn on =elfeed-tube-mpv-follow-mode=.

  • Setup Elfeed Tube is available on MELPA. After [[https://github.com/melpa/melpa#usage][adding MELPA to your package archives]], you can install it by running =M-x package-install⮐= =elfeed-tube= in Emacs, or by running the below =use-package= block(s).

MPV integration with the live transcripts is provided separately as =elfeed-tube-mpv=, you can install it with =M-x package-install⮐= =elfeed-tube-mpv=.

Requirements:

  • Emacs 27.1 or newer
  • Curl

Dependencies (automatically installed):

  • [[https://github.com/skeeto/elfeed][Elfeed]], the feed reader for Emacs.
  • [[https://github.com/skeeto/emacs-aio][aio]], the async-IO library for Emacs.

For "live" transcripts with =elfeed-tube-mpv=:

  • [[https://github.com/kljohann/mpv.el][mpv]]: The mpv library for Emacs
  • [[https://mpv.io/][MPV]] video player and [[https://youtube-dl.org/][youtube-dl]] (or [[https://github.com/yt-dlp/yt-dlp/][yt-dlp]])

** Set up with =use-package= #+BEGIN_SRC emacs-lisp (use-package elfeed-tube :ensure t ;; or :straight t :after elfeed :demand t :config ;; (setq elfeed-tube-auto-save-p nil) ; default value ;; (setq elfeed-tube-auto-fetch-p t) ; default value (elfeed-tube-setup)

:bind (:map elfeed-show-mode-map
       ("F" . elfeed-tube-fetch)
       ([remap save-buffer] . elfeed-tube-save)
       :map elfeed-search-mode-map
       ("F" . elfeed-tube-fetch)
       ([remap save-buffer] . elfeed-tube-save)))

#+END_SRC

If you want "live" captions and better MPV support: #+BEGIN_SRC emacs-lisp (use-package elfeed-tube-mpv :ensure t ;; or :straight t :bind (:map elfeed-show-mode-map ("C-c C-f" . elfeed-tube-mpv-follow-mode) ("C-c C-w" . elfeed-tube-mpv-where))) #+END_SRC

** TODO Set up without =use-package= #+BEGIN_SRC emacs-lisp (require 'elfeed-tube) (elfeed-tube-setup) (define-key elfeed-show-mode-map (kbd "F") 'elfeed-tube-fetch) (define-key elfeed-show-mode-map [remap save-buffer] 'elfeed-tube-save) (define-key elfeed-search-mode-map (kbd "F") 'elfeed-tube-fetch) (define-key elfeed-search-mode-map [remap save-buffer] 'elfeed-tube-save)) #+END_SRC

If you want "live" captions and better MPV support: #+BEGIN_SRC emacs-lisp (require 'elfeed-tube-mpv) (define-key elfeed-show-mode-map (kbd "C-c C-f") 'elfeed-tube-mpv-follow-mode) (define-key elfeed-show-mode-map (kbd "C-c C-w") 'elfeed-tube-mpv-where)

#+END_SRC

  • Usage If you are already subscribed to Youtube RSS feeds using Elfeed, you can skip to Step II.

Elfeed Tube tries its best to work out of the box with no set up, but Step I below is unavoidable if you're starting fresh.

** Step I: Add Youtube subscriptions to Elfeed Fortunately, Youtube still provides RSS feeds for channels and playlists. Unfortunately, Youtube doesn't make it easy to find them.

Elfeed Tube provides a helper function: =M-x elfeed-tube-add-feeds= to find the RSS feeds (asynchronously) for channels or playlists. When given one ore more Youtube video/playlist/channel URLs or plain text search terms, it will:

  • Find the corresponding feeds and display a summary you can confirm
  • Add the feeds to your list of elfeed-feeds.

https://user-images.githubusercontent.com/8607532/172195966-33089c16-57b1-4bbc-9bcc-71ac3d4338bc.mp4

(Finding the feeds is also asynchronous)

Examples (=RET= means pressing return):

  • =M-x elfeed-tube-add-feeds= =RET= =cgp grey, julia computing, https://www.youtube.com/playlist?list=PLZdCLR02grLqSy15ALLAZDU6LGpAJDrAQ= =RET=
  • =M-x elfeed-tube-add-feeds= =RET= =https://www.youtube.com/watch?v=6etTERFUlUI= =RET=

Queries are separated by Emacs' =crm-separator=, which is comma (,) by default. Be warned: URLs are safer, plain text queries might find the wrong channels!

When called noninteractively, it can accept a list of URLs or queries: #+BEGIN_SRC emacs-lisp (elfeed-tube-add-feeds '("veritasium" "https://www.youtube.com/playlist?list=PLEoMzSkcN8oMc34dTjyFmTUWbXTKrNfZA" "quanta magazine" "julia computing" "https://www.youtube.com/watch?v=bSVfItpvG5Q" "https://youtu.be/7CM7Ef-dPWQ" "tom scott")) #+END_SRC

See the docstring for more options.

*** Other ways to find feeds You can use a web service like https://rssbox.herokuapp.com/, or look in the HTML of a Youtube channel page if you like your web browser's element inspector!

** Step II: Use Elfeed as usual That's it. Assuming you've run =(elfeed-tube-setup)=, included in the above use-package block, there's nothing else to do.

If you're new to Elfeed, you can start with =M-x elfeed=.

In case the fetch for a Youtube entry fails you can call =M-x elfeed-tube-fetch= with a prefix argument (~C-u F~ or ~C-u M-x elfeed-tube-fetch~) to force a refetch.

** (Optional) Back-fill YouTube feeds While Elfeed is a vault, Youtube RSS feeds only contain the last 15 videos from a channel. So you will only collect entries for videos from a little before when you subscribe to a channel's feed.

Elfeed Tube provides a command to add all historical entries for Youtube feeds to Elfeed. Call =elfeed-tube-fill-feeds= in an Elfeed search or entry buffer to fetch all entries for the feed at point. You can do this for many feeds at once by selecting a region of entries.

#+html:

[[https://user-images.githubusercontent.com/8607532/184994136-2a36f81c-bb47-470f-86ca-0efd778f3b03.png]]

** (Optional) For "Live" captions with MPV For a "live" connection between the transcript and MPV:

  • Ensure the =mpv= library is installed (=M-x package-install mpv=). Without it, you can still open videos at transcript locations in MPV but it will not be "live".
  • Start playback by clicking anywhere in the transcript.
  • To seek to a text segment click there or press ~RET~.
  • To jump to the current location in the transcript, use =elfeed-tube-mpv-where= (~C-c C-w~).
  • To track a video continuously, turn on =elfeed-tube-mpv-follow-mode= (~C-c C-f~). You can continue to work in a different window.

Additionally, playback with "live" transcripts will behave as expected with buffer narrowing.

** (Optional) View entries instead of auto-saving them Not all Youtube videos contain gems of wisdom, to put it mildly. You may thus want to only /view/ video information instead of /adding/ it directly to the Elfeed database. This is especially salient since there's no (user-facing) way to delete items in Elfeed.

To do this you can set #+BEGIN_SRC emacs-lisp (setq elfeed-tube-auto-save-p nil) ;This is the default value #+END_SRC

Now Youtube entries you view will feature a =[NOT SAVED]= marker:

#+html:

The fetched info will be cached for this Emacs session. Entries that you deem useful (such as the handy Guix tutorial in the above entry) can be added to your Elfeed database by clicking on this marker, or with your =save-buffer= keybinding (typically ~C-x C-s~):

#+html:

You can change the marker style through =elfeed-tube-save-indicator= if you'd like a more subdued indicator.

** (Optional) Fetch manually Finally, you can also disable auto-fetching data and call =M-x elfeed-tube-fetch= (bound to ~F~) from an Elfeed Search or Show buffer to fetch selectively. To do this, set

#+BEGIN_SRC emacs-lisp (setq elfeed-tube-auto-fetch-p nil) #+END_SRC

=M-x elfeed-tube-fetch= will fetch info for all Youtube entries in a selected region in an Elfeed Search buffer.

You can independently control auto/manual fetching of info and auto/manual saving of info to the Elfeed database.

** (Optional) Elfeed Tube for any video You can call =elfeed-tube-fetch= outside of Elfeed to read an abritrary Youtube video URL (not just feed entries) and produce an Elfeed-like entry buffer for it with description, other metadata and live transcripts etc. Essentially you can use Elfeed Tube as a Youtube viewer. This feature is currently experimental. ** Commands summary =elfeed-tube= commands: | Command | Description | Where | suggested key-binding | |-----------------------------+----------------------------------------------+--------------------+-----------------------| | =elfeed-tube-add-feeds= | Find youtube feeds from search queries | Anywhere in Emacs | N/A | | =elfeed-tube-fetch= | Fetch info for any video | Anywhere in Emacs | N/A | | =elfeed-tube-fetch= (again) | Fetch video info for Elfeed entry (optional) | Anywhere in Elfeed | ~F~ | | =elfeed-tube-save= | Manually save entry to Elfeed DB (optional) | In an Elfeed entry | ~C-x C-s~ | | =elfeed-tube-fill-feeds= | Back-fill Youtube feed entries | Anywhere in Elfeed | N/A |

=elfeed-tube-mpv= commands, for live transcripts | Command | Description | Where | suggested key-binding | |-----------------------------+-----------------------------------------------+--------------------+-------------------------| | =elfeed-tube-mpv= | Open video at time or seek video to this time | In an Elfeed entry | ~mouse-1~, ~mouse-2~ or ~RET~ | | =elfeed-tube-mpv-where= | Jump to current video location in transcript | In an Elfeed entry | ~C-c C-w~ | | =elfeed-tube-mpv-follow-mode= | Continuously track a video playing in MPV | In an Elfeed entry | ~C-c C-f~ |

  • Customization Elfeed Tube has opinionated defaults but is fully configurable through the Customize interface (=M-x customize=). Here are some examples:

** Disabling fields Customize =elfeed-tube-fields=. To show only the duration and captions but no description, thumbnail or chapter locations: #+BEGIN_SRC emacs-lisp ;; Other options: thumbnail, description, chapters (setq elfeed-tube-fields '(duration captions)) #+END_SRC

** Customizing fields

*** Thumbnails Control the size with =elfeed-tube-thumbnail-size=. *** Captions **** Languages =elfeed-tube-captions-languages=: Language preference. The first available matching transcript will be fetched: #+BEGIN_SRC emacs-lisp ;; Arabic or English or auto generated English captions (setq elfeed-tube-captions-languages '("ar" "en" "english (auto generated)")) #+END_SRC **** Sponsored segments

  • =elfeed-tube-captions-sblock-p= controls whether sponsored segments of videos are de-emphasized in the transcript.
  • =elfeed-tube-captions-faces=: Faces to use for different types of transcript segments.

** Persistence Set the boolean =elfeed-tube-auto-save-p= to =t= to automatically save fetched information to the Elfeed database.

The boolean =elfeed-tube-save-indicator= controls the style of indicator used to indicate unsaved content.

  • FAQ *** Do I need a Youtube API key to use this? Not as of right now, it should Just Work. *** Where does Elfeed Tube fetch data from? It combines information from a number of sources:
  • Scraping the Youtube video page
  • Invidious instances that provide an API (dynamically found)
  • The Sponsorblock API for crowd-sourced Ad segment identification

Yes, this does mean that the fetcher code is going to need updating often. Them's the breaks. *** Can I use the transcripts feature without installing Elfeed?

Not at present. Elfeed Tube depends on Elfeed to do a lot of the lifting.

However you can use it with any Youtube video, see: [[#optional-elfeed-tube-for-any-video][(Optional) Elfeed Tube for any video]]. If you're interested in creating a stand alone package for this please go ahead. *** What about these Youtube features? No support is currently planned for

  • Search
  • Video recommendations
  • Comments
  • Likes, +Dislikes+ and views

Elfeed Tube is not a Youtube client for Emacs.

Instead, it plays to the strengths of RSS: to provide you with a regular digest of self-curated content in a more accessible way than otherwise possible. So search is not planned (see alternatives).

Video recommendations are sometimes useful for discovery, but in their current form they're designed primarily to keep you watching Youtube and often end in rabbit-holeing and doom spirals. Helping myself avoid these behaviors is an explicit goal of Elfeed Tube!

The top comments are occasionally useful, especially on technical videos. I might add support for these in the future, although none is planned as of now.

  • Limitations
  1. Elfeed provides only metadata, not full-text search across your entries since it is (almost) a plain text database.
  2. There is no (user-facing) way to delete entries from an Elfeed database, so curate wisely! Note that my Elfeed database has about 32,000 entries across 272 feeds and it's very snappy so far.
  3. Only one MPV instance can be connected to a live transcript in an Emacs session at a time. This is a limitation of the =mpv= library. (You can spawn as many "non-live" instances as you want.)
  4. Live transcript seeking and tracking can have an error of ±1 second.
  5. Fetches can occasionally fail. If this happens you may need to call =M-x elfeed-tube-fetch= manually on an entry or selection of entries.
  • Planned features
  • [ ] Specify what data to fetch per feed instead of globally.
  • [ ] Elfeed search keyword for video duration: ("=crafters <25min" should return videos in the Elfeed DB from the "System Crafters" Youtube channel feed that are under 25 minutes long, etc.)
  • [ ] Support for Youtube's official API
  • [ ] Re-punctuating auto-generated captions using a [[https://github.com/ottokart/punctuator2][punctuator2]] web service
  • [ ] =url-retrieve= support so Curl isn't required
  • [ ] Backporting to Emacs 26.x
  • Alternatives
  • If you are looking for a Youtube client for Emacs, with full search, comments, view counts and video recommendations, check out [[https://github.com/gRastello/ytel][ytel]] with [[https://github.com/xFA25E/ytel-show][ytel-show]].
  • Not an alternative but a nice complement to Elfeed Tube: Lua scripts for MPV can offer a superior Youtube watching experience than anything in the browser. Check out
    • [[https://github.com/po5/mpv_sponsorblock][mpv-sponsorblock]] for auto-skipping sponsored segments of videos
    • [[https://github.com/AN3223/dotfiles/blob/master/.config/mpv/scripts/auto-save-state.lua][auto-save-state]] for built-in watch-later functionality
    • [[https://github.com/jonniek/mpv-playlistmanager][playlist-manager]] for playlist support
    • [[https://github.com/Eisa01/mpv-scripts#undoredo][undo-redo]] for undo/redoing seeks in MPV
    • [[https://github.com/Sagnac/streamsave][streamsave]] for live-saving clips from a video without reencoding.
    • [[https://github.com/cvzi/mpv-youtube-upnext][youtube-upnext]] for in-player video recommendations based on the current video. (Youtube's sidebar, essentially.) [[https://github.com/mpv-player/mpv/wiki/User-Scripts][The full list]] of scripts is overwhelming!
  • Acknowledgments
  • Chris Wellons for writing [[https://github.com/skeeto/elfeed][Elfeed]], the best feed reader I've used.
  • Chris Wellons for also writing [[https://github.com/skeeto/emacs-aio][emacs-aio]], the async wrapper for Emacs. It writes callbacks for me so I never have to.
  • Ajay Ramachandran for writing and running [[https://github.com/ajayyy/SponsorBlock][Sponsorblock]]
  • The kind folks maintaining [[https://invidious.io/][Invidious]] instances, [[https://youtube-dl.org/][youtube-dl]] and [[https://github.com/yt-dlp/yt-dlp/][yt-dlp]]
  • COMMENT Notes

** TODO Fix persistence bug It disappears from the index first, then from the content store when =elfeed-db-gc= runs.

** TODO Specify what data to fetch per feed instead of globally. ** TODO Elfeed search keyword for video duration: ("=crafters <25min" should return videos in the Elfeed DB from the "System Crafters" Youtube channel feed that are under 25 minutes long, etc.)

** TODO Support for Youtube's official API ** TODO Re-punctuating auto-generated captions Using a [[https://github.com/ottokart/punctuator2][punctuator2]] web service

** TODO Backfill Youtube feeds ** TODO Download features [0/3] :PROPERTIES: :COOKIE_DATA: todo recursive :END: *** TODO Download button?

  • Display as enclosures? *** TODO Download region? *** TODO User option for download formats?

** DONE Chapter extraction CLOSED: [2022-06-18 Sat 23:07] From the descriptions.

  • Store these or generate in =elfeed-tube-show=? Ended up storing them in the captions data structure. ** DONE Chapter display [2/2] CLOSED: [2022-06-18 Sat 23:07]
  • [X] User option for timestamp chunking
  • [X] Formatting this and timestamps simultaneously?

** DONE =imenu= support [2/2] CLOSED: [2022-06-18 Sat 22:58] Headings:

  • [X] All message-header-name fields
  • [X] Chapters?

** Local

Local Variables:

eval: (when (featurep 'toc-org-mode) (toc-org-mode 1))

End: