I sometimes keep YouTube links in my Org mode files. They are often links to lectures which I don’t usually watch in one sitting. One trouble I always have is remembering the point where I stopped watching last time and starting there.
Obviously, I decided to write some Elisp to ease that task a bit. Given the time, I want to add a query parameter to the URL (or replace one that’s already there).
This task is fairly easy, though there are a few cases to consider. First of all, the suitable parameter may already be there or not. Then, YouTube links come in two forms (maybe more, I don’t really know) – a shorter one and a longer one. Finally, it would be nice to be able to supply the time either as a number of seconds (possibly with an s suffix) or as a m:ss-type string.
Another potentially difficult thing is making sure that the point is on an URL (and finding the places where the link starts and ends). This, however, is a solved problem thanks to the thingatpt package.
So, let’s bind all this together. The following function asks the user for the time (it also accepts a prefix argument – as the number of seconds) and sets the time parameter in the link.
(require 'thingatpt)
(defun yt-set-time (time)
"Set TIME in the YouTube link at point.
TIME is number of seconds if called from Lisp, and a string if
called interactively. Supported formats:
- seconds
- minutes:seconds
- number of seconds with the \"s\" suffix."
(interactive (list
(if current-prefix-arg
(prefix-numeric-value current-prefix-arg)
(read-string "Time: "))))
(let ((url (thing-at-point-url-at-point)))
(if (and url
(string-match
(format "^%s"
(regexp-opt
'("https://www.youtube.com/"
"https://youtu.be/")
"\\(?:"))
url))
(let* ((bounds (thing-at-point-bounds-of-url-at-point))
(time-present-p (string-match "t=[0-9]+" url))
(question-mark-present-p (string-search "?" url))
(seconds (cond
((numberp time)
time)
((string-match
"^\\([0-9]+\\):\\([0-9]\\{2\\}\\)$" time)
(+ (* 60 (string-to-number
(match-string 1 time)))
(string-to-number (match-string 2 time))))
((string-match "^\\([0-9]+\\)s?$" time)
(string-to-number (match-string 1 time)))
(t (error "Wrong argument format"))))
(new-url (if time-present-p
(replace-regexp-in-string
"t=[0-9]+"
(format "t=%i" seconds)
url)
(concat url
(if question-mark-present-p "&" "?")
(format "t=%i" seconds)))))
(delete-region (car bounds) (cdr bounds))
(insert new-url))
(error "Not on a Youtube link"))))