Files
readest/apps/readest.koplugin/readest-sync-api.json
T
Huang Xin 7810981417 fix(koplugin): repair reading-stats sync push/pull (#4666)
* fix(koplugin): repair reading-stats sync push/pull

Reading statistics (statistics.sqlite3 page events) never reached the
Readest sync server. Three stacked defects in the koplugin stats path:

1. push/pull called settings:readSetting/saveSetting on self.settings,
   which is the plain readest_sync data table (not a LuaSettings object),
   so auto-sync crashed on every book open
   ("attempt to call method 'readSetting' (a nil value)").
2. pushChanges declares books/notes/configs as required_params, but the
   stats push sent only statBooks/statPages, so Spore rejected the request
   client-side ("books is required for method pushChanges").
3. statBooks/statPages were listed only under the spec's payload, not as
   optional_params. Spore's expected-param set is
   required_params + optional_params, so it rejected them too
   ("statBooks is not expected for method pushChanges").

Fixes:
- Read/persist the cursor as a plain field and save the whole table via
  G_reader_settings:saveSetting, mirroring readest_syncauth.
- Send empty books/notes/configs alongside statBooks/statPages; the server
  defaults each to [] and processes the stat arrays independently.
- Declare statBooks/statPages as optional_params in readest-sync-api.json.

Also add ReadestStats debug logging across pushBookStats/pullBookStats and
push/pull (cursor, collected counts, dispatch, response status, cursor
advance), and surface the push failure status/body that was previously
swallowed on non-interactive syncs.

Tests: cover push/pull cursor handling, the pushChanges required_params
contract, and a spec-level check that every stats payload key is an
expected pushChanges param.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(koplugin): add interactive Push/Pull stats now menu items

Add "Push stats now" and "Pull stats now" entries to the Readest sync
menu, placed after "Readest library" and before "Push books now" and
gated on being signed in (access_token + user_id) like the other
account-level items. They call pushBookStats(true) / pullBookStats(true)
for a manual interactive sync of reading statistics (the whole
statistics.sqlite3 delta), complementing the automatic pull-on-open /
push-on-close.

Interactive push/pull now also confirm their result (pushed / pulled /
up to date) to match the sibling "now" items, since a silent success
would look like a no-op.

Extract the five new strings into the koplugin .po catalogs (empty
msgstr, English fallback at runtime).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(koplugin): translate reading-stats sync strings (33 locales)

Fill the previously empty msgstr entries for the reading-statistics sync
strings across all koplugin locale catalogs: the two new "Push/Pull stats
now" menu items, the pushed / pulled / up-to-date confirmations, and the
push/pull failure messages. Each locale mirrors its existing
Push/Pull/Failed-to verbs and "reading" terminology for consistency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:37:32 +02:00

52 lines
1.5 KiB
JSON

{
"base_url": "https://web.readest.com/api",
"name": "readest-sync-api",
"methods": {
"pullChanges": {
"path": "/sync",
"method": "GET",
"required_params": ["since", "type", "book", "meta_hash"],
"expected_status": [200, 400, 301, 401, 403]
},
"pushChanges": {
"path": "/sync",
"method": "POST",
"required_params": ["books", "notes", "configs"],
"optional_params": ["statBooks", "statPages"],
"payload": ["books", "notes", "configs", "statBooks", "statPages"],
"expected_status": [200, 201, 301, 400, 401, 403]
},
"pullBooks": {
"path": "/sync?type=books",
"method": "GET",
"required_params": ["since"],
"expected_status": [200, 400, 301, 401, 403]
},
"getDownloadUrl": {
"path": "/storage/download",
"method": "GET",
"required_params": ["fileKey"],
"expected_status": [200, 400, 301, 401, 403, 404]
},
"listFiles": {
"path": "/storage/list",
"method": "GET",
"required_params": ["bookHash"],
"expected_status": [200, 400, 301, 401, 403]
},
"deleteFile": {
"path": "/storage/delete",
"method": "DELETE",
"required_params": ["fileKey"],
"expected_status": [200, 400, 301, 401, 403, 404]
},
"getUploadUrl": {
"path": "/storage/upload",
"method": "POST",
"required_params": ["fileName", "fileSize", "bookHash"],
"payload": ["fileName", "fileSize", "bookHash"],
"expected_status": [200, 400, 301, 401, 403]
}
}
}