Extending org-mode to handle youtube links

Requirement

  1. I want to open youtube links in my notes in mpv instead of opening the browser
  2. When exporting to HTML, youtube links should become embedded videos instead of hyperlinks

Implementation

Org has a concept of 'link types'. We can add a new type of link and have it behave the way we want.

  • org-link-parameters

    This variable contains link types and how they behave on follow (i.e when someone opens the link with C-c C-o), export etc. A type of a link is determined by the string before first ":" in it. e.g "https://bitspook.in" has https type, "file:///etc" has file type.

    Items in this list has 2 main components:

    1. First element is string representing the link type
    2. Key-value pairs of behavior name (e.g :follow, :export) and function which handles that behavior

    For example:

    (("yt" :follow spook-org--follow-yt-link :export spook-org--export-yt-link)
     ("eww" :follow org-eww-open :store org-eww-store-link))
    
  • org-link-set-parameters

    This function is used to add new link-types (and also to add new behavior to existing ones). You can check the docs for org-link-parameters (with C-h v org-link-parameters) to see arguments provided to each type of callback.

Here's the code I've added in my emacs config:

(defun spook-org--follow-yt-link (path prefix)
  (let* ((url (format "https:%s" path))
         (proc-name (format "*yt://%s*" url)))
    (if (and prefix (executable-find "mpv"))
        (browse-url url)
      (make-process :name proc-name :buffer proc-name :command `("mpv" ,url))
      (message "Launched mpv in buffer: %s" proc-name))))

(defun spook-org--export-yt-link (path desc backend)
  (when (eq backend 'html)
    (let* ((video-id (cadar (url-parse-query-string path)))
           (url (if (string-empty-p video-id) path
                  (format "//youtube.com/embed/%s" video-id))))
      (format
       "<iframe width=\"560\" height=\"315\" src=\"%s\" title=\"%s\" frameborder=\"0\" allowfullscreen></iframe>"
       url desc))))

(org-link-set-parameters "yt" :follow #'spook-org--follow-yt-link :export #'spook-org--export-yt-link)

make-process will create a background buffer named *yt://<url>* which allow monitoring and killing the mpv process.

Update: Use make-process instead of async-shell-command for launching mpv. Thanks to nv-elisp on /r/emacs

Result

  1. C-c C-o (i.e org-open-at-point) on a yt:// link opens the youtube video in mpv
  2. C-u C-c C-o (i.e org-open-at-point with an prefix argument) on a yt:// link opens the video in browser
  3. When exported to HTML, all yt:// links file are exported as embedded youtube videos

Bonus gains 💪

Recently I've been building a tool (cl-ownpress) which to make blogging a zero-effort activity for me. Since I have already built a habit of judicious note taking, publishing a subset of my notes will enable maintaining an active blog without doing any "work".

I've also been making thickly-accented-awkardly-narrated youtube videos. These are almost always tldr; videos for my blog posts. So they get embedded in my blog posts.

'Blog with notes + embed youtube videos' become easier to do with this little tinkering. Since cl-ownpress uses my running Emacs to publish my posts, I don't need to make any change in my blogging setup. I can embed my tldr; videos in my blog posts by just prefixing youtube links with yt:// instead of https://.

I am pretty happy that I got zero work blogging-setup, and zero work extending-the-blogging-setup as well.