#pragma once #include #include #include /** * Optional document metadata sent alongside progress sync requests. * Mirrors the metadata object added in KOReader PR #15306. * The official sync server ignores this field; custom servers may use it. */ struct KOReaderMetadata { std::string filename; // e.g. "my_book.epub" std::string title; // Document title from EPUB metadata std::string authors; // Author(s) from EPUB metadata }; /** * Rich CrossPoint position sent alongside progress uploads. Maps 1:1 onto the * crosspoint-sync extended `position` object (see crosspoint-sync docs/API.md). * The official KOSync server ignores unknown fields; crosspoint-sync stores it * so CrossPoint<->CrossPoint sync is lossless instead of xpath-approximated. */ struct KOReaderRichPosition { uint32_t pctQ = 0; // Percentage quantized 0..1,000,000 (authoritative) uint16_t spineIndex = 0; // Spine (chapter) index uint16_t pageNumber = 0; // Page within spine (layout-dependent hint) uint16_t totalPages = 1; // Spine page count (layout-dependent hint) std::optional paragraphIndex; // Synthetic 1-based paragraph index std::string xpath; // KOReader-style xpath (server cap: 120 bytes) }; /** * Progress data from KOReader sync server. */ struct KOReaderProgress { std::string document; // Document hash std::string progress; // XPath-like progress string float percentage; // Progress percentage (0.0 to 1.0) std::string device; // Device name std::string deviceId; // Device ID int64_t timestamp; // Unix timestamp of last update std::optional metadata; // Optional document metadata std::optional position; // Optional rich position (crosspoint-sync servers only) }; /** * HTTP client for KOReader sync API. * * Base URL: https://sync.koreader.rocks:443/ * * API Endpoints: * GET /users/auth - Authenticate (validate credentials) * GET /syncs/progress/:document - Get progress for a document * PUT /syncs/progress - Update progress for a document * * Authentication: * x-auth-user: username * x-auth-key: MD5 hash of password */ class KOReaderSyncClient { public: enum Error { OK = 0, NO_CREDENTIALS, NETWORK_ERROR, AUTH_FAILED, SERVER_ERROR, JSON_ERROR, NOT_FOUND, LOW_MEMORY, USER_EXISTS }; /** * Authenticate with the sync server (validate credentials). * @return OK on success, error code on failure */ static Error authenticate(); /** * Register a new account on the sync server using the stored credentials * (POST /users/create with the MD5 auth key — the server never sees the * plain password). * @return OK on success, USER_EXISTS if the username is taken */ static Error createUser(); /** * Get reading progress for a document. * @param documentHash The document hash (from KOReaderDocumentId) * @param outProgress Output: the progress data * @return OK on success, NOT_FOUND if no progress exists, error code on failure */ static Error getProgress(const std::string& documentHash, KOReaderProgress& outProgress); /** * Update reading progress for a document. * @param progress The progress data to upload * @return OK on success, error code on failure */ static Error updateProgress(const KOReaderProgress& progress); /** * Get human-readable error message. */ static const char* errorString(Error error); /** HTTP status code from the last request (for diagnostics). */ static int lastHttpCode; };