fix(sync): treat page index as sole locator for fixed-layout pull path

The push path (koreader→server) was already updated to omit epubcfi
for paging documents, and SaveProgress derives percentage from
page/total_pages for fixed formats. However, the pull path
(server→koreader) still returned any stored epubcfi and attempted
CFI→XPointer conversion, and SaveProgress preserved stale CFI values
written by the web reader (which generates fake CFIs via
CFI.fake.fromIndex for every comic/PDF page).

These fake CFI strings lingered in the database and koreader's pull
path picked them up over progress.page, causing tonumber("epubcfi(...)")
→ nil → GotoPage(nil) → crash when the user confirmed the sync prompt.

Changes:
- GetMetadata (koreader.go): for fixed_layout/comic_archive formats,
  skip returning epubcfi and skip the CFI→CRE XPointer conversion.
  Reflowable documents are byte-for-byte unchanged.
- SaveProgress (progress.go): for fixed formats, explicitly clear
  epubcfi and character_offset on every save so stale values from
  prior web-reader sessions are cleaned up over time.
This commit is contained in:
2026-06-07 00:26:53 -04:00
parent d11a623f9f
commit 762181c123
2 changed files with 18 additions and 7 deletions
+13 -5
View File
@@ -622,11 +622,19 @@ func (h *KOReaderHandler) GetMetadata(c *echo.Context) error {
Percentage: progress.Percentage.Float64, Percentage: progress.Percentage.Float64,
} }
if progress.Epubcfi.Valid { // CFI/xpointer are meaningless for image-based fixed-layout content; the
progressData.Epubcfi = &progress.Epubcfi.String // page index is the canonical locator. Only return them for reflowable docs.
} formatGroup := wsync.FormatGroup(mediaItem.FormatGroup)
if progress.Epubcfi.Valid && wsync.IsStandardEPUBCFI(progress.Epubcfi.String) { isFixed := formatGroup == wsync.FormatGroupFixedLayout ||
h.convertCFIToXPointer(c, mediaItem, progress, &progressData) formatGroup == wsync.FormatGroupComicArchive
if !isFixed {
if progress.Epubcfi.Valid {
progressData.Epubcfi = &progress.Epubcfi.String
}
if progress.Epubcfi.Valid && wsync.IsStandardEPUBCFI(progress.Epubcfi.String) {
h.convertCFIToXPointer(c, mediaItem, progress, &progressData)
}
} }
if progress.Chapter.Valid { if progress.Chapter.Valid {
progress := int(progress.Chapter.Int32) progress := int(progress.Chapter.Int32)
+5 -2
View File
@@ -404,8 +404,11 @@ func (s *ProgressService) SaveProgress(ctx context.Context, req SaveProgressRequ
isFixed := formatGroup == FormatGroupFixedLayout || formatGroup == FormatGroupComicArchive isFixed := formatGroup == FormatGroupFixedLayout || formatGroup == FormatGroupComicArchive
if isFixed { if isFixed {
// Fixed-layout & comic formats: the page index is the canonical locator. // Fixed-layout & comic formats: the page index is the canonical locator.
// Derive percentage from page; CFI/character-offset are meaningless for // CFI/character-offset are meaningless for image-based content, so clear
// image-based content, so they are intentionally left untouched here. // any stale value that may have been stored by the web reader (which
// generates fake CFIs for comics).
params.Epubcfi = pgtype.Text{}
params.CharacterOffset = pgtype.Int8{}
if params.CurrentPage.Valid && params.TotalPages.Valid && params.TotalPages.Int32 > 0 { if params.CurrentPage.Valid && params.TotalPages.Valid && params.TotalPages.Int32 > 0 {
pct := PageToPercentage(int(params.CurrentPage.Int32), int(params.TotalPages.Int32)) pct := PageToPercentage(int(params.CurrentPage.Int32), int(params.TotalPages.Int32))
params.Percentage = pgtype.Float8{Float64: pct, Valid: true} params.Percentage = pgtype.Float8{Float64: pct, Valid: true}