Files
Ombre_Brain/migrate_to_domains.py

119 lines
3.5 KiB
Python

#!/usr/bin/env python3
"""
迁移脚本:将 dynamic/ 下的平铺记忆桶文件重组为域子目录结构。
旧结构: dynamic/{bucket_id}.md
新结构: dynamic/{primary_domain}/{name}_{bucket_id}.md
纯标准库,无外部依赖。
"""
import os
import re
import shutil
VAULT_DIR = os.path.expanduser("~/Documents/Obsidian Vault/Ombre Brain")
DYNAMIC_DIR = os.path.join(VAULT_DIR, "dynamic")
def sanitize_name(name: str) -> str:
cleaned = re.sub(r"[^\w\s\u4e00-\u9fff-]", "", name, flags=re.UNICODE)
return cleaned.strip()[:80] or "unnamed"
def parse_frontmatter(filepath):
"""纯正则解析 YAML frontmatter 中的 id, name, domain 字段。"""
with open(filepath, "r", encoding="utf-8") as f:
content = f.read()
if not content.startswith("---"):
return None
parts = content.split("---", 2)
if len(parts) < 3:
return None
yaml_text = parts[1]
meta = {}
# 提取 id
m = re.search(r"^id:\s*(.+)$", yaml_text, re.MULTILINE)
if m:
meta["id"] = m.group(1).strip().strip("'\"")
# 提取 name
m = re.search(r"^name:\s*(.+)$", yaml_text, re.MULTILINE)
if m:
meta["name"] = m.group(1).strip().strip("'\"")
# 提取 domain 列表
m = re.search(r"^domain:\s*\n((?:\s*-\s*.+\n?)+)", yaml_text, re.MULTILINE)
if m:
meta["domain"] = re.findall(r"-\s*(.+)", m.group(1))
else:
meta["domain"] = ["未分类"]
return meta
def migrate():
if not os.path.exists(DYNAMIC_DIR):
print(f"目录不存在: {DYNAMIC_DIR}")
return
# 只处理直接在 dynamic/ 下的 .md 文件(不处理已在子目录中的)
files = [f for f in os.listdir(DYNAMIC_DIR)
if f.endswith(".md") and os.path.isfile(os.path.join(DYNAMIC_DIR, f))]
if not files:
print("没有需要迁移的文件。")
return
print(f"发现 {len(files)} 个待迁移文件\n")
for filename in sorted(files):
old_path = os.path.join(DYNAMIC_DIR, filename)
try:
meta = parse_frontmatter(old_path)
except Exception as e:
print(f" ✗ 无法解析 {filename}: {e}")
continue
if not meta:
print(f" ✗ 无 frontmatter: {filename}")
continue
bucket_id = meta.get("id", filename.replace(".md", ""))
name = meta.get("name", "")
domain = meta.get("domain", ["未分类"])
primary_domain = sanitize_name(domain[0]) if domain else "未分类"
# 构造新路径
domain_dir = os.path.join(DYNAMIC_DIR, primary_domain)
os.makedirs(domain_dir, exist_ok=True)
if name and name != bucket_id:
new_filename = f"{sanitize_name(name)}_{bucket_id}.md"
else:
new_filename = f"{bucket_id}.md"
new_path = os.path.join(domain_dir, new_filename)
# 移动
shutil.move(old_path, new_path)
print(f"{filename}")
print(f"{primary_domain}/{new_filename}")
print(f"\n迁移完成。")
# 展示新结构
print("\n=== 新目录结构 ===")
for root, dirs, files in os.walk(DYNAMIC_DIR):
level = root.replace(DYNAMIC_DIR, "").count(os.sep)
indent = " " * level
folder = os.path.basename(root)
if level > 0:
print(f"{indent}📁 {folder}/")
for f in sorted(files):
if f.endswith(".md"):
print(f"{indent} 📄 {f}")
if __name__ == "__main__":
migrate()