解决notion不支持URL scheme 链接的问题

背景

  • 写文档的时候,有时候想创建一个链接,指向obsidian文档(obsidian://),logseq文档(logseq://)或者任意的本地的文件(file:///)。
  • 某些软件支持这样的URL scheme 链接,但是有些软件只支持HTTP链接,不支持URL scheme,比如notion。所以一个变通的办法是把URL scheme链接转为HTTP链接,可以看看Using URL Scheme Links in Notion | Kevin Jalbert
  • 上面这篇blog里的一个问题是它依赖 tinyurl.com,而且生成链接的过程需要手动操作,比较麻烦。我做了两个改进
    • 首先,去除对tinyurl.com的依赖,用一个本地HTTP server来替代它。
    • 其次,更快速的根据URL scheme链接生成HTTP链接。

原理

  • 我有一个URL scheme链接,比如
    • logseq://graph/logseq?page=XXX
  • 第一步,在把它粘贴到notion的时候,我会把它转换为一个HTTP链接
    • http://127.0.0.1:10114/v1?l=logseq%3A%2F%2Fgraph%2Flogseq%3Fpage%3DXXX
  • 第二步,我用go写了一个非常简单的Local HTTP Server,监听10114端口。它收到请求后,会把上面的链接重定向到
    • logseq://graph/logseq?page=XXX
  • 这样在notion里点击如上的链接,就会先跳到浏览器,然后跳转到logseq打开对应的文档。

实现说明

第一步,用快捷键把URL scheme链接粘贴为HTTP链接

  • 实现效果:

    • 我先从logseq里复制出文档的URL scheme链接。
    • 进入notion,按快捷键alt+shift+0,自动粘贴出转换好的HTTP链接。
  • 这一步我用AutoHotKey实现,代码如下

    ; 读取剪贴板内容,用url encode编码,生成一个本地http链接,并写回剪贴板,然后粘贴。
    ; 用来解决notion不支持url scheme的问题
    
    ; https://www.autohotkey.com/boards/viewtopic.php?t=86419
    EncodeDecodeURI(str, encode := true, component := true) {
       static Doc, JS
       if !Doc {
          Doc := ComObjCreate("htmlfile")
          Doc.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
          JS := Doc.parentWindow
          ( Doc.documentMode < 9 && JS.execScript() )
       }
       Return JS[ (encode ? "en" : "de") . "codeURI" . (component ? "Component" : "") ](str)
    }
    
    +!0::
    link := clipboard
    ; 这个url会交给一个local http server 来解析,转换回link
    encodedLink := "http://127.0.0.1:10114/v1?l=" EncodeDecodeURI(link)
    clipboard := encodedLink
    Send, ^v
    return
    

第二步,解析HTTP链接并重定向回URL scheme链接

  • 这一步,用Go写了一个非常简单的HTTP server,代码如下:
  • package main
    
    import (
        "fmt"
        "log"
        "net/http"
    )
    
    func myHandler(w http.ResponseWriter, r *http.Request) {
        l := r.URL.Query().Get("l")
        if l != "" {
            http.Redirect(w, r, l, http.StatusFound)
        } else {
            fmt.Fprintf(w, "need l query parameter as http://127.0.0.1:10114/v1?l=XXX\n")
        }
    }
    
    func main() {
        http.HandleFunc("/v1", myHandler)
        log.Fatal(http.ListenAndServe("127.0.0.1:10114", nil))
    }
    
  • Windows用下面的命令编译,可以启动为后台进程,不显示命令行窗口:
    • go build -ldflags -H=windowsgui
  • 把生成好的exe文件放到开机自启动即可。

Last modified on 2022-09-18