Ihor Radchenko <[email protected]> writes:
> See 43.17 Batch Mode section of Elisp manual. It is Emacs default to
> print message output to stderr.
OK, thanks for the information. This means that the warnings are finding a
needle in a haystack, though one can always grep for "Warning" in the stderr.
About warnings: I now issue a warning if a link description refers to the full
domain being replaced, like so:
Warning (emacs): Link description "Org-mode discussions on reddit.com" in
/home/rens/Projects/org/worg/index.org:87 refers to the full domain
"reddit.com", which we change. This is confusing, please change the description.
and I have also manually fixed all (3) cases where this happened.
>>>> +(push "./" load-path)
>>>> +(require 'rewrite-urls)
>>>
>>> We can simply use (load "rewrite-urls")
>>> Also, it is a good practice to use a non-generic prefix. Something like
>>> worg-urls.
>>
>> I can't get (load "...") to work, unless I do (push "./" load-path)
>> beforehand. From the docs of `load':
>>
>> ...
>> This function searches the directories in ‘load-path’.
>>
>> Am I missing something?
>
> Yeah. Need (load "./rewrite-urls") - path, not just file name.
This still does not work for me.
(load "./worg-urls-rewrite") gives the error
Error: file-missing ("Cannot open load file" "No such file or directory"
"./worg-urls-rewrite")
load("./worg-urls-rewrite")
#<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_101>(#<buffer
*load*> "/home/rens/Projects/org/worg/publish.sh")
load-with-code-conversion("/home/rens/Projects/org/worg/publish.sh"
"/home/rens/Projects/org/worg/publish.sh" nil t #<subr
F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_101>)
command-line--load-script("/home/rens/Projects/org/worg/publish.sh")
command-line-1(("-scriptload" "./publish.sh" "--"))
command-line()
normal-top-level()
Cannot open load file: No such file or directory, ./worg-urls-rewrite
and adding the .el suffix with (load "./worg-urls-rewrite.el") gives the same
error. It works when I do (push "." load-path) beforehand, though.
> I will give some quick feedback on what you have below.
Thanks.
>> -Tony day [[http://mid.gmane.org/[email protected]][shared]]
>> [[https://gist.github.com/4343164][this gist]] to pick up a
>> +Tony day [[http://mid.gmane.org/[email protected]][shared]]
>> [[https://gist.github.com/tonyday567/4343164][this gist]] to pick up a
>> random task.
>
> Side note: gmane is down, so all links it gmane are broken.
I have ignored this for now since it's somewhat orthogonal to this, but I can
fix this up later.
>> +(defun -get-libredirect-data ()
>> + "Returns libredirect data from github"
>
> Here and in other places, should use prefix worg-urls-get-libredirect-data.
> Also, try M-x checkdoc on the file. You will see a lot of stylistic
> suggestions.
Have done so, see attached patch for the updated version. Again, you can see
the changes on https://files.rensoliemans.nl/worg, though the new changes have
been mostly internal.
Best,
Rens
>From 0fea07080ad58c0a8031409af34d6c414f57582f Mon Sep 17 00:00:00 2001
From: Rens Oliemans <[email protected]>
Date: Fri, 21 Nov 2025 20:13:58 +0100
Subject: [PATCH] Add alternative links for YouTube, GitHub and Reddit
This commit finds alternative links for the hosts mentioned above
since they rely on non-free JS. See
https://list.orgmode.org/orgmode/87pl9szmy6.fsf@localhost/ for
context.
---
.gitignore | 1 +
index.org | 2 +-
org-contrib/index.org | 2 +-
org-hacks.org | 2 +-
org-tests/index.org | 15 +-
org-tutorials/non-beamer-presentations.org | 4 +-
publish.sh | 59 ++---
rewrite-websites.js | 24 ++
worg-urls-rewrite.el | 251 +++++++++++++++++++++
9 files changed, 321 insertions(+), 39 deletions(-)
create mode 100644 rewrite-websites.js
create mode 100644 worg-urls-rewrite.el
diff --git a/.gitignore b/.gitignore
index c9dc5b62..c572b901 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
patches/
/.project
*patch
+*.eld
diff --git a/index.org b/index.org
index 9c14fa3e..237f7b0f 100644
--- a/index.org
+++ b/index.org
@@ -84,7 +84,7 @@ is maintained by a group of [[file:worgers.org][Worg contributors]], and maybe [
- [[file:org-irc.org][Org-mode IRC Channel]] : For live Q&A, go to =#org-mode= on Libera
- Check [[https://fosstodon.org/tags/OrgMode][#orgmode]] hashtag on Mastodon
- Check [[file:org-web-social.org][latest posts]] on reddit.com and stackoverflow.com
-- Browse [[https://www.reddit.com/r/orgmode/][Org-mode discussions on reddit.com]]
+- Browse [[https://www.reddit.com/r/orgmode/][Org-mode discussions on Reddit]]
- Browse [[https://stackoverflow.com/questions/tagged/org-mode][Org-mode questions on stackoverflow.com]]
- [[https://emacslife.com/calendar/][Emacs event calendar]] : Join virtual and in-person Emacs events
- [[file:orgmeetup.org][=[[bbb:OrgMeetup]]=]] : talk (online) with Org mode developers and users
diff --git a/org-contrib/index.org b/org-contrib/index.org
index 16b8c95d..89051927 100644
--- a/org-contrib/index.org
+++ b/org-contrib/index.org
@@ -430,7 +430,7 @@ See [[file:../exporters/index.org][Exporters]].
- [[https://github.com/eschulte/org-S5][org-S5]] from Eric Schulte
- [[https://github.com/relevance/org-html-slideshow.git][org-html-slideshow]] from Stuart Sierra
- - [[https://gist.github.com/509761][org-html5presentation.el]] from kinjo
+ - [[https://gist.github.com/takumikinjo/509761][org-html5presentation.el]] from kinjo
For use with the new exporter, /ox-s5.el/ ([[contribfile:lisp/ox-s5.el][link to raw file)]] and
/ox-deck.el/ ([[contribfile:lisp/ox-deck.el][link to raw file]]), both by Rick Frankel, are
diff --git a/org-hacks.org b/org-hacks.org
index 63b36d7e..62aa6094 100644
--- a/org-hacks.org
+++ b/org-hacks.org
@@ -67,7 +67,7 @@ Here is a hook function to use archive this effect:
*** Picking up a random task in the global TODO list
-Tony day [[http://mid.gmane.org/[email protected]][shared]] [[https://gist.github.com/4343164][this gist]] to pick up a
+Tony day [[http://mid.gmane.org/[email protected]][shared]] [[https://gist.github.com/tonyday567/4343164][this gist]] to pick up a
random task.
** Building and Managing Org
diff --git a/org-tests/index.org b/org-tests/index.org
index f1232ba2..ca6f2cf1 100644
--- a/org-tests/index.org
+++ b/org-tests/index.org
@@ -64,16 +64,17 @@ _ert.el_ and _ert-x.el_ to your testing directory. This may be
accomplished with the following commands entered on the command line.
: cd /path/to/org-mode/testing
-: curl -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert.el
-: curl -O https://github.com/mirrors/emacs/raw/master/lisp/emacs-lisp/ert-x.el
+: curl -O https://cgit.git.savannah.gnu.org/cgit/emacs.git/plain/lisp/emacs-lisp/ert.el
+: curl -O https://cgit.git.savannah.gnu.org/cgit/emacs.git/plain/lisp/emacs-lisp/ert-x.el
Alternatively you may download the files within your browser.
- - browse to [[https://github.com/mirrors/emacs/tree/master/lisp/emacs-lisp][github.com/mirrors/emacs/lisp/emacs-lisp]]
- - right click ert.el link and select _download linked file_ (or
- equivalent) and save to org-mode/testing/ert.el
- - right click ert-x.el link and select _download linked file_ (or
- equivalent) and save to org-mode/testing/ert-x.el
+ - browse to [[https://cgit.git.savannah.gnu.org/cgit/emacs.git/plain/lisp/emacs-lisp/ert.el][ert.el in Emacs' source code]]
+ - Download this file with Ctrl+S, or via Right-click on the page and
+ "Save Page As" (or equivalent) and save to
+ =org-mode/testing/ert.el=.
+ - Do the same for the [[https://cgit.git.savannah.gnu.org/cgit/emacs.git/plain/lisp/emacs-lisp/ert-x.el][ert-x.el]] file, save to
+ =org-mode/testing/ert-x.el=.
That's it - you may now run the tests.
diff --git a/org-tutorials/non-beamer-presentations.org b/org-tutorials/non-beamer-presentations.org
index 33ea005e..ed1e346c 100644
--- a/org-tutorials/non-beamer-presentations.org
+++ b/org-tutorials/non-beamer-presentations.org
@@ -34,8 +34,8 @@ documents.
HTML export serves as the base of the presentation.
- org-html5presentation :: Is an Exporter of Org-mode documents to
- HTML5 slide show presentations.
- (see [[https://gist.github.com/509761][gist.github.com/509761]] for code and usage information)
+ HTML5 slide show presentations. (see
+ [[https://gist.github.com/takumikinjo/509761][this gist by takumikinjo]] for code and usage information)
- [[org-tree-slide]] :: is a simple tool to treat a tree of an org buffer as
a single slide. Since each slide is displayed by simple narrowing,
diff --git a/publish.sh b/publish.sh
index b499de3d..6a439655 100755
--- a/publish.sh
+++ b/publish.sh
@@ -14,6 +14,8 @@ This variable can be set when running publish.sh script:
(require 'htmlize)
(require 'org-inlinetask)
+(load "worg-urls-rewrite")
+
(setq make-backup-files nil
debug-on-error t)
@@ -58,30 +60,33 @@ This variable can be set when running publish.sh script:
(R . t)
(gnuplot . t)))
-(dolist (org-file (cl-remove-if
- (lambda (n) (string-match-p "worg/archive/" n))
- (directory-files-recursively default-directory "\\.org$")))
- (let ((html-file (concat (file-name-directory org-file)
- (file-name-base org-file) ".html")))
- (if (and (file-exists-p html-file)
- (file-newer-than-file-p html-file org-file)
- ;; If there are include files or code, we need to
- ;; re-generate the HTML just in case if the included
- ;; files are changed.
- (with-temp-buffer
- (insert-file-contents org-file)
- (and
- (save-excursion
- (goto-char (point-min))
- (not (re-search-forward "#\\+include:" nil t)))
- (save-excursion
- (goto-char (point-min))
- (not (re-search-forward "#\\+begin_src" nil t))))))
- (message " [skipping] unchanged %s" org-file)
- (message "[exporting] %s" (file-relative-name org-file default-directory))
- (with-current-buffer (find-file org-file)
- (if worg-publish-stop-on-error
- (org-html-export-to-html)
- (condition-case err
- (org-html-export-to-html)
- (error (message (error-message-string err)))))))))
+(add-hook 'org-export-before-parsing-functions #'worg-urls-add-alternative-links)
+(let ((export-all (worg-urls-libredirect-data-changed-p worg-urls-libredirect-data)))
+ (dolist (org-file (cl-remove-if
+ (lambda (n) (string-match-p "worg/archive/" n))
+ (directory-files-recursively default-directory "\\.org$")))
+ (let ((html-file (concat (file-name-directory org-file)
+ (file-name-base org-file) ".html")))
+ (if (and (not export-all)
+ (file-exists-p html-file)
+ (file-newer-than-file-p html-file org-file)
+ ;; If there are include files or code, we need to
+ ;; re-generate the HTML just in case if the included
+ ;; files are changed.
+ (with-temp-buffer
+ (insert-file-contents org-file)
+ (and
+ (save-excursion
+ (goto-char (point-min))
+ (not (re-search-forward "#\\+include:" nil t)))
+ (save-excursion
+ (goto-char (point-min))
+ (not (re-search-forward "#\\+begin_src" nil t))))))
+ (message " [skipping] unchanged %s" org-file)
+ (message "[exporting] %s" (file-relative-name org-file default-directory))
+ (with-current-buffer (find-file org-file)
+ (if worg-publish-stop-on-error
+ (org-html-export-to-html)
+ (condition-case err
+ (org-html-export-to-html)
+ (error (message (error-message-string err))))))))))
diff --git a/rewrite-websites.js b/rewrite-websites.js
new file mode 100644
index 00000000..be25cfc1
--- /dev/null
+++ b/rewrite-websites.js
@@ -0,0 +1,24 @@
+const nonfree_mappings = new Map();
+nonfree_mappings.set(/https?:\/\/(?:www\.)?(?:youtube\.com|youtu\.be)\/watch\?v(?:=|%3D)([a-zA-Z0-9\-_]{11})(?:&.+)?/, 'https://inv.nadeko.net/watch?v=');
+
+
+function rewriteLinks() {
+ const links = document.querySelectorAll("a");
+ links.forEach(replaceIfMatch);
+}
+
+function replaceIfMatch(a) {
+ nonfree_mappings.forEach((to, from) => {
+ const match = a.href.match(from);
+ if (match) insertReplacement(a, match, to);
+ });
+}
+
+function insertReplacement(a, match, to) {
+ element = document.createElement("a");
+ element.href = to + match[1];
+ element.innerText = "(alternative without non-free JS)";
+ a.after(" ", element);
+}
+
+window.addEventListener("load", rewriteLinks);
diff --git a/worg-urls-rewrite.el b/worg-urls-rewrite.el
new file mode 100644
index 00000000..0a164074
--- /dev/null
+++ b/worg-urls-rewrite.el
@@ -0,0 +1,251 @@
+;;; worg-urls-rewrite.el --- Rewrite URLs that refer to pages containing non-free JS to free alternatives
+
+;;; Commentary:
+;;
+;; Worg links to many external pages, some of which link to websites
+;; that rely on non-free JavaScript, such as GitHub or YouTube pages.
+;; This script contains the function `worg-urls-add-alternative-links'
+;; which converts these links to free alternatives, such as GotHub and
+;; Invidious.
+;;
+;; Since these alternatives occasionally go down, we use the
+;; alternative specified by libredirect, which checks whether these
+;; alternatives are live or not. This is seen in
+;; `worg-urls-libredirect-url'.
+;;
+;; This was discussed on the mailing list here:
+;; https://list.orgmode.org/87pl9szmy6.fsf@localhost.
+;;
+;;; Code:
+
+(defvar worg-urls-libredirect-url "https://raw.githubusercontent.com/libredirect/instances/main/data.json"
+ "Location where up-to-date libredirect data is found.")
+(defvar worg-urls-libredirect-data nil
+ "Contents of libredirect data, obtained from `worg-urls-libredirect-url'.")
+
+(defcustom worg-urls-free-alternatives '((youtube . invidious)
+ (github . gothub))
+ "Alist of alternative frontends to websites containing non-free JS.
+The CDR corresponds to a symbol that is known by libredirect, see
+https://codeberg.org/LibRedirect/instances.")
+(defcustom worg-urls-redirect-save-filename "redirect-local-data.eld"
+ "Filename used to save the relevant output of libredirect's data.")
+
+
+(defun worg-urls-libredirect-data-changed-p (&optional data)
+ "Whether or not libredirect DATA changed since our last export."
+ (setq worg-urls-libredirect-data (or data (worg-urls--get-libredirect-data)))
+ (if (not (file-exists-p worg-urls-redirect-save-filename))
+ ;; We hadn't saved stuff from our previous export, do so now.
+ (progn
+ (worg-urls--save-redirect-data worg-urls-libredirect-data)
+ t)
+ ;; Compare results from stored file with data, overwrite stored data if newer
+ (let* ((stored-data (with-temp-buffer
+ (insert-file-contents worg-urls-redirect-save-filename)
+ (read (buffer-string))))
+ (data-equal (equal (worg-urls--relevant-redirect-data worg-urls-libredirect-data)
+ stored-data)))
+ (unless data-equal
+ (worg-urls--save-redirect-data worg-urls-libredirect-data))
+ (not data-equal))))
+
+
+(defun worg-urls--get-libredirect-data ()
+ "Return libredirect data from `worg-urls-libredirect-url'."
+ (condition-case _
+ (let ((response (with-current-buffer
+ (url-retrieve-synchronously worg-urls-libredirect-url nil nil 5)
+ (prog2
+ (re-search-forward "\n\n" nil t) ; skip HTTP headers
+ (buffer-substring-no-properties (point) (point-max))
+ (kill-buffer)))))
+ (json-parse-string response :object-type 'alist :array-type 'list))
+ (t
+ (message "Worg couldn't access libredirect data, using original URLs.")
+ nil)))
+
+
+(defun worg-urls--save-redirect-data (&optional data)
+ "Save redirect DATA in a file with filename `worg-urls-redirect-save-filename'."
+ (let ((tosave (or data worg-urls-libredirect-data)))
+ (with-temp-file worg-urls-redirect-save-filename
+ (prin1
+ (worg-urls--relevant-redirect-data data)
+ (current-buffer)))))
+
+
+(defun worg-urls--relevant-redirect-data (data)
+ "Extract relevant redirection DATA, defined in `worg-urls-free-alternatives'."
+ (seq-map (lambda (association)
+ (let ((from (car association)))
+ `(,from . ,(worg-urls--first-link-of-data data from))))
+ worg-urls-free-alternatives))
+
+(defun worg-urls--is-short-gist-path-p (link)
+ "Test whether LINK links to a gist without a user id.
+These redirect to a \"full\" gist.github.com/<user>/<id> link and at the
+moment have to be fixed manually - gothub does not support these short
+links."
+ (string-match (rx "//gist.github.com/" (1+ (in digit))) link))
+
+(defun worg-urls-add-alternative-links (&optional _)
+ "Add alternative links for websites containing non-free JS.
+For each link that has an alternative (currently YouTube and GitHub), we
+insert a link to the free alternative, and change the link text of the
+original link to to =(original URL)=. We also redirect reddit links to
+old.reddit.com.
+
+See https://list.orgmode.org/orgmode/87pl9szmy6.fsf@localhost/"
+ (unless worg-urls-libredirect-data
+ (setq worg-urls-libredirect-data (worg-urls--get-libredirect-data)))
+
+ (let ((links (org-element-map (org-element-parse-buffer) 'link #'identity)))
+ (dolist (link (nreverse links))
+ (when (string-prefix-p "http" (org-element-property :type link))
+ (let ((path (org-element-property :path link)))
+ (if-let* ((new-url (worg-urls--find-replacement path)))
+ (let* ((begin (org-element-property :begin link))
+ (contents-begin (org-element-property :contents-begin link))
+ (contents-end (org-element-property :contents-end link)))
+ (save-excursion
+ (if (and contents-begin contents-end)
+ ;; Link has description.
+ (let ((description (buffer-substring contents-begin contents-end)))
+ ;; If this description refers to the full
+ ;; domain, this description will be confusing,
+ ;; since we change the domain here. Warn
+ ;; about this.
+ (when (string-match
+ (rx (or "reddit.com"
+ "github.com"
+ "youtube.com"))
+ description)
+ (warn
+ (concat "Link description \"%s\" in %s:%s refers"
+ " to the full domain \"%s\", which we change."
+ " This is confusing, please change the description.")
+ description (buffer-file-name)
+ (line-number-at-pos (org-element-property :contents-begin link))
+ (match-string 0 description)))
+
+ ;; Change description to (original URL)
+ (goto-char contents-begin)
+ (delete-region contents-begin contents-end)
+ (insert "(original URL)")
+ ;; Insert new link with free alternative
+ (goto-char begin)
+ (insert (format "[[%s][%s]] " new-url description)))
+ ;; Else, "bare" link without description. We insert
+ ;; the original link as bare, and add the old one
+ ;; with (original URL) as description.
+ (let* ((end (org-element-property :end link))
+ (original (buffer-substring begin end))
+ ;; the URL might be in [[https://...]]
+ ;; format or "just" in https://... format.
+ (url (if (string-match (rx string-start
+ "[["
+ (group (seq "https://" (* anything)))
+ "]]"
+ string-end)
+ original)
+ (match-string 1 original)
+ original))
+ (replacement (format "[[%s]] [[%s][(original URL)]]" new-url url)))
+ (delete-region begin end)
+ (goto-char begin)
+ (insert replacement)))))
+ ;; No replacement found. Check if the link is a short
+ ;; github gist, this can be fixed manually.
+ (when (worg-urls--is-short-gist-path-p path)
+ (error
+ (concat "Please replace URL %s in %s:%s with its redirect target,"
+ " we cannot convert this short Gist URL to a free alternative.")
+ path
+ (buffer-file-name)
+ (line-number-at-pos (org-element-property :begin link))))))))))
+
+
+(defun worg-urls--find-replacement (path)
+ "Take PATH and return an alternative link if known and nil otherwise."
+ (and worg-urls-libredirect-data
+ (pcase path
+ ;; See https://docs.invidious.io/redirector/
+ ;; This matches both
+ ;; - youtube.com/watch?v=<video-id>
+ ;; - youtube.com/playlist?list=<playlist-id>
+ ;; with optionally ? and = escaped
+ ((rx "//" (? "www\.")
+ "youtube.com"
+ (group
+ (or (and "/watch"
+ (or "?" "%3F")
+ "v"
+ (or "=" "%3D"))
+ (and "/playlist"
+ (or "?" "%3F")
+ "list"
+ (or "=" "%3D"))))
+ (group (+ not-newline)))
+ (let* ((route (match-string 1 path))
+ (id (match-string 2 path))
+ (host (worg-urls--first-link-of-data worg-urls-libredirect-data 'youtube)))
+ (concat host route id)))
+ ;; youtu.be/<video-id>
+ ((rx "//" (? "www\.")
+ "youtu.be"
+ "/"
+ (group (+ not-newline)))
+ (let* ((video-id (match-string 1 path))
+ (host (worg-urls--first-link-of-data worg-urls-libredirect-data 'youtube)))
+ (concat host "/watch?v=" video-id)))
+ ;; gothub supports all links currently used in Worg, except for
+ ;; /<user>/<repo>/<issues>, so we exclude those.
+ ((and
+ (rx "//" (? "www\.")
+ "github.com"
+ "/"
+ (group (+ not-newline)))
+ ;; Exclude issues path. This approach currently also would
+ ;; erroneously exclude a supported link if author or repo
+ ;; starts with "issues"
+ (guard (not (string-match-p "/issues" path))))
+ (let* ((url (match-string 1 path))
+ (host (worg-urls--first-link-of-data worg-urls-libredirect-data 'github)))
+ (concat host "/" url)))
+ ;; gist.github.com links can have the format
+ ;; gist.github.com/<user>/<id>, but also obsolete
+ ;; gist.github.com/<id>. These last links are automatically
+ ;; redirected on github.com, but we do not know where to
+ ;; redirect them to at this moment, so we cannot convert
+ ;; these. Therefore we currently only convert
+ ;; gist.github.com/<user>/<id> links.
+ ((rx "//" "gist.github.com/"
+ ;; user
+ (group (1+ (not (any "/$"))))
+ "/"
+ ;; id
+ (group (+ not-newline)))
+ (let* ((user (match-string 1 path))
+ (id (match-string 2 path))
+ (host (worg-urls--first-link-of-data worg-urls-libredirect-data 'github)))
+ (concat host "/gist/" user "/" id)))
+ ;; redirect reddit.com to old.reddit.com
+ ((rx "//" (? "www\.")
+ "reddit.com"
+ "/"
+ (group (+ not-newline)))
+ (let* ((url (match-string 1 path))
+ (host "https://old.reddit.com"))
+ (concat host "/" url))))))
+
+(defun worg-urls--first-link-of-data (data host)
+ "Get the first available link in DATA of the alternative links for HOST."
+ (let* ((alternative (cdr (assoc host worg-urls-free-alternatives)))
+ (object (cdr (assoc alternative data)))
+ (links (cdr (assoc 'clearnet object))))
+ (car links)))
+
+(provide 'worg-urls-rewrite)
+
+;;; worg-urls-rewrite.el ends here
--
2.51.2