Yay Emacs: Using elisp: links in Org Mode to note the time and display messages on stream
| yay-emacs, org
I like adding chapters to my videos so that people can jump to sections. I can figure out the sections by reading the transcript, adding NOTE
comments, and extracting the times for those with my-youtube-copy-chapters. It could be nice to capture the times on the fly. org-timer
could let me insert relative timestamps, but I think it might need some tweaking to synchronize that with when the stream starts according to YouTube. I've set up a capture, too, so I can take notes with timestamps.
It turns out that I don't have a lot of mental bandwidth when I'm on stream, so it's hard to remember keyboard shortcuts. (Maybe if I practise using the hydra I set up…) Fortunately, Org Mode's elisp:
link type makes it easy to set up executable shortcuts. For example, I can add links like [[elisp:my-stream-message-link][TODO]]
to my livestream plans like this:
I can then click on the links or use C-c C-o
(org-open-link-at-point
) to run the function. When I follow the TODO link in the first item, Emacs displays a clock and a message based on the rest of the line after the link.
In the background, the code also sets the description of the link to the wall-clock time.
If I start the livestream with a clock displayed on screen, I can use that to translate wall-clock times to relative time offsets. I'll probably figure out some Elisp to translate the times automatically at some point, maybe based on something like org-timer-change-times-in-region
.
I figured it might be fun to add a QR code automatically if we detect a URL, taking advantage of that qrencode package I started playing around with.
You can also use elisp:
links for more complicated Emacs Lisp functions, like this: elisp:(progn ... ...)
.
Here's the code that makes it happen. It's based on emacsconf-stream.el.
(defvar my-stream-message-buffer "*Yay Emacs*") (defvar my-stream-message-timer nil) (defun my-stream-message-link () (interactive) (save-excursion (when (and (derived-mode-p 'org-mode) (eq (org-element-type (org-element-context)) 'link)) (my-stream-update-todo-description-with-time) (goto-char (org-element-end (org-element-context))) (my-stream-message (org-export-string-as (buffer-substring (point) (line-end-position)) 'ascii t))))) (defun my-stream-update-todo-description-with-time () (when (and (derived-mode-p 'org-mode) (eq (org-element-type (org-element-context)) 'link)) (my-org-update-link-description (format-time-string "%-I:%M:%S %p")))) (defun my-stream-message (&optional message) (interactive "MMessage: ") ;; update the description of the link at point to be the current time, if any (switch-to-buffer (get-buffer-create my-stream-message-buffer)) (erase-buffer) (delete-other-windows) (when (string= message "") (setq message nil)) (face-remap-add-relative 'default :height 200) (insert "Yay Emacs! - Sacha Chua (sacha@sachachua.com)\n" (propertize "date" 'stream-time (lambda () (format-time-string "%Y-%m-%d %H:%M:%S %Z (%z)"))) "\n\n" message) ;; has a URL? Let's QR encode it! (when-let ((url (save-excursion (when (re-search-backward ffap-url-regexp nil t) (thing-at-point-url-at-point))))) (insert (propertize (qrencode url) 'face '(:height 50)) "\n")) (insert "\nYayEmacs.com\n") (when (timerp my-stream-message-timer) (cancel-timer my-stream-message-timer)) (my-stream-update-time) (setq my-stream-message-timer (run-at-time t 1 #'my-stream-update-time)) (goto-char (point-min))) (defun my-stream-update-time () "Update the displayed time." (if (get-buffer my-stream-message-buffer) (when (get-buffer-window my-stream-message-buffer) (with-current-buffer my-stream-message-buffer (save-excursion (goto-char (point-min)) (let (match) (while (setq match (text-property-search-forward 'stream-time)) (goto-char (prop-match-beginning match)) (add-text-properties (prop-match-beginning match) (prop-match-end match) (list 'display (funcall (get-text-property (prop-match-beginning match) 'stream-time)))) (goto-char (prop-match-end match))))))) (when (timerp my-stream-message-timer) (cancel-timer my-stream-message-timer))))
Let's see if that makes it easy enough for me to remember to actually do it!