关于用org文件管理Firefox打开的标签

火狐用的时间长了,经常会有很多打开的标签页,数量一多,火狐就容易出问题。似乎把每天打开的标签页搜集到一个org文件里面是个不错的主意。在网上搜索一下,可以发现类似以下代码

(defun save-firefox-session ()
  "Reads firefox current session and generate org-mode heading with items."
  (interactive)
  (save-excursion
    (let* ((path "~/.mozilla/firefox/bllflh9q.default/sessionstore-backups/recovery.jsonlz4")
           (cmd (concat "lz4jsoncat " path " | jq '.windows[].tabs[] | .entries[-1] | .url, .title'"))
           (ret (shell-command-to-string cmd)))
      (insert
       (concat
        "* "
        (format-time-string "[%Y-%m-%d %H:%M:%S]")
        "\n"
        (mapconcat 'identity
                   (cl-reduce (lambda (lst x)
                                (if (and x (not (string= "" x)))
                                    (cons (concat "  - " x) lst)
                                  lst))
                              (split-string ret "\n")
                              :initial-value (list))
                   "\n"))))))

这样做的结果是,生成的org文件是一行标签页的标题一行对应的网址。应该怎样去修改这段代码使之只生成标签页标题,且这些标题是org链接,指向相应的网址呢?感觉不是很难,但又不知怎么写比较好。

我改了一下。

(defun save-firefox-session ()
  "Reads firefox current session and generate org-mode heading with items."
  (interactive)
  (save-excursion
    (let* ((path "~/.mozilla/firefox/i49ukbsa.default-release/sessionstore-backups/recovery.jsonlz4")
           (cmd (concat "lz4jsoncat " path " | jq '.windows[].tabs[] | .entries[-1] | .url, .title'"))
           (ret (shell-command-to-string cmd)))
      (insert
       (concat "* " (format-time-string "[%Y-%m-%d %H:%M:%S]") "\n"))
      (-map (lambda (x)
              (let ((link (s-chop-suffix "\"" (s-chop-prefix "\"" (car x))))
                    (name (s-chop-suffix "\"" (s-chop-prefix "\"" (cadr x)))))
                (insert (concat "** [[" link "]"
                                (when name
                                  (concat "[" name "]")) "]\n"))))
            (-partition 2 (split-string ret "\n"))))))

出来的大概是这样的:

* [2020-11-07 23:23:42]
** [[about:newtab][新标签页]]
** [[https://github.com/andikleen/lz4json][andikleen/lz4json: C decompress tool for mozilla lz4json format]]
** [[https://github.com/magnars/s.el][magnars/s.el: The long lost Emacs string manipulation library.]]

另:感觉这个不错啊,求原始出处

1 个赞

从输出获得标题和链接,然后格式化成 Org 链接。

把 jq 命令改一下,直接输出 JSON,然后用 Emacs 的 JSON Parser:

$ lz4jsoncat xx.jsonlz4" | \
  jq '[.windows[].tabs[] | .entries[-1] | {url, title} ]'
[
  {
    "url": "http://example.com/",
    "title": "Example Domain"
  },
  {
    "url": "https://www.google.com/",
    "title": "Google"
  },
  {
    "url": "https://github.com/",
    "title": "GitHub: Where the world builds software · GitHub"
  }
]

用 org-link-make-string 生成 Org 链接,比 format 可靠。

https://acidwords.com/posts/2019-12-04-handle-chromium-and-firefox-sessions-with-org-mode.html

按照 @xuchunyang 的建议重新写了一下 (多谢思路) :

(defun save-firefox-session ()
  "Reads firefox current session and generate org-mode heading with items."
  (interactive)
  (save-excursion
    (let* ((path "~/.mozilla/firefox/i49ukbsa.default-release/sessionstore-backups/recovery.jsonlz4")
           (cmd (concat "lz4jsoncat " path " | jq '[.windows[].tabs[] | .entries[-1] | {url, title} ]'"))
           (ret (shell-command-to-string cmd)))
      (insert
       (concat "* " (format-time-string "[%Y-%m-%d %H:%M:%S]") "\n"))
      (-map (lambda (x)
              (let* ((url (gethash "url" x))
                     (title (gethash "title" x ""))
                     (ss (org-link-make-string url title)))
                (insert "  - " ss "\n")))
            (json-parse-string ret :array-type 'list)))))

我会用这个方式来查找 firefox 之前打开的标签

url=$(sqlite3 --ascii $HOME/.mozilla/firefox/xxxxxxxxxx.default-release/places.sqlite 'select distinct p.title, " ", p.url, char(10) from moz_historyvisits h inner join moz_places p on h.place_id = p.id where p.title != " " order by h.visit_date desc;' | fzf | awk '{print $NF}') && [ -n "$url" ] && firefox "$url"

这段代码的来源就是dingyi342提到的那个链接,但是在那里作者直接用了grep去搜索所有的网址,这样得到的结果其实并非所有打开的标签页的网址。实际上recovery.jsonlz4这个文件包含的内容还是挺复杂的。但是在

这个帖子中,有人提到用jq可以提取出所有标签页的信息,结果就是主楼中提到的例子。因为我也不懂json的格式是什么,虽然我也觉得保持json格式的输出可能比较好,但是不知道应该怎么用jq,谢谢xuchunyang的帮助。

我试了一下,这种方法似乎无法在火狐已经打开的情况下使用,会提示数据库上锁。另外,这条命令的输出其实也不是所有打开的标签页,还包括那些曾经打开但是已经关闭的标签页。

这个就是查询历史的啊,我不是写了之前吗(

至于切换缓冲区们,我用的 rofi(

(论坛啥时候能支持 mp4, 我录制的这个视频才 1.4 兆,转成 gif 后成功 11.8 兆,并且画质下降

1 个赞

我使用的时候略微改动了一下@yangsheng6810的代码,似乎把标签页条目用二级标题表示会更方便一些。在此基础上我写了个简单地恢复某天所有标签页的函数。

(defun restore-firefox-session ()
  "Restore all the tabs of a firefox session."
  (interactive)
  (save-excursion
    (org-back-to-heading 'invisible-ok)
    (while (= 2 (- (match-end 0) (match-beginning 0) 1))
      (org-backward-heading-same-level 1 'invisible-ok))
    (org-next-link)
    (org-open-at-point)
    (while (and (re-search-forward org-outline-regexp-bol nil 'move)
                (>= 2 (- (match-end 0) (match-beginning 0) 1)))
      (beginning-of-line)
      (when (= 2 (- (match-end 0) (match-beginning 0) 1))
        (org-next-link)
        (org-open-at-point)
        )
      )
    )
  )

如果一次性打开的页面比较多的话,也许应该在每次打开页面间插入适当的时间间隔,这样可以减轻火狐的负担。但是如果这样做的话,似乎应该把上述函数改造成异步的才不至于卡住emacs。