;;; ol-preview-web.el --- Preview web links in Org -*- lexical-binding: t; -*- ;; Copyright (C) 2024 Karthik Chikmagalur ;; Author: Karthik Chikmagalur ;; Keywords: hypermedia, extensions ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;;; Commentary: ;; Prototype code for previewing youtube link thumbnails ;; asynchronously in Org using `org-link-preview'. To run this make ;; sure you have the `plz' package installed and evaluate this file. ;;; Code: (require 'ol) (require 'rx) (require 'plz) (require 'url-util) (defvar ol-preview-web-youtube-regexp (rx bol ;; (zero-or-one (or "http://" "https://")) (zero-or-one "//") (zero-or-one "www.") (or "youtube.com/" "youtu.be/")) "Regex to match Youtube links") (defvar ol-preview-web--invidious-servers nil) (defun ol-preview-web--get-invidious-servers (&optional rotate) (when (and ol-preview-web--invidious-servers rotate) (setq ol-preview-web--invidious-servers (nconc (cdr ol-preview-web--invidious-servers) (list (car ol-preview-web--invidious-servers))))) (or ol-preview-web--invidious-servers (setq ol-preview-web--invidious-servers (when-let ((raw (plz 'get (concat "https://api.invidious.io/instances.json" "?pretty=1&sort_by=type,users") :then 'sync))) (thread-last (json-parse-string raw :object-type 'plist :array-type 'list) (cl-remove-if-not (lambda (s) (eq t (plist-get (cadr s) :api)))) (mapcar #'car)))))) (defun ol-preview-web-link (ov _type path) "Fetch a youtube thumbnail for the link with PATH asynchronously. Then place the thumbnail over the overlay OV in an Org buffer." (when (string-match (concat ol-preview-web-youtube-regexp "\\(?:watch\\?v=\\)?" "\\([^?&]+\\)") path) (let* ((video-id (match-string 1 path)) (params (url-build-query-string '(("fields" "videoThumbnails")))) (api-url (cadr (ol-preview-web--get-invidious-servers))) (query-url (concat api-url "/api/v1/videos/" video-id "?" params)) (thumb-file (expand-file-name (concat video-id ".jpg") temporary-file-directory)) (place-func (lambda (imagefile) (when (and (file-exists-p imagefile) (buffer-live-p (overlay-buffer ov))) (with-current-buffer (overlay-buffer ov) (let* ((link (org-element-lineage (save-excursion (goto-char (overlay-start ov)) (save-match-data (org-element-context))) 'link t)) (align (org-image--align link)) (image (org--create-inline-image imagefile 360))) (when image (when (boundp 'image-map) (overlay-put ov 'keymap image-map)) (when align (overlay-put ov 'before-string (propertize " " 'face 'default 'display (pcase align ("center" `(space :align-to (- center (0.5 . ,image)))) ("right" `(space :align-to (- right ,image))))))) (overlay-put ov 'display image)))))))) (if (file-exists-p thumb-file) (funcall place-func thumb-file) (plz 'get query-url :as (lambda () (json-parse-buffer :object-type 'plist)) :then (lambda (response) (when-let ((response) (thumburl (condition-case nil (thread-first response (plist-get :videoThumbnails) (aref 3) (plist-get :url)) (error nil)))) (plz 'get thumburl :as `(file ,thumb-file) :then place-func)))))))) ;; Register handler for https Org links (org-link-set-parameters "https" :preview #'ol-preview-web-link) (provide 'ol-preview-web) ;;; ol-preview-web.el ends here