Obsidian 迁移到思源的踩坑之旅

本贴最后更新于 420 天前,其中的信息可能已经沧海桑田

上次用思源还是上次,大概在做终身会员活动的时候有过一个月体验,因为当时内核崩溃次数比较多,也有点卡(尤其是图谱)所以印象中思源的体验并不好。

这段时间跟别人推荐 Obsidian 和 Anytype 时被反向推荐了,于是再次安装试试看。


三步走(每一步都要做好备份)

  1. 正则替换图片链接
  2. 将创建时间替换为思源文件的 UID(这一步很慢
  3. 替换块 ID(可选,好像有点问题,但这一步挺快的,可以试试)

简单体验后认为思源的进步确实蛮大的,丝滑,稳定,决定从 Obsidian 换到思源。

当然,换笔记软件的第一步是尝试把整个 Ob 库迁移过去。

这里有几个点要注意:

  1. 虽然思源支持 Markdown 文件夹导入,但是思源对于链接的格式有一定要求。这个地方我试了好久,才在论坛上看见答案。

    1. 附件的格式需要是 md 链接 ![]()
    2. 内链的格式需要是 wiki 链接 [[]]
  2. 导入到思源的文件,创建日期根据文件的 UID 来判断,但 UID 是根据在思源中创建文件的时间来生成。

  3. 无序列表里的内链不能被转换


下面是解决前两步的方法

尝试前,建议先备份 OB 库文件,新开一个思源工作空间。
仅仅抛砖引玉,如果各位大佬有更好的办法,那你们就用更好的办法!


第一步 将附件的链接改为 md 链接

进行全局搜索替换,该方案来自 Obsidian 论坛,我增加了一些格式。

可以使用 vscode 等软件打开 ob 库文件夹(最好备份一下)使用 linter 插件应该也行,我没试。

搜索:

!(\[\]\(<*)(.*?)([.])(png|jpg|jpeg|bmp|gif|tiff|canvas|svg|webp|pdf|epub|gif|excalidraw|excalidraw.md|mp4|JPG|PNG|anyotherimagetype)(.*?)(?:\|.*?)*(?:#.*?)*\)

替换:

![](/$2$3$4)

第二步 更改思源中文件的 UID,以达到思源创建日期与 ob 定义的创建日期相同

效果:
image

步骤:

  1. 在 Obsidian 中使用 linter 等插件在 metadata 中添加 created,我的格式是 YYYY/MM/DD HH:mm:ss Z

    image

  2. 在思源中导入 markdown 文件夹,复制文件夹路径,然后关闭思源(最好关闭)

  3. 复制 python 代码(引导 GPT 生成的),将倒数第二行的文件夹路径替换为导入笔记的路径,然后运行。

    import os
    import re
    from datetime import datetime
    
    def extract_original_date(filename):
        match = re.match(r'^(\d{14})-', filename)
        return match.group(1) if match else None
    
    def extract_replace_date(content):
        match = re.search(r'created: (\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) \+\d{2}:\d{2}', content)
        if match:
            created_date = match.group(1)
            dt_obj = datetime.strptime(created_date, '%Y/%m/%d %H:%M:%S')
            return dt_obj.strftime('%Y%m%d%H%M%S')
    
    def generate_new_uid(original_uid, replace_date):
        return original_uid.replace(extract_original_date(original_uid), replace_date, 1)
    
    def process_folder(folder_path):
        for root, _, files in os.walk(folder_path):
            for filename in files:
                if filename.endswith('.sy'):
                    file_path = os.path.join(root, filename)
                    with open(file_path, 'rb') as f:
                        try:
                            content = f.read().decode('utf-8')
                        except UnicodeDecodeError:
                            print(f"Error decoding file: {file_path}")
                            continue
    
                    replace_date = extract_replace_date(content)
                    if replace_date:
                        original_uid = os.path.splitext(filename)[0]
                        new_uid = generate_new_uid(original_uid, replace_date)
                        new_filename = new_uid + '.sy'
                        new_content = content.replace(original_uid, new_uid)
                        new_file_path = os.path.join(root, new_filename)
                        os.rename(file_path, new_file_path)
                        with open(new_file_path, 'w', encoding='utf-8') as f:
                            f.write(new_content)
    
                        # Replace original UID with new UID in other files
                        for root2, _, files2 in os.walk(folder_path):
                            for filename2 in files2:
                                if filename2.endswith('.sy'):
                                    file_path2 = os.path.join(root2, filename2)
                                    with open(file_path2, 'rb') as f2:
                                        try:
                                            content2 = f2.read().decode('utf-8')
                                        except UnicodeDecodeError:
                                            print(f"Error decoding file: {file_path2}")
                                            continue
    
                                    new_content2 = content2.replace(original_uid, new_uid)
                                    with open(file_path2, 'w', encoding='utf-8') as f2:
                                        f2.write(new_content2)
    
    # 调用示例
    folder_path = r'D:\笔记\SiyuanObsidian导入\data\20230816005055-be9q8kj\20230816025046-f1kqm5h'
    print(f"Processing folder: {folder_path}")
    process_folder(folder_path)
    print("Processing complete.")
    

等待转换完成,这个代码比较慢,它的原理是用 created 中的日期替换旧 UID 的日期形成新的 UID,然后全局搜索旧 UID 替换为新 UID。耐心等等。

‍替换块 ID

import os
import re

def collect_new_uids(folder_path):
    new_uids = []
    for root, _, files in os.walk(folder_path):
        for filename in files:
            if filename.endswith('.sy'):
                new_uid = os.path.splitext(filename)[0]
                new_uids.append(new_uid)
    return new_uids

def process_folder_with_global_new_uids(folder_path, new_uids):
    for root, _, files in os.walk(folder_path):
        for filename in files:
            if filename.endswith('.sy'):
                file_path = os.path.join(root, filename)
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
              
                file_new_uid = os.path.splitext(filename)[0]
                block_ids = re.findall(r'\d{14}-[a-zA-Z0-9]{7}', content)
              
                for block_id in block_ids:
                    if block_id not in new_uids and "Data\":\"assets/" not in content:
                        new_block_id = file_new_uid[:12] + block_id[12:]
                        content = content.replace(block_id, new_block_id)
              
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(content)

# 调用示例
folder_path = r'D:\笔记\SiyuanObsidian导入\data\20230816005055-be9q8kj\20230816025046-f1kqm5h'
new_uids = collect_new_uids(folder_path)
process_folder_with_global_new_uids(folder_path, new_uids)

  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    23103 引用 • 93005 回帖
1 操作
ACai 在 2023-11-01 23:13:47 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...