Where parallels cross

Interesting bits of life

Emacs with Nyxt: capturing YouTube links at time and molding Nyxt with JS

Too long; didn't read

The little command org-capture in emacs-with-nyxt now captures YouTube links with duration! And moldable-emacs lets you interact with Nyxt in JavaScript.

The problem

When I watch a YouTube video I like to capture links. It is useful to take a note about a certain moment in the video. Often this saves my (and others) time because I can review only the bit I cared to cite. So far this involved a bit of clicking in the browser. I had plans to automate this with Nyxt, but watching video didn't really work on the Guix version. Then, just the other day, Nyxt 2.2.1 came out. And for me YouTube came with it (because @ambrevar fixed a bug in the Debian package: thanks!!). I already captured notes via Nyxt: so how difficult would be to capture the link at a certain duration?

It is a problem indeed

This is super interesting for a subtle reason: it involves interacting with JavaScript (JS)! When you click on a video, it is evident that you see a menu produced via JS. And if you read my previous blogs, you know that I believe that small things can open new worlds. Here capturing a link could let us connect Emacs to the browser JS console! And that definitely means we could make a JS Playground in moldable-emacs. That is exciting because you can command a webpage: did you ever want to login to your bank account automatically? What is the first step to get to that? A YouTube link with duration :D

And there is a solution

First things first. I discovered there is an easy way to run your JS in Nyxt: ffi-buffer-evaluate-javascript. This little function is the engine of what I am showing you next.

In the video above I take an Org Roam note with a "timed" YouTube link. I show that by opening the link in Firefox: the video opens at the same point. And you can see a Playground in action to query the current page in JS and getting the data in Emacs!

The function ffi-buffer-evaluate-javascript takes two arguments: the current Nyxt buffer and a string with JavaScript. The result is in Common Lisp.

You can evaluate the duration of the video you are watching by evaluating the following JS.

document.getElementById('movie_player').getCurrentTime();

This queries the movie_player element for the timestamp. This returns a real number. We can achieve the very same with Parenscript.

(ps:ps (ps:chain document (get-element-by-id "movie_player") (get-current-time)))

This lets you use Common Lisp to produce JS: indeed, ps:ps converts the Common Lisp code into a JS string.

Given the YouTube url, all you need is the following.

(str:concat
          url
          "&t="
          (write-to-string ;; Common Lisp way to produce a string
           (floor ;; keep only the integer part
            (ffi-buffer-evaluate-javascript (current-buffer)
                                            (ps:ps (ps:chain document (get-element-by-id "movie_player") (get-current-time))))))
          "s")

This code produces a link like "www.youtube.com?v=xxx&t=10s". Injecting that in my old commands org-capture and org-roam-capture makes capturing possible.

That inspired me to make a Playground mold to exploit ffi-buffer-evaluate-javascript even more! Now you can query/command the JS of a Nyxt page and get the results in your Emacs. How cool would it be to automate boring flows that don't have APIs via JS? With a mix of emacs-with-nyxt and moldable-emacs, I am sure I could automate a lot of things that I do manually. I am pretty sure I will write more about this soon!

Conclusion

We can interact with our browser JS from Emacs. Probably there was some way to that even before, but now we can interact with Nyxt and in Common Lisp!! So if you know JS, Common Lisp and you use Emacs, it is time you grab my emacs-with-nyxt and start exploring your options :D

Happy JavaScripting!

Comments