skeeto / elfeed

An Emacs web feeds client
The Unlicense
1.48k stars 112 forks source link

Special visit functions (e.g. mpv, eww) #267

Open Ambrevar opened 6 years ago

Ambrevar commented 6 years ago

Many feeds cannot be visited within elfeed. Two of the most common examples:

Possible solutions:

Implementation suggestion:

(defun elfeed-play-with-mpv ()
  "Play entry link with mpv."
  (interactive)
  (let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single)))
        (quality-arg "")
        (quality-val (completing-read "Max height resolution (0 for unlimited): " '("0" "480" "720") nil nil)))
    (setq quality-val (string-to-number quality-val))
    (message "Opening %s with height≤%s with mpv..." (elfeed-entry-link entry) quality-val)
    (when (< 0 quality-val)
      (setq quality-arg (format "--ytdl-format=[height<=?%s]" quality-val)))
    (start-process "elfeed-mpv" nil "mpv" quality-arg (elfeed-entry-link entry))))

(defun elfeed-open-with-eww ()
  "Open in eww with `eww-readable'."
  (interactive)
  (let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single))))
    (eww  (elfeed-entry-link entry))
    (add-hook 'eww-after-render-hook 'eww-readable nil t)))

(defvar elfeed-visit-patterns
  '(("youtu\\.?be" . elfeed-play-with-mpv)
    ("phoronix" . elfeed-open-with-eww))
  "List of (regexps . function) to match against elfeed entry link to know
whether how to visit the link.")

(defun elfeed-visit-maybe-externally ()
  "Visit with external function if entry link matches `elfeed-visit-patterns',
visit otherwise."
  (interactive)
  (let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single)))
        (patterns elfeed-visit-patterns))
    (while (and patterns (not (string-match (caar patterns) (elfeed-entry-link entry))))
      (setq patterns (cdr patterns)))
    (if patterns
        (funcall (cdar patterns))
      (if (eq major-mode 'elfeed-search-mode)
          (elfeed-search-browse-url)
        (elfeed-show-visit)))))

elfeed-visit-maybe-externally can then be used in lieu of elfeed-search-show-entry.

A more streamlined way to do this would be to assign a "visit function" to each feed in elfeed-feeds. Then elfeed could provide elfeed-play-with-mpv and elfeed-open-with-eww as example functions.

What do you think?

j3ky commented 6 years ago

mpv defun: highly appreciated!

benjaminwd commented 6 years ago

I like the idea of this in principle, but for truncated articles, I don't see the difference between "Visit with external function if link entry matches 'elfeed-visit-patterns'", and "visit otherwise" in your proposal. The behavior of this function either opens in eww with readability mode or opens in eww (or your browser of choice), without readability mode. Wouldn't it make more sense to open in eww with readability or to show the feed?

Ambrevar commented 6 years ago

Yes, I think it would but it's been a long time since I wrote this quick & dirty snippet. Let me have a second look and fix it (it was quite badly written).

Ambrevar commented 6 years ago

Now I remember: I originally took a conservative approach and I did not want to replace elfeed-search-show-entry (RET by default). I bound my proposal to S-RET in my init.el.

In the search view:

In the show view (same thing):

I don't think it's the right approach though: we should try to minimize the effort on the user end so that they don't have to remember whether they should press RET or S-RET.

So here is an update to my proposal, in the line of what @benjaminwd suggested:

In the search view:

In the show view:

What do you think?

Ambrevar commented 6 years ago

Updated function:

(defun elfeed-visit-maybe-externally ()
  "Visit with external function if entry link matches `elfeed-visit-patterns',
show normally otherwise."
  (interactive)
  (let ((entry (if (eq major-mode 'elfeed-show-mode)
                   elfeed-show-entry
                 (elfeed-search-selected :single)))
        (patterns elfeed-visit-patterns))
    (while (and patterns (not (string-match (caar patterns) (elfeed-entry-link entry))))
      (setq patterns (cdr patterns)))
    (cond
     (patterns
      (funcall (cdar patterns)))
     ((eq major-mode 'elfeed-search-mode)
      (elfeed-search-show-entry))
     (t (elfeed-show-visit)))))

Note that if the above function is called from the "show" buffer, it will either use eww-readable if matching or browse-url otherwise. Leaving S-RET to elfeed-show-visit enables us to open matched entries with browse-url.

benjaminwd commented 6 years ago

I did try switching to elfeed-search-show-entry as you show, but that is not getting the correct arguments. I don't know elfeed well yet, so I hesitate to speculate, but I suspect elfeed-search-show-entry is looking for something other than the link url.

Ambrevar commented 6 years ago

Sorry, elfeed-search-show-entry must be called intereactively. Fixed code:

(defun elfeed-visit-maybe-externally ()
  "Visit with external function if entry link matches `elfeed-visit-patterns',
show normally otherwise."
  (interactive)
  (let ((entry (if (eq major-mode 'elfeed-show-mode)
                   elfeed-show-entry
                 (elfeed-search-selected :single)))
        (patterns elfeed-visit-patterns))
    (while (and patterns (not (string-match (caar patterns) (elfeed-entry-link entry))))
      (setq patterns (cdr patterns)))
    (cond
     (patterns
      (funcall (cdar patterns)))
     ((eq major-mode 'elfeed-search-mode)
      (call-interactively 'elfeed-search-show-entry))
     (t (elfeed-show-visit)))))
Ambrevar commented 5 years ago

@skeeto: Ping?