summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorCase Duckworth2022-11-15 19:51:16 -0600
committerCase Duckworth2022-11-15 19:51:16 -0600
commitdc7b26268398cc72eb7fad17acc4adc07fd6f55e (patch)
tree34c9727bbbb9d99a27fa8567d7ad4f0459468df2
parentAdd `yoke-dirs' and functions working on them (diff)
downloademacs-dc7b26268398cc72eb7fad17acc4adc07fd6f55e.tar.gz
emacs-dc7b26268398cc72eb7fad17acc4adc07fd6f55e.zip
Customize browse-url
-rw-r--r--init.el75
-rw-r--r--lisp/+browse-url.el88
-rw-r--r--lisp/browse-url-transform.el45
3 files changed, 208 insertions, 0 deletions
diff --git a/init.el b/init.el index f7dcd7e..0bd11d4 100644 --- a/init.el +++ b/init.el
@@ -17,6 +17,7 @@
17 read-file-name-completion-ignore-case t 17 read-file-name-completion-ignore-case t
18 comment-auto-fill-only-comments t 18 comment-auto-fill-only-comments t
19 password-cache t 19 password-cache t
20 eww-use-browse-url "." ; use `browse-url' in every link
20 password-cache-expiry (* 60 60) 21 password-cache-expiry (* 60 60)
21 initial-buffer-choice (defun +initial-buffer-choose () 22 initial-buffer-choice (defun +initial-buffer-choose ()
22 (cond 23 (cond
@@ -904,3 +905,77 @@ CAPES defaults to `+capes'. CAPF will be made un-exclusive."
904 (spinner "https://github.com/Malabarba/spinner.el") 905 (spinner "https://github.com/Malabarba/spinner.el")
905 (sesman "https://github.com/vspinu/sesman")) 906 (sesman "https://github.com/vspinu/sesman"))
906 :when (executable-find "clojure")) 907 :when (executable-find "clojure"))
908
909(yoke browse-url
910 (require '+browse-url)
911 (setf browse-url-browser-function #'eww-browse-url
912 browse-url-chrome-program (seq-some #'executable-find
913 '("chromium" "chrome" "google-chrome-stable"))
914 browse-url-firefox-program (seq-some #'executable-find
915 '("firefox" "firefox-esr"))
916 browse-url-generic-program (or browse-url-firefox-program
917 browse-url-chrome-program)
918 browse-url-firefox-new-window-is-tab t
919 browse-url-firefox-arguments "-new-tab"
920 browse-url-handlers `((video-url-p . +browse-url-with-mpv)
921 (music-url-p . +browse-url-with-mpv)
922 (image-url-p . +browse-image-with-mpv)
923 (blobp . +browse-url-download)
924 (external-url-p . ,browse-url-secondary-browser-function)))
925 (+browse-url-make-external-viewer-handler "mpv" '("--cache-pause-wait=30"
926 "--cache-pause-initial=yes")
927 "Video URL: "
928 :fallback browse-url-secondary-browser-function)
929 (+browse-url-make-external-viewer-handler "mpv" '("--image-display-duration=inf")
930 "Image URL: "
931 :name +browse-image-with-mpv)
932 (defun video-url-p (url) "Is URL a video?"
933 (string-match-p (rx (or "youtube.com" "youtu.be" "invidious" "yewtu.be"
934 (seq "." (or "mp4" "gif" "mov" "MOV" "webm") eos)))
935 url))
936 (defun music-url-p (url) "Is URL music?"
937 (string-match-p (rx "soundcloud.com" "bandcamp.com"
938 (seq "." (or "ogg" "mp3" "opus" "m4a") eos))
939 url))
940 (defun image-url-p (url) "Is URL an image?"
941 (string-match-p (rx "." (or "jpeg" "jpg" "png" "bmp" "webp") eos)
942 url))
943 (defun external-url-p (url) "Should URL open in an external browser?"
944 (string-match-p (rx (or "github.com" "gitlab.com" "codeberg.org"
945 "tildegit.org" "git.tilde.town" "google.com"
946 "imgur.com" "twitch.tv" "pixelfed" "instagram.com"
947 "bibliogram.art" "reddit.com" "teddit.net"
948 "twitter.com" "nitter" "t.co"
949 "streamable.com" "spotify.com"
950 "hetzner.cloud" "melpa.org"))
951 url))
952 (defun blobp (url) "Is URL some other blob that can't open in Emacs?"
953 (string-match-p (rx (or (: (or ".tar.gz" ".pdf")
954 eos)))
955 url))
956 (eval-after chd
957 (add-to-list 'browse-url-handlers (cons chd/url-regexps #'browse-url-chrome)))
958 (require 'browse-url-transform)
959 (setf browse-url-transform-alist `(("twitter\\.com" . "nitter.net")
960 ("\\(?:\\(?:old\\.\\)?reddit\\.com\\)"
961 . "libreddit.de")
962 ("medium\\.com" . "scribe.rip")
963 ("www\\.npr\\.org" . "text.npr.org")))
964 (browse-url-transform-mode))
965
966(yoke eww
967 (defun +eww-browse-with-external-browser (&optional url)
968 "Browse URL with an external browser and close eww."
969 (interactive nil eww-mode)
970 (condition-case e
971 ;; This is wrapped in a `condition-case' so that the eww window won't
972 ;; close if there's an error calling the browser.
973 (funcall browse-url-secondary-browser-function
974 (or url (plist-get eww-data :url)))
975 (:success
976 (when (null url) ; interactive
977 (quit-window)))
978 (t (signal (car e) (cdr e)))))
979 (define-key* eww-mode-map
980 "&" #'+eww-browse-with-external-browser))
981
diff --git a/lisp/+browse-url.el b/lisp/+browse-url.el new file mode 100644 index 0000000..12e433c --- /dev/null +++ b/lisp/+browse-url.el
@@ -0,0 +1,88 @@
1;;; +browse-url.el -*- lexical-binding: t -*-
2
3(require 'browse-url)
4
5(cl-defmacro +browse-url-make-external-viewer-handler
6 (viewer default-args
7 &optional (prompt "URL: ")
8 &key
9 (custom-group 'browse-url)
10 (name (intern (format "+browse-url-with-%s" viewer)))
11 doc vardoc
12 (varname (intern (format "%s-args" name)))
13 (fallback t))
14 "Create a `browse-url' handler function calling VIEWER on the url.
15This macro also creates a `customize' setting in CUSTOM-GROUP for
16VIEWER's command-line arguments. DEFAULT-ARGS specifies the
17default arguments for that setting.
18
19PROMPT is shown to the user in the function's `interactive' spec,
20as an argument to `browse-url-interactive-arg'.
21
22The resulting function is named NAME, which defaults to
23`+browse-url-wth-VIEWER'. The custom variable is named VARNAME,
24which defaults to `NAME-args'. If DOC or VARDOC are provided,
25they'll be the documentation of the function and variable
26respectively; otherwise, basic docstrings are used.
27
28Finally, if FALLBACK is non-nil (by default, it's
29`browse-url-generic'), the function will call that if unable to
30start VIEWER."
31 (declare (indent 1))
32 `(progn
33 (defcustom ,varname ,default-args
34 ,(or doc (format "Arguments to pass to %s in `%s'." viewer name))
35 :type '(repeat :tag "Command-line argument" string)
36 :group ',custom-group)
37 (defun ,name (url &optional new-window)
38 ,(or vardoc (format "Open URL in %s." viewer))
39 (interactive (browse-url-interactive-arg ,prompt))
40 (let* ((url (browse-url-encode-url url))
41 (process-environment (browse-url-process-environment)))
42 (message ,(format "Opening %%s in %s..." viewer) url)
43 (unless (ignore-errors (apply #'start-process
44 (format "%s %s" ,viewer url) nil
45 ,viewer
46 (append ,varname (list url))))
47 ,@(cond
48 ((eq fallback t) '((browse-url-generic url new-window)))
49 (fallback `((funcall ,fallback url new-window)))
50 (:else `((message "Can't find viewer: `%s'" ,viewer)
51 nil))))))))
52
53(defcustom +browse-url-download-open t
54 "Whether to open downloaded files afterward."
55 :group 'browse-url
56 :type 'boolean)
57
58(defun +browse-url-download-callback (status url dir)
59 ;; A slight change to `eww-download-callback' that returns the downloaded
60 ;; filename.
61 (unless (plist-get status :error)
62 (let* ((obj (url-generic-parse-url url))
63 (path (directory-file-name (car (url-path-and-query obj))))
64 (file (eww-make-unique-file-name
65 (eww-decode-url-file-name (file-name-nondirectory path))
66 dir)))
67 (goto-char (point-min))
68 (re-search-forward "\r?\n\r?\n")
69 (let ((coding-system-for-write 'no-conversion))
70 (write-region (point) (point-max) file))
71 (message "Saved %s" file)
72 file)))
73
74(defun +browse-url-download (url &rest _)
75 "Download URL to `eww-download-directory'."
76 (interactive "sDownload URL: ")
77 (let ((dir eww-download-directory))
78 (when (functionp dir) (setq dir (funcall dir)))
79 (make-directory dir :parents)
80 (url-retrieve url
81 (lambda (s u d)
82 (let ((file (+browse-url-download-callback s u d)))
83 (when +browse-url-download-open
84 (browse-url-xdg-open file))))
85 (list url dir))))
86
87(provide '+browse-url)
88;;; +browse-url.el ends here
diff --git a/lisp/browse-url-transform.el b/lisp/browse-url-transform.el new file mode 100644 index 0000000..c290a1f --- /dev/null +++ b/lisp/browse-url-transform.el
@@ -0,0 +1,45 @@
1;;; browse-url-transform --- transform URLs
2
3(defgroup browse-url-transform nil
4 "Transformations for urls passed to `browse-url'."
5 :group 'browse-url)
6
7(defcustom browse-url-transform-alist nil
8 "Transformation rules for URLs.
9The keys are regexps matching URLs, and the values are how to
10transform them. The replacements will use match capture data."
11 :type '(alist :key-type (string :tag "URL regexp match")
12 :value-type (string :tag "URL regexp transformation")))
13
14(defun browse-url-transform-url (url)
15 "Transform URL before passing it to `browse-url'."
16 (cl-loop with url = (substring-no-properties
17 (if (consp url) (car url) url))
18 for (regex . transformation) in browse-url-transform-alist
19 if (string-match regex url)
20 return (replace-match transformation nil nil url)
21 finally return url))
22
23(defun browse-url-transform-advice (url &rest args)
24 "Advice to `browse-url' for URL transformations.
25ARGS are passed on for further processing."
26 (apply #'list (and url (browse-url-transform-url url)) args))
27
28(define-minor-mode browse-url-transform-mode
29 "Minor mode to transform a URL before passing it to `browse-url'.
30This can be used to \"redirect\" URLs, for example from an
31information silo to a more privacy-respecting one (e.g.,
32\"twitter.com\" -> \"nitter.com\"), by adding advice to `browse-url'.
33
34When using this mode, ensure that the transformed URL is also in
35`browse-url-handlers', since that's what `browse-url' will see."
36 :lighter " Turl"
37 :keymap nil
38 :global t
39 (cond (browse-url-transform-mode
40 (advice-add 'browse-url :filter-args #'browse-url-transform-advice))
41 (:else
42 (advice-remove 'browse-url #'browse-url-transform-advice))))
43
44(provide 'browse-url-transform)
45;;; browse-url-transform.el ends here