fix: Fix kosync repositioning issue#783
Conversation
|
Seems like it was built on top of overlay PR branch, better to separate them |
|
Rebased |
|
How well does this work? I was doing some light testing in #61 on the KOReader side of things by replacing its own sync function with a percentage based look-up. If you do this, then the syncing can technically be two-way preserved. But, I think KOReader bases its % on ‘page index / total pages’, not book size as crosspoint does. So text based books should probably be pretty close in terms of syncing, but books with a lot of images might be a bit off. I don’t own an X4, so I could be very wrong. |
|
Caveat; I dont use a loot of books containing images, so YMMV |
GetCumulativeSize function that percentage calculation relies on uses uncompressed sizes of each spine item file (the XHTML/HTML/other spine href targets) and accumulates those sizes; it does not include images referenced inside those files. So it should work the same for image-heavy books as for non-image books |
|
@osteotek Do you happen to know if there are methods already implemented to achieve a ‘current page / total pages’ calculation instead of using the size? Or does the file have to be fully indexed before that can be done? I don't think that should replace all references to % as already done, but in this specific domain of KOSync, it's probably valuable to try and match up so that it'll work regardless of the file being synced. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a KOReader synchronization bug where reading positions were incorrectly restored depending on whether XPath parsing succeeded or failed. The fix unifies the positioning logic to use percentage-based calculations exclusively, removing the unreliable XPath parsing approach that caused positions to jump to chapter beginnings.
Changes:
- Removed XPath-based positioning logic that incorrectly set pageNumber to 0
- Unified toCrossPoint() to use percentage-based positioning for both spine identification and page calculation
- Added intelligent default selection in sync UI to prefer the furthest progress between local and remote
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| lib/KOReaderSync/ProgressMapper.cpp | Removed parseDocFragmentIndex() function and refactored toCrossPoint() to use percentage-based positioning exclusively with density estimation for page calculations |
| lib/KOReaderSync/ProgressMapper.h | Updated function signature to accept currentSpineIndex and totalPagesInCurrentSpine parameters; updated comments to reflect percentage-based approach |
| src/activities/reader/KOReaderSyncActivity.cpp | Updated toCrossPoint() call with new parameters; added logic to default to furthest progress option |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else { | ||
| selectedOption = 0; // Apply remote progress | ||
| } | ||
|
|
|
@jpirnay I’ve tested this, and it appears to work better than before. At least, it doesn’t always sync to the beginning of the chapter on the Crosspoint side. However, syncing from crosspoint to the remote server and then fetching progress on another Koreader always puts me at the beginning of the next chapter (not the same one I’ve been reading on Crosspoint). Is this the same for you? |
You are pointing our a very important issue: We are assuming KOReader uses the XPath to identify the DocFragment and the percentage for fine positioning within it (thats how Crosspoint does it when reading something). However, based on KOReader's syncToProgress implementation, it only uses GotoXPointer(xPath) for EPUB, ignoring percentage. This results in positioning at the start of the specified DocFragment (chapter start), not an exact location. Reality: Sync achieves approximate, chapter-level positioning, not pixel-perfect sync. KOReader lands at the beginning of the DocFragment because the XPath lacks detailed element paths (e.g., no If KOReader were modified to use percentage for intra-DocFragment positioning, sync would be more accurate. As-is, this is the best CrossPoint can do. It works for round-trip CrossPoint sync, and provides chapter-level KOReader positioning. |
|
@jpirnay thanks for the analysis, seems like we can't solve this without modifications to how crosspoint stores text, or without reparsing html doc on every sync. PR in its current form looks like a good addition, I've tested it, works good. |
|
For the brave of heart, @iandchasse pointed out a solution for better KOReader sync behaviour, which requires the patching of a single file though: #61 (comment) |
|
@jpirnay would it be possible to fix merge conflicts here? |
Done |
|
No actionable comments were generated in the recent review. 🎉 📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used🧠 Learnings (2)📚 Learning: 2026-02-16T20:43:19.339ZApplied to files:
📚 Learning: 2026-02-18T15:43:07.515ZApplied to files:
🧬 Code graph analysis (1)src/activities/reader/KOReaderSyncActivity.cpp (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
🔇 Additional comments (2)
📝 WalkthroughWalkthroughProgressMapper.toCrossPoint was refactored to use percentage-based mapping (no XPath/DocFragment parsing), accept (currentSpineIndex, totalPagesInCurrentSpine) instead of totalPagesInSpine, and add fallbacks/estimations for spine/page totals. KOReaderSyncActivity call sites updated and selection logic now compares local vs remote percentage; RenderLock usage unchanged. Changes
Sequence Diagram(s)sequenceDiagram
participant Activity as KOReaderSyncActivity
participant Mapper as ProgressMapper
participant Epub as Epub
participant UI as UI/RenderLock
Activity->>Mapper: toCrossPoint(epub, koPos, currentSpineIndex, totalPagesInCurrentSpine)
Mapper->>Epub: read spine boundaries / byte positions
Epub-->>Mapper: spine byte map
Mapper-->>Activity: CrossPointPosition (spineIndex, pageNumber, totalPages)
Activity->>Activity: compare localProgress.percentage vs remoteProgress.percentage
Activity->>UI: acquire RenderLock / update selected option
UI-->>Activity: release RenderLock
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/KOReaderSync/ProgressMapper.cpplib/KOReaderSync/ProgressMapper.hsrc/activities/reader/KOReaderSyncActivity.cpp
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-16T20:43:19.339Z
Learnt from: Levrk
Repo: crosspoint-reader/crosspoint-reader PR: 909
File: src/activities/reader/EpubReaderActivity.cpp:461-461
Timestamp: 2026-02-16T20:43:19.339Z
Learning: In src/activities/reader/EpubReaderActivity.cpp, when using ConfirmationActivity with enterNewActivity(), it's not necessary to call exitActivity() beforehand. The ConfirmationActivity operates as a modal dialog and cleanup is handled via the pendingSubactivityExit flag in the callback lambda. This differs from other activities like EpubReaderChapterSelectionActivity or KOReaderSyncActivity which do require exitActivity() before enterNewActivity().
Applied to files:
src/activities/reader/KOReaderSyncActivity.cpp
🧬 Code graph analysis (1)
src/activities/reader/KOReaderSyncActivity.cpp (1)
lib/KOReaderSync/ProgressMapper.cpp (4)
toCrossPoint(32-107)toCrossPoint(32-33)toKOReader(7-30)toKOReader(7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: cppcheck
🔇 Additional comments (3)
lib/KOReaderSync/ProgressMapper.h (1)
53-58: Signature update looks consistent.
No concerns with the new parameters or defaults.lib/KOReaderSync/ProgressMapper.cpp (1)
32-38: Initialization change looks fine.
No issues spotted.src/activities/reader/KOReaderSyncActivity.cpp (1)
126-143: Progress-based default selection looks good.
The mutex-guarded update and comparison read clean and should pick the sensible default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@lib/KOReaderSync/ProgressMapper.cpp`:
- Around line 44-99: The file uses std::min and std::max in ProgressMapper.cpp
(e.g., clampedProgress calculation and pageNumber clamping) but is missing the
<algorithm> include; add the missing `#include` <algorithm> at the top of
ProgressMapper.cpp (near other includes) so std::min/std::max resolve cleanly
for functions/methods like the progress-to-page calculation that reference
result.pageNumber and clampedProgress.
- Around line 109-113: The XPath generation in ProgressMapper::generateXPath
currently uses a 0-based spineIndex, producing invalid XPath like
/body/DocFragment[0]/body; change the string construction to use (spineIndex +
1) so the returned path is "/body/DocFragment[" + std::to_string(spineIndex + 1)
+ "]/body"; also update the inline comment to reflect that XPath uses 1-based
indices rather than claiming 0-based DocFragment indices.
---
Duplicate comments:
In `@lib/KOReaderSync/ProgressMapper.h`:
- Around line 63-65: The header comment for generateXPath is out of sync: it
states the format uses "spineIndex+1" but the implementation returns the raw
spine index; update the comment in ProgressMapper.h to reflect that
generateXPath returns the raw spineIndex (not +1) and describe the format
accordingly (e.g., "/body/DocFragment[spineIndex]/body" or explicitly state
"uses raw spineIndex"), ensuring the doc matches the generateXPath
implementation.
## Summary * Original implementation had inconsistent positioning logic: - When XPath parsing succeeded: incorrectly set pageNumber = 0 (always beginning of chapter) - When XPath parsing failed: used percentage for positioning (worked correctly) - Result: Positions restored to wrong locations depending on XPath parsing success - Mentioned in Issue crosspoint-reader#581 * Solution - Unified ProgressMapper::toCrossPoint() to use percentage-based positioning exclusively for both spine identification and intra-chapter page calculation, eliminating unreliable XPath parsing entirely. ## Additional Context * ProgressMapper.cpp: Simplified toCrossPoint() to always use percentage for positioning, removed parseDocFragmentIndex() function * ProgressMapper.h: Updated comments and removed unused function declaration * Tests confirmed appropriate positioning * __Notabene: the syncing to another device will (most probably) end up at the current chapter of crosspoints reading position. There is not much we can do about it, as KOReader needs to have the correct XPath information - we can only provide an apporximate position (plus percentage) - the percentage information is not used in KOReaders current implementation__ --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? YES
## Summary * Original implementation had inconsistent positioning logic: - When XPath parsing succeeded: incorrectly set pageNumber = 0 (always beginning of chapter) - When XPath parsing failed: used percentage for positioning (worked correctly) - Result: Positions restored to wrong locations depending on XPath parsing success - Mentioned in Issue crosspoint-reader#581 * Solution - Unified ProgressMapper::toCrossPoint() to use percentage-based positioning exclusively for both spine identification and intra-chapter page calculation, eliminating unreliable XPath parsing entirely. ## Additional Context * ProgressMapper.cpp: Simplified toCrossPoint() to always use percentage for positioning, removed parseDocFragmentIndex() function * ProgressMapper.h: Updated comments and removed unused function declaration * Tests confirmed appropriate positioning * __Notabene: the syncing to another device will (most probably) end up at the current chapter of crosspoints reading position. There is not much we can do about it, as KOReader needs to have the correct XPath information - we can only provide an apporximate position (plus percentage) - the percentage information is not used in KOReaders current implementation__ --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? YES
## Summary * Original implementation had inconsistent positioning logic: - When XPath parsing succeeded: incorrectly set pageNumber = 0 (always beginning of chapter) - When XPath parsing failed: used percentage for positioning (worked correctly) - Result: Positions restored to wrong locations depending on XPath parsing success - Mentioned in Issue crosspoint-reader#581 * Solution - Unified ProgressMapper::toCrossPoint() to use percentage-based positioning exclusively for both spine identification and intra-chapter page calculation, eliminating unreliable XPath parsing entirely. ## Additional Context * ProgressMapper.cpp: Simplified toCrossPoint() to always use percentage for positioning, removed parseDocFragmentIndex() function * ProgressMapper.h: Updated comments and removed unused function declaration * Tests confirmed appropriate positioning * __Notabene: the syncing to another device will (most probably) end up at the current chapter of crosspoints reading position. There is not much we can do about it, as KOReader needs to have the correct XPath information - we can only provide an apporximate position (plus percentage) - the percentage information is not used in KOReaders current implementation__ --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? YES
Summary
Additional Context
AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? YES