feat(scanner): store EPUB reading direction at scan time
Release / build-and-push (push) Successful in 2m33s
Release / build-and-push (push) Successful in 2m33s
Fixed-layout EPUBs lean on the reading_direction column (the web reader forces book.dir = rtl from it when the file didn't set direction itself), but the scanner never populated it for EPUBs - only ComicInfo fed it. Meanwhile real Japanese EPUBs declare page-progression-direction on the OPF spine, which foliate reads client-side but nothing stored. Read the spine attribute in the structured OPF parser and map it into ReadingDirection in parseOPFContent (EPUB2/3, case-insensitive, plus 'right-to-left'/'left-to-right' spellings); undeclared stays empty rather than forcing ltr, preserving the editor's Auto default. Sidecar OPFs are metadata-only documents without spines, so the Calibre path is a no-op. The merge gap-fill copies an embedded-only direction into a blank sidecar field, and hand-set values keep winning through the existing OverrideReadingDirection protection. Tests: declared rtl/RTL/ltr, undeclared and unknown values staying empty, plus sidecar-wins vs embedded-fills merge cases. Existing manga EPUBs declaring rtl (verified live in-library) pick the value up on their next scan, feeding the API and reader config mobile clients consume.
This commit is contained in:
@@ -58,7 +58,8 @@ type opfDocument struct {
|
||||
Items []opfItem `xml:"item"`
|
||||
} `xml:"manifest"`
|
||||
Spine struct {
|
||||
Itemrefs []struct {
|
||||
PageProgressionDirection string `xml:"page-progression-direction,attr"`
|
||||
Itemrefs []struct {
|
||||
IDRef string `xml:"idref,attr"`
|
||||
} `xml:"itemref"`
|
||||
} `xml:"spine"`
|
||||
@@ -184,6 +185,22 @@ func (d *opfDocument) readSeries() (series string, index float64) {
|
||||
return series, index
|
||||
}
|
||||
|
||||
// pageProgressionDirection returns the OPF spine's reading direction as
|
||||
// "rtl" or "ltr", or "" when the file declares none (callers treat that as
|
||||
// unknown, not as left-to-right). EPUB2/3 declare this on <spine>; it is
|
||||
// what foliate reads client-side, and the DB column feeds clients (and the
|
||||
// web reader's fixed-layout override) that need it up front.
|
||||
func (d *opfDocument) pageProgressionDirection() string {
|
||||
switch strings.ToLower(strings.TrimSpace(d.Spine.PageProgressionDirection)) {
|
||||
case "rtl", "right-to-left":
|
||||
return "rtl"
|
||||
case "ltr", "left-to-right", "default":
|
||||
return "ltr"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// itemByID returns manifest items with id, href and media-type, keyed by id.
|
||||
func (d *opfDocument) itemByID() map[string]opfItem {
|
||||
m := make(map[string]opfItem, len(d.Manifest.Items))
|
||||
|
||||
Reference in New Issue
Block a user