2023-09-11 20:23:04 +02:00
|
|
|
{
|
|
|
|
config,
|
|
|
|
lib,
|
|
|
|
pkgs,
|
|
|
|
...
|
|
|
|
}:
|
2020-05-26 16:15:21 +02:00
|
|
|
with lib;
|
2023-09-11 20:23:04 +02:00
|
|
|
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
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
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")
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(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)))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(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)
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(defun elfeed-get-yt-description (url)
|
2024-11-16 11:02:33 +01:00
|
|
|
(shell-command-to-string (format "${pkgs.yt-dlp}/bin/yt-dlp --get-description \"%s\" 2> /dev/null" url)))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(defun elfeed-get-sd-article (url)
|
|
|
|
(eval-string (shell-command-to-string (format "${pyEnv}/bin/python3 ${pyScript} \"%s\" 2> /dev/null" url))))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(defun eval-string (string)
|
|
|
|
(eval (car (read-from-string (format "(progn %s)" string)))))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(defun elfeed-content-fetcher (entry)
|
|
|
|
"Fetches content for various entries (currently only sciencedaily)."
|
|
|
|
(interactive (list (elfeed-search-selected :ignore-region)))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(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))))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(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))))
|
2020-05-26 16:15:21 +02:00
|
|
|
|
|
|
|
|
2023-09-11 20:23:04 +02:00
|
|
|
(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)))))
|
|
|
|
'';
|
|
|
|
};
|
2020-05-26 16:15:21 +02:00
|
|
|
};
|
2023-09-11 20:23:04 +02:00
|
|
|
}
|