From 762181c123244f77111c4f6e10236c59adab8b46 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sun, 7 Jun 2026 00:26:53 -0400 Subject: [PATCH] fix(sync): treat page index as sole locator for fixed-layout pull path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- internal/handlers/koreader.go | 18 +++++++++++++----- internal/sync/progress.go | 7 +++++-- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/internal/handlers/koreader.go b/internal/handlers/koreader.go index e7b3461..895ebea 100644 --- a/internal/handlers/koreader.go +++ b/internal/handlers/koreader.go @@ -622,11 +622,19 @@ func (h *KOReaderHandler) GetMetadata(c *echo.Context) error { Percentage: progress.Percentage.Float64, } - if progress.Epubcfi.Valid { - progressData.Epubcfi = &progress.Epubcfi.String - } - if progress.Epubcfi.Valid && wsync.IsStandardEPUBCFI(progress.Epubcfi.String) { - h.convertCFIToXPointer(c, mediaItem, progress, &progressData) + // CFI/xpointer are meaningless for image-based fixed-layout content; the + // page index is the canonical locator. Only return them for reflowable docs. + formatGroup := wsync.FormatGroup(mediaItem.FormatGroup) + isFixed := formatGroup == wsync.FormatGroupFixedLayout || + 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 { progress := int(progress.Chapter.Int32) diff --git a/internal/sync/progress.go b/internal/sync/progress.go index 6ee754f..ec3c647 100644 --- a/internal/sync/progress.go +++ b/internal/sync/progress.go @@ -404,8 +404,11 @@ func (s *ProgressService) SaveProgress(ctx context.Context, req SaveProgressRequ isFixed := formatGroup == FormatGroupFixedLayout || formatGroup == FormatGroupComicArchive if isFixed { // Fixed-layout & comic formats: the page index is the canonical locator. - // Derive percentage from page; CFI/character-offset are meaningless for - // image-based content, so they are intentionally left untouched here. + // CFI/character-offset are meaningless for image-based content, so clear + // 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 { pct := PageToPercentage(int(params.CurrentPage.Int32), int(params.TotalPages.Int32)) params.Percentage = pgtype.Float8{Float64: pct, Valid: true}