Emacs Lisp
Table of Contents
- 1. Switch to vterm buffer
- 2. Add tag
dlto selected entries found in yt-dlp archive - 3. Filter entries by project instance
- 4. Select new
lsswitches for current Dired buffer - 5. Read YouTube video top comments in a buffer using
yt-dlp - 6. Decode encoded paths
- 7. QR code of clipboard content
- 8. Return the channel name associated with
CHANNEL-ID - 9. Compare Elfeed and FreshRSS feeds
- 10. Parse FreshRSS XML file to show filters
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")))