/// Represents reading progress data from a KOReader sync server class KoreaderProgress { /// MD5 hash identifying the document final String document; /// Reading progress percentage (0.0 to 1.0) final double percentage; /// Position string (e.g., CFI for EPUB, page number for PDF) final String progress; /// Device name that last updated the progress final String device; /// Device identifier final String? deviceId; /// Unix timestamp (seconds) of last update final int? timestamp; const KoreaderProgress({ required this.document, required this.percentage, required this.progress, required this.device, this.deviceId, this.timestamp, }); /// Create from JSON response factory KoreaderProgress.fromJson(Map json) { return KoreaderProgress( document: json['document'] as String? ?? '', percentage: (json['percentage'] as num?)?.toDouble() ?? 0.0, progress: json['progress'] as String? ?? '', device: json['device'] as String? ?? '', deviceId: json['device_id'] as String?, timestamp: json['timestamp'] as int?, ); } /// Convert to JSON for API request Map toJson() { return { 'document': document, 'percentage': percentage, 'progress': progress, 'device': device, if (deviceId != null) 'device_id': deviceId, }; } /// Whether this progress has meaningful data bool get isEmpty => document.isEmpty; /// Convert timestamp to DateTime DateTime? get lastUpdated => timestamp != null ? DateTime.fromMillisecondsSinceEpoch(timestamp! * 1000) : null; }