emacs-org-mode中同步bangumi的收看进度
(部分)同步Bangumi的观看进度。
完全同步内容:某一条目的观看情况(在看、看过、抛弃)。
部分同步内容:某一条目章节的观看情况,只能增不能减。
1. 基础配置
需要安装 plz
包以处理HTTP请求。
后面的具体配置细则请直接复制粘贴即可
1.1. bangumi与org-mode
根据org-mode统计进度更新bangumi进度的小函数
;; 在这里 https://next.bgm.tv/demo/access-token 申请token,然后取消下一行的注释并填入你的TOKEN
;; (setq my/bgm-token "YOUR-BANGUMI-ACCESS-TOKEN")
(defun my/bgm-mark-read-episodes (subject readcount)
"更新某subject的观看进度"
(when (> readcount 0)
;; 这里调用下面的函数处理得到需要标记为已读的章节编号
(let ((unread (my/bgm-get-subject-marked-unread-episodes subject readcount)))
;;没有匹配到的未读章节时跳过
(when unread
(plz 'patch (concat "https://api.bgm.tv/v0/users/-/collections/" subject "/episodes")
:headers `(("User-Agent" . "tomoemami/emacs-bgm")
("Authorization" . ,(concat "Bearer " my/bgm-token))
("Accept" . "*/*")
("Content-Type" . "application/json"))
:body (json-encode `(:episode_id ,unread :type 2))
:then (lambda (r) (message "%s" r)))
(message "已更新BGM观看进度")))))
(defun my/bgm-get-subject-marked-unread-episodes (subject readcount)
"""
返回已勾选checkbox集数却未在BGM里标为已看的章节编号。传入str主题编号与number观看进度。
"
;; 获取该subject的全部章节
(let ((episodes (plz 'get (concat "https://api.bgm.tv/v0/users/-/collections/" subject "/episodes?offset=0&limit=100")
:headers `(("User-Agent" . "tomoemami/emacs-bgm")
("Authorization" . ,(concat "Bearer " my/bgm-token))
("Accept" . "application/json"))
:as #'json-read))
;; 处理传入的观看进度
(readed (number-sequence 1 readcount))
(result '()))
;; 获取全部章节数据
(dolist (epi (seq-into (alist-get 'data episodes) 'list))
;; 当 某一章节未标为已读(2代表已读) 且 序号在目前标记的观看进度内时
(when (and (< (alist-get 'type epi) 1) (memq (alist-get 'ep (alist-get 'episode epi)) readed))
;; 收集汇总Bangumi上未读章节的编号
(push (alist-get 'id (alist-get 'episode epi)) result)))
result))
(defun my/bgm-update-episodes()
"更新章节,放在checkbox变化的hook里"
(interactive)
;; 仅在有BGM property和有TODO-keywords的时候触发
(when (and (org-entry-get nil "BGM") (nth 2 (org-heading-components)))
(let* ((heading (nth 4 (org-heading-components)))
(readed (when (string-match "\\[\\([0-9]+\\)/" heading) (string-to-number (match-string 1 heading))))
(subject (org-entry-get nil "BGM")))
(my/bgm-mark-read-episodes subject readed))))
(defun my/bgm-update-subject()
"""
同步更新BGM的观看情况。目前是TODO-在看;DONE-看过;XXXX-抛弃;HOLD-想看;没有TODO关键字-搁置。
相关数字与BGM状态的对应:
1 想看,2 看过,3 在看,4 搁置,5 抛弃
目前仅会在以下情况触发:
有'BGM'属性,证明需要调用bangumi的api 'and'
'not' 有'repeater'且标记为'DONE'/'TODO',证明是类似追番的场景
"""
(interactive)
(let* ((subject (org-entry-get nil "BGM"))
(todo (nth 2 (org-heading-components)))
(status (cond ((string-equal todo "TODO") '(3 . "在看"))
((string-equal todo "HOLD") '(1 . "想看"))
((string-equal todo "DONE") '(2 . "看过"))
((string-equal todo "XXXX") '(5 . "抛弃"))
(t '(4 . "搁置")))))
(when (and subject
(not (and (org-get-repeat) (or (string-equal todo "DONE") (string-equal todo "TODO")))))
(plz 'post (concat "https://api.bgm.tv/v0/users/-/collections/" subject)
:headers `(("User-Agent" . "tomoemami/emacs-bgm")
("Authorization" . ,(concat "Bearer " my/bgm-token))
("Content-Type" . "application/json")
("Accept" . "*/*"))
:body (json-encode `(("type" . ,(car status))))
:then (lambda (r) (message "%s" r)))
(message (concat "已更新BGM观看状态为:" (cdr status))))))
;; 在TODO关键字变化时触发更新subject
(add-hook 'org-after-todo-state-change-hook 'my/bgm-update-subject)
;; 在checkbox统计数字变化时触发更新episode
(add-hook 'org-checkbox-statistics-hook 'my/bgm-update-episodes)
2. 使用方法
首先是创建一个不含TODO关键字的heading,然后按 C-c C-x p
为其输入 BGM
的属性,具体值为Bangumi网站上的对应值(如该条目的页面网址为 https://bgm.tv/subject/454684 ,则应输入 454684。
\*** AveMujica
:PROPERTIES:
:BGM: 454684
:END:
然后变更其TODO状态,如果变成TODO,则Bangumi网站上会同步变为在看;变到DONE,则同步变为看过;变到XXXX,则同步变为抛弃。
\*** TODO AveMujica
:PROPERTIES:
:BGM: 454684
:END:
最后,为其添加checkbox以及checkbox的统计。
\*** AveMujica[0/12]
:PROPERTIES:
:BGM: 454684
:END:
1. [ ]
2. [ ]
3. [ ]
4. [ ]
5. [ ]
6. [ ]
7. [ ]
8. [ ]
9. [ ]
10. [ ]
11. [ ]
12. [ ]
当你调整一个checkbox之后,进度统计变成 [N/12]
,则会将Bangumi网站上前 N
章的观看进度设置为已看。
3. 注意事项
注意,这里我偷了一个懒,观看进度是根据 [0/12]
的前面数字判断的。如果你跳跃式观看,比如只看了第2、3、4集,checkbox统计数据会变成 [3/12]
,则以上代码依然会自动标记前3集为已读。
所以说是(部分)同步,实际上只有TODO关键词可以完全同步,观看进度只能增不能减(心虚)。