diff --git a/app/reader.py b/app/reader.py
index 2c51b8a..3f8efaa 100644
--- a/app/reader.py
+++ b/app/reader.py
@@ -513,15 +513,17 @@ CLI_PROMPT_RE = re.compile(
def extract_outline_entries(outline, reader) -> List[Tuple[str, int]]:
"""
- Recursively extracts bookmarks / outline entries from PDF.
- Returns a list of (title, 0-indexed page_number).
+ Recursively extracts bookmarks / outline entries from PDF respecting hierarchy depth.
+ For books with deep trees (Parts -> Chapters -> Sections -> Subsections),
+ prioritizes major structural headings and prevents fragmenting chapters into
+ hundreds of tiny 1-page snippets.
"""
- entries: List[Tuple[str, int]] = []
+ raw_entries: List[Tuple[str, int, int]] = []
- def _traverse(items):
+ def _traverse(items, depth=0):
for item in items:
if isinstance(item, list):
- _traverse(item)
+ _traverse(item, depth + 1)
else:
try:
title = getattr(item, "title", None) or (item.get("/Title") if hasattr(item, "get") else None)
@@ -529,13 +531,44 @@ def extract_outline_entries(outline, reader) -> List[Tuple[str, int]]:
if title and page_num is not None:
clean_title = str(title).strip()
if clean_title:
- entries.append((clean_title, int(page_num)))
+ raw_entries.append((clean_title, int(page_num), depth))
except Exception:
continue
if outline:
- _traverse(outline)
- return entries
+ _traverse(outline, 0)
+
+ if not raw_entries:
+ return []
+
+ # If total entries is reasonable (<= 50), return all
+ if len(raw_entries) <= 50:
+ return [(t, p) for t, p, _ in raw_entries]
+
+ # Check depth distribution
+ d0_entries = [e for e in raw_entries if e[2] == 0]
+ d1_entries = [e for e in raw_entries if e[2] <= 1]
+
+ # If top-level (depth 0) already has >= 10 chapters, use depth 0
+ if len(d0_entries) >= 10:
+ return [(t, p) for t, p, _ in d0_entries]
+
+ # Otherwise, if depth <= 1 gives a good number of chapters (10 to 80), use depth <= 1
+ if len(d1_entries) <= 80:
+ return [(t, p) for t, p, _ in d1_entries]
+
+ # If still too many entries (> 80), filter by major chapter keywords
+ major_re = re.compile(
+ r"^(cover|title\s+page|copyright|contents|introduction|your\s+study\s+plan|"
+ r"part\s+[0-9ivxlcdm]+|chapter\s+\d+|appendix\s+[a-z]|glossary|index)",
+ re.IGNORECASE
+ )
+ major_entries = [e for e in d1_entries if major_re.match(e[0])]
+ if len(major_entries) >= 10:
+ return [(t, p) for t, p, _ in major_entries]
+
+ return [(t, p) for t, p, _ in d1_entries[:80]]
+
def clean_page_lines(raw_pages_text: List[str]) -> List[str]:
diff --git a/app/static/index.html b/app/static/index.html
index 8755fad..e94a815 100644
--- a/app/static/index.html
+++ b/app/static/index.html
@@ -1075,7 +1075,7 @@
-
+