(部分)同步Bangumi观看进度

[2025-02-16 日 01:00]

(部分)同步Bangumi的观看进度。

完全同步内容:某一条目的观看情况(在看、看过、抛弃)。

部分同步内容:某一条目章节的观看情况,只能增不能减。

基础配置

需要安装 plz 包以处理HTTP请求。注意, plz 包尚未支持HTTP1.1中的 patch 方式,故需要手动修改 plz.el 中的源代码,把

(or 'put 'post)

改为

(or 'put 'post 'patch)

后面的具体配置细则请直接复制粘贴即可

(use-package plz)
;; 在这里 https://next.bgm.tv/demo/access-token 申请token
(setq my/bgm-token "YOUR-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)))))))

(defun my/bgm-get-subject-marked-unread-episodes (subject readcount)
  """
  返回已勾选checkbox集数却未在BGM里标为已看的章节编号。传入str主题编号与number观看进度。
  "
  ;; 获取该条目的全部章节
  (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-抛弃。
相关数字与BGM状态的对应:
1 想看,2 看过,3 在看,4 搁置,5 抛弃
  """
  (interactive)
  ;; 仅在有BGM property和有TODO-keywords的时候触发
  (when (and (org-entry-get nil "BGM") (nth 2 (org-heading-components)))
    (let* ((subject (org-entry-get nil "BGM"))
           (todo (nth 2 (org-heading-components)))
           (status (cond ((string-equal todo "TODO") 3)
                         ((string-equal todo "DONE") 2)
                         ((string-equal todo "XXXX") 5))))
      (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" . ,status))))
      )))
;; 在TODO关键字变化时触发更新条目
(add-hook 'org-after-todo-state-change-hook 'my/bgm-update-subject)
;; 在checkbox统计数字变化时触发更新章节
(add-hook 'org-checkbox-statistics-hook 'my/bgm-update-episodes)

使用方法

首先是创建一个不含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章的观看进度设置为已看。

注意事项

注意,这里我偷了一个懒,观看进度是根据 [0/12] 的前面数字判断的。如果你跳跃式观看,比如只看了第2、3、4集,checkbox统计数据会变成 [3/12] ,则以上代码依然会自动标记前3集为已读。

所以说是(部分)同步,实际上只有TODO关键词可以完全同步,观看进度只能增不能减(心虚)。

2 个赞