Spaces:
Running
Running
Commit ·
bf53ae4
1
Parent(s): cfd4523
fix: extract end markers from raw <B> tags instead of sections (works per-page, not proximity guess)
Browse files
webapp/tipitaka-api/app/schemas.py
CHANGED
|
@@ -18,6 +18,7 @@ class PageResponse(BaseModel):
|
|
| 18 |
content_html_formatted: str = ""
|
| 19 |
title: Optional[str] = None
|
| 20 |
sections: List[SectionInfo] = Field(default_factory=list)
|
|
|
|
| 21 |
requested_page: Optional[int] = None
|
| 22 |
is_blank: Optional[bool] = False
|
| 23 |
skipped_from: Optional[int] = None
|
|
|
|
| 18 |
content_html_formatted: str = ""
|
| 19 |
title: Optional[str] = None
|
| 20 |
sections: List[SectionInfo] = Field(default_factory=list)
|
| 21 |
+
end_markers: List[str] = Field(default_factory=list)
|
| 22 |
requested_page: Optional[int] = None
|
| 23 |
is_blank: Optional[bool] = False
|
| 24 |
skipped_from: Optional[int] = None
|
webapp/tipitaka-api/app/services/page_service.py
CHANGED
|
@@ -95,6 +95,16 @@ class PageService:
|
|
| 95 |
sections.sort(key=lambda s: s["level"])
|
| 96 |
cleaned = self.search_service.clean_text(row["content_text"])
|
| 97 |
is_blank = self._is_blank_page(cleaned)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
return {
|
| 99 |
"id": row["id"],
|
| 100 |
"volume_id": row["volume_id"],
|
|
@@ -102,10 +112,11 @@ class PageService:
|
|
| 102 |
"page_number": row["page_number"],
|
| 103 |
"total_pages": row["total_pages"] or 0,
|
| 104 |
"content_text": "" if is_blank else cleaned,
|
| 105 |
-
"content_html": "" if is_blank else
|
| 106 |
-
"content_html_formatted": "" if is_blank else self.format_content_html(
|
| 107 |
"title": row["title"],
|
| 108 |
"sections": sections,
|
|
|
|
| 109 |
"is_blank": is_blank,
|
| 110 |
}
|
| 111 |
|
|
|
|
| 95 |
sections.sort(key=lambda s: s["level"])
|
| 96 |
cleaned = self.search_service.clean_text(row["content_text"])
|
| 97 |
is_blank = self._is_blank_page(cleaned)
|
| 98 |
+
|
| 99 |
+
# Extract end markers from raw content_html: <B> tags containing "จบ"
|
| 100 |
+
raw_html = row["content_html"] or ""
|
| 101 |
+
end_markers = []
|
| 102 |
+
if not is_blank and raw_html:
|
| 103 |
+
for m in re.finditer(r'<B>(.*?จบ.*?)</B>', raw_html, re.IGNORECASE):
|
| 104 |
+
tm = m.group(1).strip()
|
| 105 |
+
if tm and tm not in end_markers:
|
| 106 |
+
end_markers.append(tm)
|
| 107 |
+
|
| 108 |
return {
|
| 109 |
"id": row["id"],
|
| 110 |
"volume_id": row["volume_id"],
|
|
|
|
| 112 |
"page_number": row["page_number"],
|
| 113 |
"total_pages": row["total_pages"] or 0,
|
| 114 |
"content_text": "" if is_blank else cleaned,
|
| 115 |
+
"content_html": "" if is_blank else raw_html,
|
| 116 |
+
"content_html_formatted": "" if is_blank else self.format_content_html(raw_html),
|
| 117 |
"title": row["title"],
|
| 118 |
"sections": sections,
|
| 119 |
+
"end_markers": end_markers,
|
| 120 |
"is_blank": is_blank,
|
| 121 |
}
|
| 122 |
|
webapp/tipitaka-web/src/components/layout/ReaderPanel.tsx
CHANGED
|
@@ -17,21 +17,7 @@ const SHELL_STYLES: Record<string, string> = {
|
|
| 17 |
classic: 'bg-[#f5edd8]',
|
| 18 |
};
|
| 19 |
|
| 20 |
-
const renderContent = (html: string, fontSize: number,
|
| 21 |
-
// Sections with "จบ" → end marker below content
|
| 22 |
-
// Only if section's page_number is close to current page (within 5 pages)
|
| 23 |
-
const endMarkers = sections.filter(s => {
|
| 24 |
-
const title = s.title.trim();
|
| 25 |
-
if (!(title.endsWith('จบ') || title.includes('จบ'))) return false;
|
| 26 |
-
// Skip if this end marker belongs to a faraway page
|
| 27 |
-
if (s.page_number !== undefined && currentPage !== undefined) {
|
| 28 |
-
if (currentPage - s.page_number > 5) return false;
|
| 29 |
-
}
|
| 30 |
-
// Skip if already rendered as <h4> inside content body (from <B> tag)
|
| 31 |
-
if (html && html.includes(title)) return false;
|
| 32 |
-
return true;
|
| 33 |
-
});
|
| 34 |
-
|
| 35 |
return (
|
| 36 |
<div
|
| 37 |
style={{ fontSize: `${fontSize}px`, lineHeight: '2.1' }}
|
|
@@ -45,12 +31,12 @@ const renderContent = (html: string, fontSize: number, sections: {title: string;
|
|
| 45 |
) : (
|
| 46 |
<p className="text-[#888] italic text-center py-10">ไม่มีข้อความในหน้านี้</p>
|
| 47 |
)}
|
| 48 |
-
{/* End markers
|
| 49 |
{endMarkers.length > 0 && (
|
| 50 |
<div className="space-y-1 pt-2">
|
| 51 |
-
{endMarkers.map((
|
| 52 |
<p key={i} className="text-xs text-[#888] tracking-widest">
|
| 53 |
-
--------- {
|
| 54 |
</p>
|
| 55 |
))}
|
| 56 |
</div>
|
|
@@ -198,7 +184,7 @@ const ReaderPanel: React.FC = () => {
|
|
| 198 |
))}
|
| 199 |
</header>
|
| 200 |
<div className="border-b border-[#c8860a]/15 mb-5" />
|
| 201 |
-
{renderContent(content.content_html_formatted || content.content_html || '', fontSize, content.
|
| 202 |
</>
|
| 203 |
)}
|
| 204 |
|
|
|
|
| 17 |
classic: 'bg-[#f5edd8]',
|
| 18 |
};
|
| 19 |
|
| 20 |
+
const renderContent = (html: string, fontSize: number, endMarkers: string[]) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
return (
|
| 22 |
<div
|
| 23 |
style={{ fontSize: `${fontSize}px`, lineHeight: '2.1' }}
|
|
|
|
| 31 |
) : (
|
| 32 |
<p className="text-[#888] italic text-center py-10">ไม่มีข้อความในหน้านี้</p>
|
| 33 |
)}
|
| 34 |
+
{/* End markers from raw <B> tags containing "จบ" */}
|
| 35 |
{endMarkers.length > 0 && (
|
| 36 |
<div className="space-y-1 pt-2">
|
| 37 |
+
{endMarkers.map((title, i) => (
|
| 38 |
<p key={i} className="text-xs text-[#888] tracking-widest">
|
| 39 |
+
--------- {title} ---------
|
| 40 |
</p>
|
| 41 |
))}
|
| 42 |
</div>
|
|
|
|
| 184 |
))}
|
| 185 |
</header>
|
| 186 |
<div className="border-b border-[#c8860a]/15 mb-5" />
|
| 187 |
+
{renderContent(content.content_html_formatted || content.content_html || '', fontSize, content.end_markers || [])}
|
| 188 |
</>
|
| 189 |
)}
|
| 190 |
|