Problem

I've been using the w3m, the text web browser, with Emacs and it is fantastic. Aside from losing color and layout, it is faster and lighter than using a graphical based browser and extensible as conkeror since it has Elisp by association. One common activity with web browsing is online video streaming with sites such as youtube or netflix.

In Emacs 25, the promise to browse the web and develop with the WebKit extension has not been quite fulfilled or stable yet, so browsing video streaming sites within buffers or simply the text browser might not be possible yet. A good workaround is to summon an external video player like vlc that can take a video url from the text browser and pop the video up locally. Of course, media players could have plugins to find online videos but I am here to emphasize the extensible nature of Emacs.

This is easy to shiv together with this glue and here is what it looks like.

Screencast

The video player will popup but refrained from showing due to possible copyright and both the link and video gets is used for example. This video is Faure's Pavane that still gives me a soulful chill so check it out. The key command here is fn/w3m-view-video which takes this (url at point) or current (page) url and passes is on via start-process. Nothing really fancy here so let's explore this idea further.

w3m

What made me think about using a text based browser recently? Aside from it being lighter and faster, it is really about me living in Emacs as much as possible. When I want to browse just a single simple web page, I decide whether it is worth opening a GUI aside from breaking the buffer lifestyle. It has been in my to-do list to explore the possibility of using w3m but the loss of colors and layout is such a big mental block. Once again forgotten, I scrambled my fingers and memory on how to use the browser within Emacs.

Weirdly, the transition wasn't that hard or long. After some reading and tweaking, I am now browsing within Emacs. I was impressed that the conkeror functionality of following links by number or w3m-lnum made the transition much easier. Of course there are still browsing issues with certain sites, cookies and javascript but I wish I made the switch quite earlier.

One thing I needed was how to view online videos? Since there is no widget for video streaming in Emacs, I have to relinquish the buffer life here. I took it up as a study to write the glue code for it.

vlc

My video player in Linux. Thankfully it can run online videos by passing the url of it as says in the manual.

# http://<server address>[:<server port>]/[<file>]           HTTP stream
vlc https://www.youtube.com/watch?v=wQDoN40-_C4

Power to the command line for more complex invocations. Some configurations such as subtitle and the window position can be passed on and thanks to Emacs' start-process this is not a big deal.

(start-process
 "w3m-vlc"
 nil
 "vlc"
 "--loop"
 url)

However, if you want make it configurable on what arguments to pass, you have to use this weird invocation or create a wrapper for start-process.

(defvar video-args (list "--loop" "--no-video-title"))

(apply #'start-process
   (append
    (list "w3m-vlc" nil "vlc")
    video-args
    (list url)))

(defun fn/start-process (process-name command command-args)
  (apply #'start-process
     (list process-name nil command)
     command-args))

Either way, you have to concatenate the default args with the supplied one although I still find it a little messy. So if you have an external program you want to integrate with Emacs, this is commonly found.

If you use this snippet, you might be opening too many video players and might want to avoid too many popups. So let's talk about managing processes next.

Process Management

If you do open a process in Emacs, you can check it out using the command list-processes as shown below.

Screenshot for list-processes

This is a nice tabulated list but this interface just tabulates it, you can't do anything meaningful. I have to import list-processes+.el in order to kill something from it like how prodigy.el does it or you might be stuck closing the window yourself. So let's come up with a simple scheme to manage our video player.

A simple scheme is to have one video associated with one page or buffer, so if the page is killed or changed the associated player is closed. The function start-process returns a process object which we can set to a buffer via buffer local variables, then manage that variable with w3m-display-hook and kill-buffer-hook. That is what fn/w3m-kill-page-process does, if the page buffer has a working process it will kill by kill-process. Again nothing fancy here and you can be on your merry way.

Processes

There is one caveat to all of this: since this is an external process, it is not possible to truly manage it. For my own configuration, I add the options --one-instance and --play-and-exit which simply maintains a single vlc instance and automatically closing itself when done. The problem lies when I use the former option which produces two possibilities:

  • Every processes points to the same process
  • The last processes is the only living process while the rest is killed

With that in mind, I shiv this code to allocate the last process as the only living one while the rest is set to nil.

(defun fn/w3m-single-page-process (result)
  "If the page process is a singleton, adjust page container accordingly."
  (when result
    (lexical-let ((this-page (current-buffer))
        (active-process nil))
      (mapc
       (lambda (page)
         (with-current-buffer page
           (when (process-live-p fn/w3m-page-process)
             (setq active-process fn/w3m-page-process))

           (setq-local fn/w3m-page-process nil)))
       (w3m-list-buffers))
      (with-current-buffer this-page
        (setq-local fn/w3m-page-process active-process))))
  result)

(advice-add 'fn/w3m-view-video :filter-return  #'fn/w3m-single-page-process)

Advicing my own function is weird but I find it more appropriate as a hack instead of being part of the core. However, the real problem lies in a third possibility:

  • The process is managed by vlc itself and Emacs just gets dummy processes.

So if I enable said option, it cannot kill the window no matter what I do with the given process. In the end, it is really just a glue since the process cannot be ultimately managed which is understandable. The situation is not just with vlc but probably with other players as well so there is no need to craft perfect code, just working is enough.

So if assume one page per video is fine, then this is serviceable. Sadly, this also shows some limitation of managing processes but one really has to go to lengths to perfect it.

Autoplay

As a final feature, we can add autoplay on specific sites. For example, we open youtube and want the video to play. For this we can simply use the w3m-display-hook but we want to be a bit more cautious. Naively, this will open a video player for each page we visit. So we have to have a filter on what pages have videos in them. A simple predicate would be for this intention:

(defun fn/w3m-video-url-p (url)
  "Check if URL is a video."
  (ignore-errors
    (lexical-let* ((pieces (w3m-parse-http-url url))
        (host (elt pieces 1))
        (path (elt pieces 3)))
      (if (or (and (string= host "www.youtube.com")
                (string-prefix-p "/watch" path)))
          url nil))))

This checks if the path is has /watch primarily and ignores parsing errors. While there are more sites and possibilities, this is enough for now. Lastly, it is wise to ask for confirmation to open the video url of the page just in case you just want to browse. The familiar yes-or-no-p is a common theme here and we can combine that to come up with this autoplay function:

(defun fn/w3m-view-this-video-external (url)
  "View this video externally"
  (lexical-let ((video-url (fn/w3m-video-url-p (fn/w3m-video))))
    (when
        (and video-url
           (yes-or-no-p
            (format "%s is a video, view it with %s?"
                    video-url fn/w3m-video-executable)))
      (fn/w3m-view-video))))

(add-hook 'w3m-display-hook #'fn/w3m-auto-kill-page-process t)
(add-hook 'w3m-display-hook #'fn/w3m-view-this-video-external t)

Easy, so we now have autoplay but this was the primary driving force in managing the processes since I opened a lot of players during my own testing.

Conclusion

So this small journey of using processes and w3m was informative. The question is what else can we do? Here is somethings I tried and thought of:

  • Zoning out when retrieving a page or screensaver when retrieving a page
  • Auto article summarization with sumy which is my gist command in conkeror and probably a small post.

There are other text-based browsers such as Lynx and other browsers have plugins that does more and probably something more; as for me, I will be living the rest of my browsing experience with w3m and probably more to hack with.