summaryrefslogtreecommitdiff
path: root/content/migrate.py
diff options
context:
space:
mode:
authorEltrac <hi@guhub.cn>2025-11-27 22:37:21 +0800
committerEltrac <hi@guhub.cn>2025-11-27 22:37:21 +0800
commit7ee4eff516985967d9b4d610012918387764c831 (patch)
tree3f28e8ed2ed0fc1626ed4dd6c46912ce85bf6133 /content/migrate.py
parent3f50451e2857abde4d0777f90816697304071bbb (diff)
docs: split posts into essays and other posts
Diffstat (limited to 'content/migrate.py')
-rw-r--r--content/migrate.py154
1 files changed, 154 insertions, 0 deletions
diff --git a/content/migrate.py b/content/migrate.py
new file mode 100644
index 00000000..7cf2b7c9
--- /dev/null
+++ b/content/migrate.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python3
+import os
+import yaml
+import unicodedata
+import re
+
+# 需要处理的目录
+TARGET_DIRS = ["./struct", "./posts", "./pour"]
+VALID_EXT = [".md", ".markdown"]
+
+def auto_slug(text):
+ # 全部转小写
+ text = text.lower()
+ # 用连字符替换空白
+ text = re.sub(r"\s+", "-", text)
+ # 移除大部分符号,但保留中文
+ text = "".join(
+ c if unicodedata.category(c).startswith(("L", "N")) else "-"
+ for c in text
+ )
+ # 合并重复的 '-'
+ text = re.sub(r"-+", "-", text)
+ # 去掉头尾 '-'
+ return text.strip("-")
+
+# --------------------------------------------------------
+# 读取 YAML Front Matter
+# --------------------------------------------------------
+def read_front_matter(content):
+ """
+ 返回: (yaml_dict, body)
+ """
+ if not content.startswith("---"):
+ return None, content
+
+ end = content.find("\n---", 3)
+ if end == -1:
+ return None, content
+
+ fm_raw = content[3:end].strip()
+ body = content[end + 4:]
+
+ try:
+ fm = yaml.safe_load(fm_raw) or {}
+ return fm, body
+ except Exception:
+ return None, content
+
+
+# --------------------------------------------------------
+# 写回 YAML Front Matter
+# --------------------------------------------------------
+def dump_front_matter(fm):
+ return "---\n" + yaml.safe_dump(fm, sort_keys=False, allow_unicode=True) + "---\n"
+
+
+# --------------------------------------------------------
+# 生成 alias
+# --------------------------------------------------------
+
+def build_alias(file_path, fm):
+ rel_path = os.path.relpath(file_path, ".")
+ parts = rel_path.split(os.sep)
+
+ # 忽略 _index.md
+ if parts[-1].lower() == "_index.md":
+ return None
+
+ # 移除顶级目录 struct/posts/pour
+ if parts[0] in ("struct", "posts", "pour"):
+ parts = parts[1:]
+
+ # 文件名(无扩展名)
+ filename = os.path.splitext(parts[-1])[0]
+
+ # slug 优先
+ name = fm.get("slug", filename)
+
+ # ---- 新逻辑:对 slug / name 做安全化与大小写处理 ----
+ # 1. 空格换成连字符
+ # 2. 全部转为小写
+ # 3. 保留中文,保留字母数字与连字符
+ name = name.strip()
+ name = name.lower() # 全部小写
+ name = name.replace(" ", "-")
+ # 只去除 *不安全* 的字符,避免破坏中文
+ name = re.sub(r"[^a-z0-9\-\u4e00-\u9fff]", "", name)
+
+ # 构建路径
+ if len(parts) > 1:
+ sub = "/".join(parts[:-1])
+ return f"/posts/{sub}/{name}"
+ else:
+ return f"/posts/{name}"
+
+
+
+
+def should_ignore(path):
+ """忽略 _index.md / _index.markdown,不区分大小写"""
+ filename = os.path.basename(path).lower()
+ name, ext = os.path.splitext(filename)
+ if name == "_index" and ext in VALID_EXT:
+ return True
+ return False
+
+# --------------------------------------------------------
+# 处理单个 Markdown 文件
+# --------------------------------------------------------
+def process_file(path):
+ if should_ignore(path):
+ return
+
+ with open(path, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ fm, body = read_front_matter(content)
+ if fm is None:
+ print(f"[跳过] 无 YAML front-matter: {path}")
+ return
+
+ alias = build_alias(path, fm)
+
+ # 添加 aliases 字段
+ if "aliases" not in fm:
+ fm["aliases"] = [alias]
+ else:
+ if not isinstance(fm["aliases"], list):
+ fm["aliases"] = [fm["aliases"]]
+ if alias not in fm["aliases"]:
+ fm["aliases"].append(alias)
+
+ # 写回文件
+ new_content = dump_front_matter(fm) + body
+ with open(path, "w", encoding="utf-8") as f:
+ f.write(new_content)
+
+ print(f"[更新] {path} → {alias}")
+
+
+# --------------------------------------------------------
+# 主函数
+# --------------------------------------------------------
+def main():
+ for root_dir in TARGET_DIRS:
+ for root, dirs, files in os.walk(root_dir):
+ for file in files:
+ if os.path.splitext(file)[1].lower() in VALID_EXT:
+ full = os.path.join(root, file)
+ process_file(full)
+
+
+if __name__ == "__main__":
+ main()