diff --git a/pkgsets/emacs/elfeed.nix b/pkgsets/emacs/elfeed.nix new file mode 100644 index 0000000..4f1780b --- /dev/null +++ b/pkgsets/emacs/elfeed.nix @@ -0,0 +1,148 @@ +{ config, lib, pkgs, ... }: + +with lib; + +mkIf (elem "emacs::elfeed" config.machine.pkgs) { + programs.emacs.init.usePackage = { + elfeed = let + pyEnv = pkgs.python3.withPackages (ps: with ps; [ beautifulsoup4 lxml requests ]); + pyScript = pkgs.writeScript "elfeedFetcher.py" '' + #!${pyEnv}/bin/python3 + import sys + from requests import get + from bs4 import BeautifulSoup + + header_agent = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:75.0) Gecko/20100101 Firefox/75.0'} + with get(sys.argv[1], headers=header_agent) as r: + soup = BeautifulSoup(r.text, 'lxml') + abstract = soup.find('dd', {"id": "abstract"}).text.replace('"', '\\"') + article = '\n\n'.join( + [soup.find('p', {"id": "first"}, class_="lead").text] + + [ + p.text for p in + soup.find('div', {"id": "text"}).findAll('p') + ]).replace('"', '\\"') + print(f"(setq abstract \"{abstract}\") (setq article \"{article}\")") + ''; + in { + enable = true; + hook = [ ''(elfeed-new-entry . elfeed-content-fetcher)'' ]; + config = '' + (load-file "~/.emacs.d/elfeed.el") + + (defun elfeed-play-with-mpv (entry) + "Play entry link with mpv." + (interactive (elfeed-search-selected :single)) + (start-process "elfeed-mpv" nil "mpv" "--ytdl-format=[height<=1080]" (elfeed-entry-link entry))) + + (defun elfeed-search-show-entry-custom (entry) + "Custom actions for various sources." + (interactive (list (elfeed-search-selected :ignore-region))) + (if (equal (car (elfeed-entry-id entry)) "www.youtube.com") (elfeed-play-with-mpv entry) + (elfeed-show-entry entry))) + (define-key elfeed-search-mode-map [remap elfeed-search-show-entry] 'elfeed-search-show-entry-custom) + + (defun elfeed-get-yt-description (url) + (shell-command-to-string (format "${pkgs.youtube-dl}/bin/youtube-dl --get-description \"%s\" 2> /dev/null" url))) + + (defun elfeed-get-sd-article (url) + (eval-string (shell-command-to-string (format "${pyEnv}/bin/python3 ${pyScript} \"%s\" 2> /dev/null" url)))) + + (defun eval-string (string) + (eval (car (read-from-string (format "(progn %s)" string))))) + + (defun elfeed-content-fetcher (entry) + "Fetches content for various entries (currently only sciencedaily)." + (interactive (list (elfeed-search-selected :ignore-region))) + + (let ((url (elfeed-entry-link entry)) + (feed-id (elfeed-deref (elfeed-entry-feed-id entry))) + ) + (when (equal (car (elfeed-entry-id entry)) "www.sciencedaily.com") + (elfeed-get-sd-article (cdr (elfeed-entry-id entry))) + ;; (setf (elfeed-entry-content entry) (elfeed-ref article)) + (setf (elfeed-meta entry :content) (elfeed-ref article)) + (setf (elfeed-meta entry :abstract) abstract) + (makunbound 'abstract) + (makunbound 'article)))) + + (defun elfeed-show-refresh--mail-style () + "Update the buffer to match the selected entry, using a mail-style." + (interactive) + (let* ((inhibit-read-only t) + (title (elfeed-entry-title elfeed-show-entry)) + (date (seconds-to-time (elfeed-entry-date elfeed-show-entry))) + (authors (elfeed-meta elfeed-show-entry :authors)) + (link (elfeed-entry-link elfeed-show-entry)) + (tags (elfeed-entry-tags elfeed-show-entry)) + (tagsstr (mapconcat #'symbol-name tags ", ")) + (nicedate (format-time-string "%a, %e %b %Y %T %Z" date)) + (content (if (elfeed-meta elfeed-show-entry :content) (elfeed-deref (elfeed-meta elfeed-show-entry :content)) (elfeed-deref (elfeed-entry-content elfeed-show-entry)))) + (type (elfeed-entry-content-type elfeed-show-entry)) + (feed (elfeed-entry-feed elfeed-show-entry)) + (feed-title (elfeed-feed-title feed)) + (base (and feed (elfeed-compute-base (elfeed-feed-url feed))))) + (erase-buffer) + (insert (format (propertize "Title: %s\n" 'face 'message-header-name) + (propertize title 'face 'message-header-subject))) + (when elfeed-show-entry-author + (dolist (author authors) + (let ((formatted (elfeed--show-format-author author))) + (insert + (format (propertize "Author: %s\n" 'face 'message-header-name) + (propertize formatted 'face 'message-header-to)))))) + (insert (format (propertize "Date: %s\n" 'face 'message-header-name) + (propertize nicedate 'face 'message-header-other))) + (insert (format (propertize "Feed: %s\n" 'face 'message-header-name) + (propertize feed-title 'face 'message-header-other))) + (when tags + (insert (format (propertize "Tags: %s\n" 'face 'message-header-name) + (propertize tagsstr 'face 'message-header-other)))) + (insert (propertize "Link: " 'face 'message-header-name)) + (elfeed-insert-link link link) + (insert "\n") + (cl-loop for enclosure in (elfeed-entry-enclosures elfeed-show-entry) + do (insert (propertize "Enclosure: " 'face 'message-header-name)) + do (elfeed-insert-link (car enclosure)) + do (insert "\n")) + (insert "\n") + (if content + (if (eq type 'html) + (elfeed-insert-html content base) + (insert content)) + (insert (propertize "(empty)\n" 'face 'italic))) + (goto-char (point-min)))) + + + (defun elfeed-db-gc (&optional stats-p) + "Clean up unused content from the content database. + If STATS is true, return the space cleared in bytes." + (elfeed-db-gc-empty-feeds) + (let* ((data (expand-file-name "data" elfeed-db-directory)) + (dirs (directory-files data t "^[0-9a-z]\\{2\\}$")) + (ids (cl-mapcan (lambda (d) (directory-files d nil nil t)) dirs)) + (table (make-hash-table :test 'equal))) + (dolist (id ids) + (setf (gethash id table) nil)) + (with-elfeed-db-visit (entry _) + (let ((content (elfeed-entry-content entry)) + (meta-content (elfeed-meta entry :content))) + (when (elfeed-ref-p content) + (setf (gethash (elfeed-ref-id content) table) t)) + (when (elfeed-ref-p (meta-content)) + (setf (gethash (elfeed-ref-id meta-content) table) t)))) + (cl-loop for id hash-keys of table using (hash-value used) + for used-p = (or used (member id '("." ".."))) + when (and (not used-p) stats-p) + sum (let* ((ref (elfeed-ref--create :id id)) + (file (elfeed-ref--file ref))) + (* 1.0 (nth 7 (file-attributes file)))) + unless used-p + do (elfeed-ref-delete (elfeed-ref--create :id id)) + finally (cl-loop for dir in dirs + when (elfeed-directory-empty-p dir) + do (delete-directory dir))))) + ''; + }; + }; +}