37 lines
1.2 KiB
Python
37 lines
1.2 KiB
Python
import hashlib
|
||
|
||
|
||
def make_cfi(spine_index: int, file_href: str, element_id: str = "") -> str:
|
||
"""生成 CFI(Canonical Fragment Identifier)
|
||
格式: epubi://title/spine_index/file_href!element_id
|
||
"""
|
||
clean_href = file_href.split("#")[0]
|
||
cfi = f"epubi://book/{spine_index}/{clean_href}"
|
||
if element_id:
|
||
cfi += f"!{element_id}"
|
||
return cfi
|
||
|
||
|
||
def make_element_id(text: str, index: int) -> str:
|
||
"""基于文本内容和索引用哈希生成稳定 ID"""
|
||
short_hash = hashlib.md5(f"{text[:100]}{index}".encode()).hexdigest()[:8]
|
||
return f"seg-{index:04d}-{short_hash}"
|
||
|
||
|
||
def parse_cfi(cfi: str) -> dict | None:
|
||
"""解析 CFI 字符串,返回各组成部分"""
|
||
if not cfi.startswith("epubi://"):
|
||
return None
|
||
try:
|
||
rest = cfi[len("epubi://book/"):]
|
||
parts = rest.split("/", 2)
|
||
spine_index = int(parts[0])
|
||
file_href = parts[1] if len(parts) > 1 else ""
|
||
fragment = parts[2] if len(parts) > 2 else ""
|
||
return {
|
||
"spine_index": spine_index,
|
||
"file_href": file_href,
|
||
"fragment": fragment.lstrip("!"),
|
||
}
|
||
except (ValueError, IndexError):
|
||
return None |