:crowdanki_uuid如何让跨设备的牌组合并不再丢卡片)
CrowdAnki源码剖析二crowdanki_uuid如何让跨设备的牌组合并不再丢卡片【免费下载链接】CrowdAnkiPlugin for Anki SRS designed to facilitate cooperation on creation of notes and decks.项目地址: https://gitcode.com/gh_mirrors/cr/CrowdAnkiCrowdAnki 是一款基于 Anki SRS 的协作插件支持将牌组Deck以 JSON 格式导出、导入并配合 Git 实现多人协同编辑。而在它的 JSON 文件里藏着一个决定协作成败的字段——crowdanki_uuid。正是这个UUID 身份证让两台设备各自修改过的牌组合并时卡片不再丢失、不再重复。本文从源码角度剖析 CrowdAnki 的 UUID 机制它是什么、如何自动生成、导入时如何靠它匹配实体以及插件如何处理 UUID 重复这一经典坑点。先搞懂痛点为什么跨设备合并牌组容易丢卡片Anki 内部为每个实体牌组、笔记模型、牌组配置都分配了一个数字id但问题是这个 id 是每台设备本地数据库各自生成的。 你在电脑 A 导出牌组伙伴在电脑 B 克隆同名牌组两边拿到的id完全不同。如果合并时只按名称或本地 id去匹配就会发生同一张卡片被当成两张新卡片重复创建本地修改被远端数据直接覆盖同名牌组冲突卡片消失在某一边CrowdAnki 的解法非常直接引入一个全局唯一、跨设备不变的标识符——crowdanki_uuid。上图是 GitHub Desktop 中一个真实牌组仓库的历史记录展开DeckX.json可以清楚看到crowdanki_uuid字段——它就是整个牌组的身份证号码。crowdanki_uuid 从哪来字段定义与自动生成整个机制的入口是一个常量定义见 constants.pyUUID_FIELD_NAME crowdanki_uuid而 UUID 的自动生成集中在基类 json_serializable.py 的_update_fields()方法中def _update_fields(self): self.anki_dict.setdefault(UUID_FIELD_NAME, str(uuid1()))这里的setdefault是关键设计只有在字段不存在时才生成新的uuid1()。也就是说第一次导出时牌组/模型/配置各获得一个全新 UUID之后每次导入再导出UUID 原样保留——身份从此锁死与此同时导出时的过滤集合export_filter_set会主动剔除id、mod、usn这些本地相关字段见 json_serializable.py。本地 id 不进 JSON跨设备匹配的噪音源被彻底清除了剩下的只有稳定的crowdanki_uuid。合并的核心UuidFetcher 按 UUID 精准定位实体导入时插件要回答一个问题JSON 里的这个牌组/模型在我本地已经存在了吗这个工作由 UuidFetcher 类完成它为每类实体提供按 UUID 查找的方法方法用途get_deck(uuid)按 UUID 查找本地牌组get_model(uuid)按 UUID 查找笔记模型get_deck_config(uuid)按 UUID 查找牌组配置get_note(uuid)按 guid 在 notes 表中查 SQL 定位笔记牌组找到就合并找不到就新建看 _save_deck 的逻辑先用UuidFetcher(collection).get_deck(self.get_uuid())按 UUID 查本地牌组若本地不存在该 UUID → 新建卡片直接挂上去若存在 →deck_dict.update(self.anki_dict)增量更新本地牌组历史进度与卡片全部保留 这就是不丢卡片的核心合并的依据不再是易变的名称而是永远一致的 UUID。同名不同身自动重命名防冲突还有一个容易被忽略的细节在 _rename_deck如果本地存在同名但 id 不同的牌组比如两人各自独立新建过同名牌组插件不会贸然合并而是自动把新牌组重命名为名称_2、名称_3……保证两边卡片都安全落地宁可多一个牌组也不丢一张卡片。笔记按 guid 更新而非重复添加笔记侧的逻辑在 note.py 的save_to_collection中先用UuidFetcher按 guid 查本地笔记查到了就update_note更新内容查不到才add_note新建。同一张卡片在两端各改了一版合并后只会留下一张内容以导入方为准、牌组归属自动迁移。上图展示将含 UUID 的牌组 JSON 发布到远程仓库的过程——伙伴克隆仓库并导入后插件正是靠这些 UUID 把两人的改动合而为一。防身份撞车两类 UUID 重复问题的源码级修复UUID 机制有一个天然风险如果两个实体意外拿到同一个 UUID合并时就会互相顶掉。CrowdAnki 源码里有两处针对性修复很值得学习。修复一旧版本克隆模型继承 UUID 的历史包袱见 disambiguate_uuids.pyCrowdAnki ≤ 0.9 时代克隆笔记模型会继承原模型的 crowdanki_uuid造成重复。该函数在启动时扫描所有带crowdanki_uuid的模型对重复者生成全新uuid1()替换并弹窗告知用户。细节上有两个巧妙之处按模型id排序再处理——因为 Anki 的模型 id 接近创建时间戳副本的 id 通常更大所以改掉的几乎总是副本的 UUID而非原件的每个替换都会print日志防止循环中途崩溃时用户无从排查修复二新增牌组配置时主动去身份deckconf.py 中的disambiguate_crowdanki_uuid监听 Anki 的deck_conf_did_add_config钩子注册于 hook_vendor.py当用户克隆一份牌组配置时直接删掉新配置里继承来的crowdanki_uuid让它下次导出时重新获得独立身份——从源头杜绝重复。小结一张身份证换来的协作安全感回顾整套设计CrowdAnki 用极小的代码量解决了协作系统里最棘手的一致性问题生成setdefaultuuid1()一次生成、终身不变json_serializable.py过滤导出时剔除本地id消除设备差异json_serializable.py匹配UuidFetcher按 UUID/guid 精准定位更新优先于新建uuid.py兜底同名不同身自动重命名、UUID 重复自动消歧deck.py如果你也想让自己的工具具备多人协同不丢数据的能力这套稳定 UUID 按身份合并 冲突自动改名的组合拳是一个非常值得抄作业的范本。 完整项目结构可参考 crowd_anki/ 目录导出入口在 export/导入逻辑在 importer/配置项说明见 README.md。【免费下载链接】CrowdAnkiPlugin for Anki SRS designed to facilitate cooperation on creation of notes and decks.项目地址: https://gitcode.com/gh_mirrors/cr/CrowdAnki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考