diff --git a/apps/readest.koplugin/main.lua b/apps/readest.koplugin/main.lua index f59712ce..6eb7a9f6 100644 --- a/apps/readest.koplugin/main.lua +++ b/apps/readest.koplugin/main.lua @@ -273,6 +273,69 @@ function ReadestSync:logout(menu) }) end +function normalizeIdentifier(identifier) + if identifier:match("urn:") then + -- Slice after the last ':' + return identifier:match("([^:]+)$") + elseif identifier:match(":") then + -- Slice after the first ':' + return identifier:match("^[^:]+:(.+)$") + end + return identifier +end + +function normalizeAuthor(author) + -- Trim leading and trailing whitespace + author = author:gsub("^%s*(.-)%s*$", "%1") + return author +end + +function ReadestSync:generateMetadataHash() + local doc_props = self.ui.doc_settings:readSetting("doc_props") or {} + local title = doc_props.title or '' + if title == '' then + local doc_path, filename = util.splitFilePathName(self.ui.doc_settings:readSetting("doc_path") or '') + local basename, suffix = util.splitFileNameSuffix(filename) + title = basename or '' + end + + local authors = doc_props.authors or '' + if authors:find("\n") then + authors = util.splitToArray(authors, "\n") + for i, author in ipairs(authors) do + authors[i] = normalizeAuthor(author) + end + authors = table.concat(authors, ",") + else + authors = normalizeAuthor(authors) + end + + local identifiers = doc_props.identifiers or '' + if identifiers:find("\n") then + identifiers = util.splitToArray(identifiers, "\n") + for i, id in ipairs(identifiers) do + identifiers[i] = normalizeIdentifier(id) + end + identifiers = table.concat(identifiers, ",") + else + identifiers = normalizeIdentifier(identifiers) + end + local doc_meta = title .. "|" .. authors .. "|" .. identifiers + local meta_hash = sha2.md5(doc_meta) + return meta_hash +end + +function ReadestSync:getMetaHash() + local doc_readest_sync = self.ui.doc_settings:readSetting("readest_sync") or {} + local meta_hash = doc_readest_sync.meta_hash + if not meta_hash then + meta_hash = self:generateMetadataHash() + doc_readest_sync.meta_hash = meta_hash + self.ui.doc_settings:saveSetting("readest_sync", doc_readest_sync) + end + return meta_hash +end + function ReadestSync:getDocumentIdentifier() return self.ui.doc_settings:readSetting("partial_md5_checksum") end @@ -325,7 +388,8 @@ end function ReadestSync:getCurrentBookConfig() local book_hash = self:getDocumentIdentifier() - if not book_hash then + local meta_hash = self:getMetaHash() + if not book_hash or not meta_hash then UIManager:show(InfoMessage:new{ text = _("Cannot identify the current book"), timeout = 2, @@ -335,6 +399,7 @@ function ReadestSync:getCurrentBookConfig() local config = { bookHash = book_hash, + metaHash = meta_hash, progress = "", xpointer = "", updatedAt = os.time() * 1000, @@ -437,7 +502,8 @@ function ReadestSync:pullBookConfig(interactive) end local book_hash = self:getDocumentIdentifier() - if not book_hash then return end + local meta_hash = self:getMetaHash() + if not book_hash or not meta_hash then return end if NetworkMgr:willRerunWhenOnline(function() self:pullBookConfig(interactive) end) then return @@ -468,6 +534,7 @@ function ReadestSync:pullBookConfig(interactive) since = 0, type = "configs", book = book_hash, + meta_hash = meta_hash, }, function(success, response) if not success then diff --git a/apps/readest.koplugin/readest-sync-api.json b/apps/readest.koplugin/readest-sync-api.json index b768fd13..bfff8b05 100644 --- a/apps/readest.koplugin/readest-sync-api.json +++ b/apps/readest.koplugin/readest-sync-api.json @@ -1,19 +1,19 @@ { - "base_url": "https://web.readest.com/api", - "name": "readest-sync-api", - "methods": { - "pullChanges": { - "path": "/sync", - "method": "GET", - "required_params": ["since", "type", "book"], - "expected_status": [200, 400, 301, 401, 403] - }, - "pushChanges": { - "path": "/sync", - "method": "POST", - "required_params": ["books", "notes", "configs"], - "payload": ["books", "notes", "configs"], - "expected_status": [200, 201, 301, 400, 401, 403] - } + "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"], + "payload": ["books", "notes", "configs"], + "expected_status": [200, 201, 301, 400, 401, 403] } + } } diff --git a/apps/readest.koplugin/readestsync.lua b/apps/readest.koplugin/readestsync.lua index 0ae89585..742fb86d 100644 --- a/apps/readest.koplugin/readestsync.lua +++ b/apps/readest.koplugin/readestsync.lua @@ -81,6 +81,7 @@ function ReadestSyncClient:pullChanges(params, callback) since = params.since, type = params.type, book = params.book, + meta_hash = params.meta_hash, }) end) if ok then diff --git a/apps/readest.koplugin/supabase-auth-api.json b/apps/readest.koplugin/supabase-auth-api.json index 60f94bb8..b7b3be90 100644 --- a/apps/readest.koplugin/supabase-auth-api.json +++ b/apps/readest.koplugin/supabase-auth-api.json @@ -1,33 +1,33 @@ { - "base_url": "https://readest.supabase.co/auth/v1/", - "name": "supabase-auth-api", - "methods": { - "sign_in_password": { - "path": "/token?grant_type=password", - "method": "POST", - "required_params": ["email", "password"], - "payload": ["email", "password"], - "expected_status": [200, 400, 401] - }, - "refresh_token": { - "path": "/token?grant_type=refresh_token", - "method": "POST", - "required_params": ["refresh_token"], - "payload": ["refresh_token"], - "expected_status": [200, 400, 401, 403] - }, - "sign_out": { - "path": "/logout", - "method": "POST", - "required_params": [], - "payload": [], - "expected_status": [204, 401] - }, - "get_user": { - "path": "/user", - "method": "GET", - "required_params": [], - "expected_status": [200, 401] - } + "base_url": "https://readest.supabase.co/auth/v1/", + "name": "supabase-auth-api", + "methods": { + "sign_in_password": { + "path": "/token?grant_type=password", + "method": "POST", + "required_params": ["email", "password"], + "payload": ["email", "password"], + "expected_status": [200, 400, 401] + }, + "refresh_token": { + "path": "/token?grant_type=refresh_token", + "method": "POST", + "required_params": ["refresh_token"], + "payload": ["refresh_token"], + "expected_status": [200, 400, 401, 403] + }, + "sign_out": { + "path": "/logout", + "method": "POST", + "required_params": [], + "payload": [], + "expected_status": [204, 401] + }, + "get_user": { + "path": "/user", + "method": "GET", + "required_params": [], + "expected_status": [200, 401] } + } }