Emacs Lisp

Table of Contents

1. Switch to vterm buffer

(defun alan-vterm ()
  "Switch to an existing vterm session or create one based on the current buffer directory."
  (interactive)
  (if (file-remote-p default-directory)
      (let ((remote-host (file-remote-p default-directory)))
        (string-match "/ssh:\\([^:]+\\):" remote-host)
        (setq remote-host (match-string 1 remote-host))
        (setq remote-host-buffer (concat "*" remote-host "*"))

        (if (get-buffer remote-host-buffer)
            (pop-to-buffer-same-window remote-host-buffer)

          ;; Set current buffer directory to localhost path to
          ;; prevent TRAMP connecting with `term'
          (let ((default-directory (getenv "HOME")))
            (vterm remote-host-buffer)
            (with-current-buffer remote-host-buffer
              (evil-local-mode -1)
              (setq-local show-trailing-whitespace nil)
              (vterm-send-string (concat "ssh " remote-host))
              (vterm-send-return)))))

    (let ((host-buffer (concat "*" system-name "*")))
      (if (get-buffer host-buffer)
          (pop-to-buffer-same-window host-buffer)
        (let ((default-directory (getenv "HOME")))
          (vterm host-buffer)
          (with-current-buffer host-buffer
            (evil-local-mode -1)
            (setq-local show-trailing-whitespace nil)))))))

2. Add tag dl to selected entries found in yt-dlp archive

(defun alan-elfeed-tag-downloaded-videos ()
  "Add tag `dl' to selected entries found in yt-dlp archive."
  (interactive)
  (when (eq major-mode 'elfeed-search-mode)
    (let ((archive "~/.local/share/yt-dlp/archive.log")
          (entries (elfeed-search-selected))
          (regexp "\\(/watch\\?v=\\|youtu.be/\\)\\([^?&]+\\)"))
      (when (file-exists-p archive)
        (dolist (entry entries)
          (when (elfeed-entry-p entry)
            (let ((link (elfeed-entry-link entry)))
              (when (string-match regexp link)
                (let ((video-id (match-string 2 link)))
                  (with-temp-buffer
                    (insert-file-contents archive)
                    (goto-char (point-max))
                    (when (search-backward video-id nil t)
                      (elfeed-tag entry 'dl)
                      (elfeed-search-update :force))))))))))))

3. Filter entries by project instance

(defun alan-elfeed-search-select-instance ()
  "Filter entries in `elfeed-search-mode' by project instance."
  (interactive)
  (let* ((projects
          '(("Invidious" . "/feed/channel/")
            ("RSS Bridge" . "&bridge=")))
         (selected-project
          (completing-read "Select project: " (mapcar #'car projects)))
         (project-regexp (cdr (assoc selected-project projects)))
         (instances
          (cl-loop for (feed . _) in elfeed-feeds
                   when (string-match-p project-regexp feed)
                   collect (url-host (url-generic-parse-url feed)) into list
                   finally return (delete-dups list)))
         (selected-instance (completing-read "Select instance: " instances)))
    (setq elfeed-search-filter (concat "=" selected-instance))
    (elfeed-search-update :force)
    (elfeed-search-first-entry)))

4. Select new ls switches for current Dired buffer

(defun alan-dired-sort ()
  "Select new `ls' switches for current Dired buffer."
  (interactive)
  (let* ((switches
          '(("Access" . "-lhAtu")
            ("Modification" . "-lhAtc")
            ("Creation" . "-lhAt --time=creation")
            ("Extension" . "-lhAtX")
            ("Name (default)" . "-lhav --group-directories-first")
            ("Size" . "-lhAS")))
         (selected-switches
          (completing-read "Sort by: " (mapcar #'car switches))))
    (dired-sort-other
     (cdr (assoc selected-switches switches)))))

5. Read YouTube video top comments in a buffer using yt-dlp

# ~/.config/yt-dlp/comments.conf - yt-dlp(1)
--skip-download
--write-comments
--print-to-file "after_filter:%(comments)j" "/tmp/comments.json"
--no-write-info-json
--parse-metadata "video::(?P<comments>)"
--extractor-args "youtube:comment_sort=top,max_comments=5,5,2,2"
(defun alan-read-video-comments (video-id)
  (interactive (list (read-from-minibuffer "VideoID: ")))
  (require 'json)
  (message "Fetching comments... [ID:%s]" video-id)
  (let* ((comments "/tmp/comments.json")
         (cmd `(,(executable-find "yt-dlp")
                "--config-locations"
                ,(expand-file-name "~/.config/yt-dlp/comments.conf")
                ,(format "https://www.youtube-nocookie.com/embed/%s" video-id)))
         (exit-status
          (apply #'call-process (car cmd) nil nil nil (cdr cmd))))
    (if (zerop exit-status)
        (let ((buf (get-buffer-create
                    (format "*Video comments from [ID:%s], %s*"
                            video-id (format-time-string "%c"))))
              (data (json-read-file comments)))
          (with-current-buffer buf
            (mapcar (lambda (comment)
                      (let* ((parent (alist-get 'parent comment))
                             (text (alist-get 'text comment))
                             (like (alist-get 'like_count comment))
                             (author (alist-get 'author comment))
                             (type (if (string= parent "root")
                                       "comment"
                                     "reply")))
                        (insert (format " ---- %s from %s (%s likes) ----\n%s\n\n"
                                        type author like text))))
                    data))
          (pop-to-buffer-same-window buf)
          (goto-char (point-min))))
    (when (file-exists-p comments)
      (delete-file comments))))

6. Decode encoded paths

(url-unhex-string (url-filename (url-generic-parse-url ENCODED-PATH)))

7. QR code of clipboard content

(defun alan-yank-to-qrcode ()
  (interactive)
  (let ((buf (get-buffer-create "*qrcode*"))
        (recent-kill (with-temp-buffer (yank) (buffer-string))))
    (with-current-buffer buf
      (call-process "/usr/bin/python3-qr" nil buf nil "--ascii" recent-kill)
      (while (search-backward " " nil t)
        (replace-match " " nil t))
      (pop-to-buffer-same-window buf))))

8. Return the channel name associated with CHANNEL-ID

(defun alan-elfeed-return-channel-name (channel-id)
  "Return the channel name associated with CHANNEL-ID."
  (let ((channel-name nil))
    (with-temp-buffer
      (insert-file-contents-literally
       (locate-user-emacs-file "lisp/alan-feeds.el"))
      (goto-char (point-min))
      (while (re-search-forward (format "channel_id=%s.+$" channel-id) nil t)
        (let ((line (match-string 0)))
          (when (string-match ".+?;\s\\(.+\\)$" line)
            (setq channel-name (match-string 1 line))))))
    (when channel-name
      channel-name)))

9. Compare Elfeed and FreshRSS feeds

(defun alan-elfeed-freshrss-compare-feeds ()
  "Compare feeds with FreshRSS."
  (interactive)
  (if-let* ((freshrss-xml-file
             (directory-files "~/downloads/" t "^feeds_.+?\\.xml$")))
      (let ((freshrss-feeds-list
             (xml-query-all
              '(body outline outline [type "rss"] :xmlUrl)
              (car (xml-parse-file (car freshrss-xml-file)))))
            (elfeed-feeds-list
             (elfeed-feed-list)))
        (dolist (f freshrss-feeds-list)
          (unless (member f elfeed-feeds-list)
            (if (string-match-p "channel_id=" f)
                (let* ((channel-id (cadr (string-split f "=")))
                       (channel-name
                        (alan-elfeed-return-channel-name channel-id)))
                  (message "FRESHRSS ONLY: %s -- %s" f channel-name))
              (message "FRESHRSS ONLY: %s" f))))
        (dolist (e elfeed-feeds-list)
          (unless (member e freshrss-feeds-list)
            (if (string-match-p "channel_id=" e)
                (let* ((channel-id (cadr (string-split e "=")))
                       (channel-name
                        (alan-elfeed-return-channel-name channel-id)))
                  (message "ELFEED ONLY: %s -- %s" e channel-name))
              (message "ELFEED ONLY: %s" e))))
        (pop-to-buffer-same-window "*Messages*"))
    (user-error "FreshRSS feeds XML file not found")))

10. Parse FreshRSS XML file to show filters

(defun alan-elfeed-freshrss-show-filters ()
  "Parse FreshRSS XML file to show filters."
  (interactive)
  (if-let* ((xml-file
             (car (directory-files "~/downloads/" t "^feeds_.+?\\.xml$"))))
      (let* ((feeds
              (xml-query-all
               '(body outline outline [type "rss"])
               (car (xml-parse-file xml-file))))
             (filters
              (mapcar
               (lambda (feed)
                 (let ((a (cadr feed)))
                   (list (assoc-default 'text a)
                         (assoc-default 'frss:filtersActionRead a))))
               feeds)))
        (pop-to-buffer-same-window
         (format "*FreshRSS filters: %s*" (format-time-string "%c")))
        (dolist (f filters)
          (unless (null (cadr f))
            (insert
             (format
              "\n%s\n%s\n%s\n"
              (car f)
              (make-string (length (car f)) ?-)
              (cadr f)))))
        (local-set-key "q" #'kill-buffer)
        (read-only-mode 1))
    (user-error "FreshRSS XML file not found")))