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.
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
(url at point) or
current (page) url and passes is on via
start-process. Nothing really fancy here so let's explore this idea
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.
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.
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
(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.
If you do open a process in Emacs, you can check it out using the
list-processes as shown below.
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
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.
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
--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
- 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
(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.
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.
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
gistcommand 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.