Unofficial Paperbnd/Popfeed plugin for KOReader
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 300 lines 14 kB view raw view rendered
1# Paperbnd KOReader Plugin - Developer Documentation 2 3> **Document Purpose**: This is technical documentation for developers and maintainers. For user-facing installation and usage instructions, see [README.md](README.md). 4 5A KOReader plugin for syncing reading progress to an AT Protocol PDS using the Popfeed List Item lexicon. 6 7## Overview 8 9Paperbnd allows KOReader users to sync their reading progress to their AT Protocol Personal Data Server (PDS) via the Popfeed social reading platform. The plugin tracks page progress and synchronizes it with existing Popfeed book entries. 10 11## Architecture 12 13The plugin consists of two main components: 14 15### 1. XRPC Client (`xrpc.lua`) 16 17A dedicated module for AT Protocol/XRPC communication that handles: 18 19- **Generic XRPC method invocation** with support for GET and POST requests 20- **PDS resolution** via the Slingshot service (Microcosm) 21- **Advanced session management** with multi-level token renewal and automatic retry 22- **Authentication** using handle and app password 23- **Record operations** (list, get, put) for AT Protocol collections 24 25**Key Features:** 26- Table-based parameters for flexibility (method, params, body, pds, skip_renewal) 27- Multi-level session renewal: refresh token → app password fallback → automatic retry 28- Session validation on plugin startup to proactively renew expired tokens 29- Authorization headers only on POST requests (GET requests never authenticated) 30- Callback support for persisting refreshed tokens to plugin settings 31- Handles both 401 and 400 "expired" errors as authentication failures 32- Prevents infinite recursion during renewal with `skip_renewal` flag 33 34### 2. Main Plugin (`main.lua`) 35 36The primary plugin file that integrates with KOReader: 37 38- **Settings management** with persistent storage of credentials and book mappings 39- **Authentication flow** with two-step credential input (handle, then app password) 40- **Book linking** allows users to select from their existing Popfeed list items 41- **Progress sync** tracks current page, total pages, and percentage 42- **Document hooks** for automatic sync on document close 43- **Menu integration** with conditional menu items based on authentication state 44 45## User Workflow 46 471. **Set Credentials**: User enters their AT Protocol handle and app password 48 - Plugin resolves PDS URL via Slingshot service 49 - Creates session and stores access/refresh tokens 50 512. **Link Book**: User selects current document and links it to an existing Popfeed book 52 - Fetches user's list items from their PDS 53 - Filters for books only 54 - Stores mapping between document path and list item rkey 55 563. **Sync Progress**: Reading progress is synchronized automatically 57 - Updates `bookProgress` field with current page, total pages, and percentage 58 - Updates `listType` to "currently_reading_books" if needed 59 - Can be triggered manually or automatically on document close 60 614. **Session Management**: Tokens are automatically renewed with multi-level fallback 62 - Session validated at plugin startup to ensure fresh tokens 63 - Authentication errors (401 or 400 "expired") trigger automatic renewal 64 - First attempts refresh with refresh token 65 - Falls back to creating new session with app password if refresh token expired 66 - Original request automatically retried after successful renewal 67 - New tokens saved to settings transparently via callback 68 - No user intervention required at any point 69 70## Technical Details 71 72### External Resources 73 74- **KOReader Plugin Development**: https://github.com/koreader/koreader/wiki/Developer-documentation 75- **AT Protocol Specifications**: https://atproto.com/specs/atp 76- **Popfeed Platform**: https://popfeed.social/ 77- **Paperbnd Website**: https://paperbnd.club/ 78 79### Lexicons Used 80 81- `social.popfeed.feed.listItem` - Individual book entries with progress tracking 82- `social.popfeed.feed.list` - Reading lists (e.g., "currently_reading_books") 83- `com.atproto.server.createSession` - Initial authentication 84- `com.atproto.server.refreshSession` - Token refresh 85- `com.atproto.repo.listRecords` - Fetch book collections 86- `com.atproto.repo.getRecord` - Fetch individual book records 87- `com.atproto.repo.putRecord` - Update book records with progress 88 89### Data Storage 90 91Settings are persisted in `paperbnd.lua` within KOReader's settings directory: 92- Handle and app password 93- PDS URL 94- Access and refresh tokens 95- User DID (Decentralized Identifier) 96- Document-to-listItem mappings (document path -> {rkey, title, author}) 97 98### Book Progress Format 99 100```lua 101bookProgress = { 102 status = "in_progress", 103 percent = 42, -- Calculated percentage 104 currentPage = 150, -- From KOReader document stats 105 totalPages = 357, -- From KOReader document stats 106 updatedAt = "2025-10-20T14:30:00.000Z" -- ISO 8601 timestamp 107} 108``` 109 110## Design Decisions 111 112### Simplicity First 113- Single-file XRPC client for clean separation of concerns 114- No future feature speculation - only implemented what's needed 115- Direct and straightforward code without unnecessary abstractions 116 117### User Book Selection 118Rather than attempting to match books by ISBN or title, users manually link documents to existing Popfeed list items. This approach: 119- Avoids complex ISBN DB API integration 120- Ensures accuracy (user confirms the correct book) 121- Works with any book format supported by KOReader 122- Leverages existing Popfeed records as the source of truth 123 124### Advanced Session Management 125 126The plugin implements a sophisticated multi-level session management system that is the core reliability feature. 127 128**Architecture:** 129 130The session management system has three levels of components: 131 1321. **`call()` method in xrpc.lua**: Generic XRPC request handler that detects auth errors (401 or 400 "expired"), calls `renewSession()` on failure, rebuilds requests with new tokens, and retries automatically. This is the single point of entry for all XRPC operations. 133 1342. **`renewSession()` method**: Multi-level fallback coordinator that first attempts `refreshSession()`, then falls back to `createSession()` with app password if refresh token expired. Returns boolean success status. 135 1363. **`validateSession()` method**: Makes lightweight test request at plugin startup to verify token validity and proactively renew expired tokens before user operations. 137 138**Integration with Main Plugin:** 139 140- **Initialization**: Loads credentials from persistent storage, configures XRPC client with tokens and app password, and sets callback for automatic token persistence 141- **Token Callback**: Receives new tokens from XRPC client, saves to plugin settings immediately, ensuring credentials never get out of sync 142- **Startup Validation**: Runs `validateSession()` silently when plugin loads to ensure tokens are fresh before first use 143 144**Automatic Renewal Flow:** 145 1461. Authentication error detected (401 or 400 "expired") 1472. Attempt to refresh using refresh token 1483. If refresh token expired, create new session with stored app password 1494. If renewal successful, retry the original request automatically 1505. Save new tokens to persistent storage via callback 151 152**Example Flow - User syncs progress with expired access token:** 153 1541. `syncProgress()` calls `xrpc:putRecord()` 1552. `putRecord()` calls `call()` with POST body 1563. Server returns 401 Unauthorized 1574. `call()` detects auth error, calls `renewSession()` 1585. `renewSession()` tries `refreshSession()` 1596. If refresh token valid: new tokens received, callback fired 1607. If refresh token expired: `createSession()` with app password 1618. `call()` rebuilds request with new access token 1629. `call()` retries POST request 16310. Request succeeds, progress synced 16411. User sees success message, unaware of token renewal 165 166**Benefits:** 167 168- No manual "Refresh Session" button needed 169- Tokens can expire for weeks without user intervention 170- Original requests automatically retried after renewal 171- Seamless user experience without interruptions 172- Callback mechanism keeps plugin and XRPC client in sync 173- Proactive validation prevents mid-operation failures 174 175### Authentication Strategy 176- Uses Slingshot service to resolve PDS from handle (no manual PDS entry) 177- App passwords for security (no main account passwords stored) 178- Refresh tokens enable long-lived sessions without re-authentication 179 180## Implementation Notes 181 182### Reference Implementation 183Built by studying the ReadwiseReader KOReader plugin for: 184- Plugin structure and initialization patterns 185- UI component creation (menus, dialogs, input forms) 186- HTTP request handling with `socket.http` and `ltn12` 187- JSON encoding/decoding with `rapidjson` 188- Settings persistence with `LuaSettings` 189- Document metadata access and event hooks 190 191### AT Protocol Specifics 192- GET requests never require authentication headers 193- POST requests use Bearer token authentication 194- Refresh tokens are used as access tokens for the refresh endpoint 195- Both access and refresh tokens are rotated on each refresh 196- Session validation uses `com.atproto.server.getSession` endpoint 197- Authentication errors can return 401 or 400 with "expired" message 198 199### Error Handling 200- User-friendly error messages via `InfoMessage` widgets 201- Graceful degradation when operations fail 202- Network errors handled with appropriate fallbacks 203 204## Code Quality Assessment 205 206**Current State (December 2024):** 207 208The codebase is well-structured and production-ready with the following characteristics: 209 210**Strengths:** 211- Clean separation of concerns (XRPC client vs. plugin logic) 212- Comprehensive error handling with user-friendly messages 213- Robust session management with multiple fallback mechanisms 214- Proper state synchronization between components via callbacks 215- No memory leaks or blocking operations 216- Defensive programming with validation at critical points 217- Clear function naming and logical organization 218 219**Architecture:** 220- **XRPC Client (353 lines)**: Self-contained networking layer with 15 public methods 221- **Main Plugin (446 lines)**: UI integration with 17 public methods 222- **Metadata (6 lines)**: KOReader plugin manifest 223- **Total**: 805 lines of well-documented Lua code 224 225**Known Limitations:** 226- One TODO in `main.lua` for updating `listUri` when changing reading status (currently commented out at lines 413-416). Implementation would require fetching the user's "currently_reading_books" list URI and updating the `listUri` field when moving a book to that list. 227- Session validation at startup runs synchronously (could be async but startup is fast enough) 228- No retry logic for network failures (only authentication failures) 229- No offline queuing (by design, for simplicity) 230 231**Testing Considerations:** 232- Manual testing required (KOReader plugin environment) 233- Test scenarios: expired access token, expired refresh token, network errors 234- Edge cases: malformed responses, missing credentials, unlinked books 235 236## Future Enhancements (Not Implemented) 237 238Potential features intentionally left out for simplicity: 239 240- **Automatic book detection via ISBN**: Query ISBN databases to automatically match books instead of manual linking 241- **Batch syncing multiple books**: Sync progress for all linked books at once 242- **Conflict resolution for concurrent edits**: Handle cases where book data is modified from multiple clients 243- **Offline queue for syncing**: Queue progress updates when network unavailable and sync when connection restored 244- **Automatic list URI updates**: When changing a book's reading status (e.g., to "currently_reading_books"), automatically fetch and update the `listUri` field to point to the correct list. Currently commented out in code - would require additional `listRecords` call to fetch the user's lists and find the matching URI. 245- **Retry logic for transient network failures**: Automatically retry failed requests due to temporary network issues 246- **Progress indicators for long-running operations**: Show loading spinners or progress bars for network requests 247 248## Files 249 250- `main.lua` - Main plugin with UI and KOReader integration (446 lines) 251- `xrpc.lua` - XRPC client for AT Protocol communication (353 lines) 252- `_meta.lua` - KOReader plugin manifest (6 lines) 253- `README.md` - User-facing documentation with installation and usage instructions 254- `CLAUDE.md` - This technical documentation for developers and maintainers 255 256## Troubleshooting 257 258### Common Issues 259 260**"Failed to resolve PDS" error** 261- Verify handle is correct (e.g., "user.bsky.social") 262- Check internet connection 263- Confirm Slingshot service (slingshot.microcosm.blue) is accessible 264 265**"Authentication failed" error** 266- Ensure app password is correct (not your main account password) 267- Create a new app password if needed from your account settings 268- Verify handle format matches your AT Protocol identifier 269 270**"Failed to fetch books: No books found"** 271- Books must be added to Popfeed/Paperbnd first before linking 272- Books must be in a "currently_reading_books" list 273- Use the Popfeed website or app to add books to your account 274 275**"Failed to sync progress" error** 276- Check that the book is still linked (may have been unlinked) 277- Verify internet connection 278- Token renewal should happen automatically, but if persisting, try re-entering credentials 279 280**Book not appearing in link list** 281- Ensure book has `creativeWorkType` set to "book" 282- Verify book is in "currently_reading_books" list type 283- Try unlinking and re-linking the book 284 285### Debug Tips 286 287- Check KOReader logs for detailed error messages 288- Verify credentials are saved by checking settings file at `<KOReader settings dir>/paperbnd.lua` 289- Test network connectivity with other KOReader plugins 290- Ensure document has page count metadata (required for progress percentage) 291 292## Development 293 294The plugin was developed collaboratively with Claude (Anthropic's AI assistant) with these priorities: 295- Clean, readable code 296- Comprehensive error handling 297- User experience focused on simplicity 298- No emojis in documentation or messages 299- Direct communication style in code comments 300- Proactive reliability through startup validation