diff --git a/.gitignore b/.gitignore index 1e4c791..29f4e84 100644 --- a/.gitignore +++ b/.gitignore @@ -89,7 +89,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/Makefile b/Makefile index 6428d1a..fbb802a 100755 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -version = 0.5.1-beta +version = 0.5.2-beta zip_file = releases/KOReader Sync v$(version).zip zip_contents = about.txt LICENSE plugin-import-name-koreader.txt *.py *.md images/*.png diff --git a/README.md b/README.md index 867c915..7a17102 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ A calibre plugin to synchronize metadata from KOReader to calibre. -[KOReader](https://koreader.rocks/) creates sidecar files that hold read progress and annotations. This plugin reads the data from those sidecar files and updates calibre's metadata based on them. It is inspired by [the Kobo Utilities plugin](https://www.mobileread.com/forums/showthread.php?t=215339), that synchronizes reading progress between the original Kobo firmware (“Nickel”) and custom columns in calibre. +[KOReader](https://koreader.rocks/) creates sidecar files that hold read progress and annotations. This plugin reads the data from those sidecar files and updates calibre's metadata based on them. It is inspired by [the Kobo Utilities plugin](https://www.mobileread.com/forums/showthread.php?t=215339), that synchronizes reading progress between the original Kobo firmware ("Nickel") and custom columns in calibre. -Note that at the moment the sync is primarily one-way—from the KOReader device to calibre — and only works for USB and [wireless](https://github.com/koreader/koreader/wiki/Calibre-wireless-connection) devices. For the latter, you'll need [KOReader 2021.04 or newer](https://github.com/koreader/koreader/releases). +Note that at the moment the sync is primarily one-way—from the KOReader device to calibre, and only works for USB and [wireless](https://github.com/koreader/koreader/wiki/Calibre-wireless-connection) devices. For the latter, you'll need [KOReader 2021.04 or newer](https://github.com/koreader/koreader/releases). -Pushing metadata from Calibre to KOReader currently works only for books which do not have KOReader sidecar files, and of course requires the raw metadata column to be mapped. The use-case is for e.g. setting up a new device, or if a book was removed from your device and you've now added it back. This has been tested for Calibre's Connect to Folder and Custom USB Device modes. It does not seem to work for the Kobo Touch device driver nor with wireless connections, but I (@charlesangus) find those don't communicate perfectly with Calibre/KOReader in any case... I haven't disabled it for other devices - it may be a quirk in my setup which is causing it to fail, and it may work fine for you. +Pushing metadata from calibre to KOReader currently works only for books which do not have KOReader sidecar files, and of course requires the raw metadata column to be mapped. The use-case is for setting up a new device, or if a book was removed from your device and you've now added it back. This has been tested for calibre's Connect to Folder and Custom USB Device modes. It does not seem to work for the Kobo Touch device driver nor with wireless connections, but I (@charlesangus) find those don't communicate perfectly with Calibre/KOReader in any case... I haven't disabled it for other devices - it may be a quirk in my setup which is causing it to fail, and it may work fine for you. Releases will also be uploaded to [this plugin thread on the MobileRead Forums](https://www.mobileread.com/forums/showthread.php?p=4060141). If you are on there as well, please let me know what you think of the plugin in that thread. @@ -20,35 +20,38 @@ Releases will also be uploaded to [this plugin thread on the MobileRead Forums]( ### Download and install -1. Go to your calibre's “Preferences” > “Plugins” > “Get new plugins” and search for “KOReader Sync” -2. Click “Install” +1. Go to your calibre's _Preferences_ > _Plugins_ > _Get new plugins_ and search for _KOReader Sync_ +2. Click _Install_ 3. Restart calibre #### Alternatively 1. Download the latest release from [here](https://github.com/harmtemolder/koreader-calibre-plugin/tree/main/releases). -2. Go to your calibre's “Preferences” > “Plugins” > “Load plugin from file” and point it to the downloaded ZIP file +2. Go to your calibre's _Preferences_ > _Plugins_ > _Load plugin from file_ and point it to the downloaded ZIP file 3. Restart calibre ### Setup 1. Pick and choose the metadata you would like to sync and create the appropriate columns in calibre. These are your options: - - A “Floating point numbers” column to store the **current percent read**, with “Format for numbers” set to `{:.0%}`. - - An “Integers” column to store the **current percent read**. - - A regular “Text” column to store the **location you last stopped reading at**. - - A “Rating” column to store your **rating** of the book, as entered on the book's status page. - - A “Long text” column to store your **review** of the book, as entered on the book's status page. - - A “Long text” column to store your **bookmarks and highlights** of the book, with “Interpret this column as” set to “Plain text formatted using markdown”. (Highlights are an unordered list with their metadata in an HTML comment.) - - A regular “Text” column to store the **reading status** of the book, as entered on the book status page (“Finished”, “Reading”, “On hold”). - - A “Date” column to store **the date on which the first highlight or bookmark was made**. (This is probably around the time you started reading.) - - A “Date” column to store **the date on which the last highlight or bookmark was made**. (This is probably around the time you finished reading.) - - A regular “Text” column to store the **MD5 hash** KOReader uses to sync progress to a [**KOReader Sync Server**](https://github.com/koreader/koreader-sync-server#koreader-sync-server). (“Progress sync” in the KOReader app.) This might allow for syncing progress to calibre without having to connect your KOReader device, in the future. - - A “Long text” column to store the **raw contents of the metadata sidecar**, with “Interpret this column as” set to “Plain text”. This is required to sync metadata back to KOReader sidecars. -10. Add “KOReader Sync” to “main toolbar when a device is connected”, if it isn't there already. -11. Right-click the “KOReader Sync” icon and “Configure”. + - A _Floating point numbers_ column to store the **current percent read**, with _Format for numbers_ set to `{:.0%}`. + - An _Integers_ column to store the **current percent read**. + - A regular _Text_ column to store the **location you last stopped reading at**. + - A _Rating_ column to store your **rating** of the book, as entered on the book's status page. + - A _Long text_ column to store your **review** of the book, as entered on the book's status page. + - A regular _Text_ column to store the **reading status** of the book, as entered on the book status page (_Finished_, _Reading_, _On hold_). + - A _Yes/No_ column to store the **reading status** of the book, as a boolean (_Yes_ = _Finished_, _No_ = everything else). + - A _Date_ column to store **the date on which the first highlight or bookmark was made**. (This is probably around the time you started reading.) + - A _Date_ column to store **the date on which the last highlight or bookmark was made**. (This is probably around the time you finished reading.) + - A _Long text_ column to store your **bookmarks and highlights** of the book, with _Interpret this column as_ set to _Plain text formatted using markdown_. (Highlights are an unordered list with their metadata in an HTML comment.) + - A regular _Text_ column to store the **MD5 hash** KOReader uses to sync progress to a [**KOReader Sync Server**](https://github.com/koreader/koreader-sync-server#koreader-sync-server). (_Progress sync_ in the KOReader app.) This might allow for syncing progress to calibre without having to connect your KOReader device, in the future. + - A _Date_ column to store **when the last sync was performed**. + - A _Date_ column to store **when the sidecar file was last modified**.', + - A _Long text_ column to store the **contents of the metadata sidecar** as JSON, with _Interpret this column as_ set to _Plain text_. This is required to sync metadata back to KOReader sidecars. +10. Add _KOReader Sync_ to _main toolbar when a device is connected_, if it isn't there already. +11. Right-click the _KOReader Sync_ icon and _Configure_. 12. Map the metadata you want to sync to the newly created calibre columns. -13. Click “OK” to save your mapping. -14. From now on just click the “KOReader Sync” icon to sync all mapped metadata for all books on the connected device to calibre. +13. Click _OK_ to save your mapping. +14. From now on just click the _KOReader Sync_ icon to sync all mapped metadata for all books on the connected device to calibre. ### Things to consider @@ -89,7 +92,7 @@ If you encounter any issues with the plugin, please submit them [here](https://g ### Notes & Tips - My first attempt was actually to sync calibre with KOReader's read progress through the progress sync plugin and a [sync server](https://github.com/koreader/koreader-sync-server). Read [here](https://github.com/koreader/koreader/issues/6399#issuecomment-721826362) why that did not work. This plugin might actually make that possible now by allowing you to store KOReader's MD5 hash in calibre... -- calibre allows you to auto-connect to a folder device on boot, which greatly speeds up your workflow when testing. You can find this under “Preferences” > “Tweaks”, search for `auto_connect_to_folder`. Point that to the `dummy_device` folder in this repository. (I have included royalty free EPUBs for your and my convenience.) +- calibre allows you to auto-connect to a folder device on boot, which greatly speeds up your workflow when testing. You can find this under "Preferences" > "Tweaks", search for `auto_connect_to_folder`. Point that to the `dummy_device` folder in this repository. (I have included royalty free EPUBs for your and my convenience.) - If you're testing and don't actually want to update any metadata, set `DRY_RUN` to `True` in `__init__.py`. - I work in PyCharm, which offers a remote debugging server. To enable that in this plugin, set `PYDEVD` to `True` in `__init__.py`.You might need to change `sys.path.append` in `action.py`. - The supported device drivers can be found in [the `SUPPORTED_DEVICES` list in `config.py`](https://github.com/harmtemolder/koreader-calibre-plugin/blob/main/config.py#L30). Adding a new type here is the first step to adding support, but make sure all features are tested thoroughly before releasing a version with an added device @@ -110,7 +113,7 @@ make zip load ### Building a release -Make sure you have the dependencies and have set the correct version number in `__init__.py`, `pluginIndexKOReaderSync.txt` and `Makefile`. Also update [Changelog](#Changelog). Then: +Make sure you have the dependencies and have set the correct version number in `__init__.py`, `pluginIndexKOReaderSync.txt` and `Makefile`. Also update [Changelog](#changelog). Then: ```shell make zip @@ -136,22 +139,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] -## [0.5.2-beta] - ? +## [0.5.2-beta] - 2023-05-22 + +- Many thanks to @elmodor and igorius for their help! ### Added - Added config option to only sync if the metadata is newer than the data stored in calibre (will fallback to "Percent read column" if no "Date Modified column" exists or can not be obtained) - Added config option to not sync if the book has already been marked as finished (via "Percent read column" or "Reading status column") -- Added a yes/no column for read status (based on changes from igorius at mobilread) +- Added a yes/no column for read status (based on [changes from igorius at MobileRead](https://www.mobileread.com/forums/showpost.php?p=4323088&postcount=90)) ### Changed - Pylint cleanup +- Update README to match new columns +- Update dummy device and library to match new columns ### Fixed - Fixed crash for wireless connected devices while trying to get the "Date Modified column" value -- Fixed setting correct sync status for `column_status` if no status is send from koreader +- Fixed setting correct sync status for `column_status` if no status is sent from KOReader ## [0.5.1-beta] - 2022-12-27 @@ -326,5 +333,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), [0.3.1-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/blob/main/releases/KOReader%20Sync%20v0.3.1-beta.zip [0.3.2-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/blob/main/releases/KOReader%20Sync%20v0.3.2-beta.zip [0.4.0-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/blob/main/releases/KOReader%20Sync%20v0.4.0-beta.zip -[0.4.1-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/blob/main/releases/KOReader%20Sync%20v0.4.1-beta.zip +[0.4.1-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/releases/tag/v0.4.1-beta +[0.5.0-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/releases/tag/v0.5.0-beta +[0.5.1-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/releases/tag/v0.5.1-beta +[0.5.2-beta]: https://github.com/harmtemolder/koreader-calibre-plugin/releases/tag/v0.5.2-beta [unreleased]: https://github.com/harmtemolder/koreader-calibre-plugin diff --git a/__init__.py b/__init__.py index 20b4277..9e8c5f8 100644 --- a/__init__.py +++ b/__init__.py @@ -31,9 +31,9 @@ class KoreaderSync(InterfaceActionBase): name = 'KOReader Sync' - description = 'Get metadata from a locally connected KOReader device ' + description = 'Get metadata from a connected KOReader device' author = 'harmtemolder' - version = (0, 5, 1) + version = (0, 5, 2) minimum_calibre_version = (5, 0, 1) # Because Python 3 config = JSONConfig(os.path.join('plugins', 'KOReader Sync.json')) actual_plugin = 'calibre_plugins.koreader.action:KoreaderAction' diff --git a/action.py b/action.py index ec15fcc..d1f1993 100644 --- a/action.py +++ b/action.py @@ -38,7 +38,7 @@ from calibre.constants import numeric_version __license__ = 'GNU GPLv3' -__copyright__ = '2021, harmtemolder ' +__copyright__ = '2023, harmtemolder ' __docformat__ = 'restructuredtext en' if numeric_version >= (5, 5, 0): @@ -65,7 +65,7 @@ class KoreaderAction(InterfaceAction): name = KoreaderSync.name - action_spec = (name, 'copy-to-library.png', KoreaderSync.description, None) + action_spec = (name, 'edit-redo.png', KoreaderSync.description, None) action_add_menu = True action_menu_clone_qaction = 'Sync from KOReader' dont_add_to = frozenset( @@ -96,12 +96,12 @@ def genesis(self): # Right-click menu (already includes left-click action) self.create_menu_action( self.qaction.menu(), - 'Sync Missing Sidecars to KOReader', - 'Sync Missing Sidecars to KOReader', - icon='config.png', - description='Where Calibre has a raw metadata entry but KOReader ' - 'does not have a sidecar file, push the metadata from Calibre ' - 'to a new sidecar file.', + 'Sync missing to KOReader', + 'Sync missing to KOReader', + icon='edit-undo.png', + description='If calibre has an entry in the "Raw sidecar column", ' + 'but KOReader does not have a sidecar file, push the ' + 'metadata from calibre to a new sidecar file.', triggered=self.sync_missing_sidecars_to_koreader ) @@ -123,7 +123,7 @@ def genesis(self): 'Readme for KOReader Sync', 'Readme', icon='dialog_question.png', - description='About KOReader Sync', + description='Readme for KOReader Sync', triggered=self.show_readme ) @@ -143,8 +143,7 @@ def show_readme(self): debug_print = partial(module_debug_print, 'KoreaderAction:show_readme:') debug_print('start') readme_url = QUrl( - 'https://git.sr.ht/~harmtemolder/koreader-calibre' - '-plugin#koreader-calibre-plugin' + 'https://github.com/harmtemolder/koreader-calibre-plugin#readme' ) open_url(readme_url) @@ -583,7 +582,7 @@ def sync_missing_sidecars_to_koreader(self): 'KoreaderAction:sync_missing_sidecars_to_koreader:' ) - if CONFIG["column_sidecar"] is '': + if CONFIG["column_sidecar"] == '': error_dialog( self.gui, 'Failure', @@ -618,7 +617,7 @@ def sync_missing_sidecars_to_koreader(self): num_fail = 0 for book_uuid, path in sidecar_paths_not_exist.items(): result, details = self.push_metadata_to_koreader_sidecar(book_uuid, path) - if result is "success": + if result == "success": num_success += 1 results.append( { @@ -627,7 +626,7 @@ def sync_missing_sidecars_to_koreader(self): 'sidecar_path': path, } ) - elif result is "failure": + elif result == "failure": num_fail += 1 results.append( { @@ -636,7 +635,7 @@ def sync_missing_sidecars_to_koreader(self): 'sidecar_path': path, } ) - elif result is "no_metadata": + elif result == "no_metadata": num_no_metadata += 1 results.append( { diff --git a/config.py b/config.py index 88040ce..b6027fd 100644 --- a/config.py +++ b/config.py @@ -49,29 +49,29 @@ COLUMNS = [{ 'name': 'column_percent_read', 'label': 'Percent read column (float):', - 'tooltip': 'A “Floating point numbers” column to store the current\n' - 'percent read, with “Format for numbers” set to `{:.0%}`.', + 'tooltip': 'A "Floating point numbers" column to store the current\n' + 'percent read, with "Format for numbers" set to `{:.0%}`.', 'type': 'float', 'sidecar_property': ['percent_finished'], 'transform': (lambda value: float(value)) }, { 'name': 'column_percent_read_int', 'label': 'Percent read column (int):', - 'tooltip': 'An “Integers” column to store the current percent read.', + 'tooltip': 'An "Integers" column to store the current percent read.', 'type': 'int', 'sidecar_property': ['percent_finished'], 'transform': (lambda value: math.floor(float(value) * 100)) }, { 'name': 'column_last_read_location', 'label': 'Last read location column:', - 'tooltip': 'A regular “Text” column to store the location you last\n' + 'tooltip': 'A regular "Text" column to store the location you last\n' 'stopped reading at.', 'type': 'text', 'sidecar_property': ['last_xpointer'], }, { 'name': 'column_rating', 'label': 'Rating column:', - 'tooltip': 'A “Rating” column to store your rating of the book,\n' + 'tooltip': 'A "Rating" column to store your rating of the book,\n' 'as entered on the book’s status page.', 'type': 'rating', 'sidecar_property': ['summary', 'rating'], @@ -79,31 +79,30 @@ }, { 'name': 'column_review', 'label': 'Review column:', - 'tooltip': 'A “Long text” column to store your review of the book,\n' + 'tooltip': 'A "Long text" column to store your review of the book,\n' 'as entered on the book’s status page.', 'type': 'comments', 'sidecar_property': ['summary', 'note'], }, { 'name': 'column_status', 'label': 'Reading status column (text):', - 'tooltip': 'A regular “Text” column to store the reading status of the\n' - 'book, as entered on the book status page (“Finished”,\n' - '“Reading”, “On hold”).', + 'tooltip': 'A regular "Text" column to store the reading status of the\n' + 'book, as entered on the book status page ("Finished",\n' + '"Reading", "On hold").', 'type': 'text', 'sidecar_property': ['summary', 'status'], }, { 'name': 'column_status_bool', 'label': 'Reading status column (yes/no):', - 'tooltip': 'A regular “yes/no” column to store the reading status of the\n' - 'book, as entered on the book status page (“complete”,\n' - '“reading”).', + 'tooltip': 'A "Yes/No" column to store the reading status of the book,\n' + 'as a boolean ("Yes" = "Finished", "No" = everything else).', 'type': 'bool', 'sidecar_property': ['summary', 'status'], 'transform': (lambda val: bool(val == 'complete')), }, { 'name': 'column_date_first_bookmark', 'label': 'First bookmark date column:', - 'tooltip': 'A “Date” column to store the date on which the first\n' + 'tooltip': 'A "Date" column to store the date on which the first\n' 'highlight or bookmark was made. (This is probably\n' 'around the time you started reading.)', 'type': 'datetime', @@ -111,7 +110,7 @@ }, { 'name': 'column_date_last_bookmark', 'label': 'Last bookmark date column:', - 'tooltip': 'A “Date” column to store the date on which the last\n' + 'tooltip': 'A "Date" column to store the date on which the last\n' 'highlight or bookmark was made. (This is probably\n' 'around the time you finished reading.)', 'type': 'datetime', @@ -119,7 +118,7 @@ }, { 'name': 'column_bookmarks', 'label': 'Bookmarks column', - 'tooltip': 'A “Long text” column to store your bookmarks and\n' + 'tooltip': 'A "Long text" column to store your bookmarks and\n' 'highlights.', 'type': 'comments', 'sidecar_property': ['bookmarks'], @@ -127,8 +126,8 @@ }, { 'name': 'column_md5', 'label': 'MD5 hash column:', - 'tooltip': 'A regular “Text” column to store the MD5 hash KOReader uses\n' - 'to sync progress to a KOReader Sync Server. (“Progress sync”\n' + 'tooltip': 'A regular "Text" column to store the MD5 hash KOReader uses\n' + 'to sync progress to a KOReader Sync Server. ("Progress sync"\n' 'in the KOReader app.) This might allow for syncing progress\n' 'to calibre without having to connect your KOReader device,\n' 'in the future.', @@ -149,9 +148,9 @@ }, { 'name': 'column_sidecar', 'label': 'Raw sidecar column:', - 'tooltip': 'A “Long text” column to store the contents of the\n' - 'metadata sidecar as JSON, with “Interpret this column as” set to\n' - '“Plain text”.', + 'tooltip': 'A "Long text" column to store the contents of the\n' + 'metadata sidecar as JSON, with "Interpret this column as" set to\n' + '"Plain text". This is required to sync metadata back to KOReader sidecars.', 'type': 'comments', 'sidecar_property': [], # `[]` gives the entire sidecar dict 'transform': (lambda d: json.dumps( @@ -165,13 +164,13 @@ CHECKBOXES = [{ 'name': 'checkbox_sync_if_more_recent', 'label': 'Sync only if changes are more recent:', - 'tooltip': 'Only sync book only if the metadata is more recent.\n' - 'Requires "Date Modified Column" or "Percent read column" to be synced', + 'tooltip': 'Sync book only if the metadata is more recent. Requires\n' + '"Date Modified Column" or "Percent read column" to be synced', }, { 'name': 'checkbox_no_sync_if_finished', 'label': 'No sync if book has already been finished:', - 'tooltip': 'Does not sync book if it has already been finished.\n' - 'Requires "Percent read column" or "Reading status column" to be synced', + 'tooltip': 'Do not sync book if it has already been finished. Requires\n' + '"Percent read column" or "Reading status column" to be synced', }] CONFIG = JSONConfig(os.path.join('plugins', 'KOReader Sync.json')) diff --git a/pluginIndexKOReaderSync.txt b/pluginIndexKOReaderSync.txt index 0e2e758..5399264 100644 --- a/pluginIndexKOReaderSync.txt +++ b/pluginIndexKOReaderSync.txt @@ -1,4 +1,4 @@ [*][URL="https://www.mobileread.com/forums/showthread.php?p=4060545"]KOReader Sync[/URL] [I]Synchronize metadata (e.g. read progress and rating) from KOReader to calibre. -Version: 0.5.1; Released: 27-12-2022; Calibre: 5.0.1; Author: harmtemolder; History: Yes; +Version: 0.5.2; Released: 22-05-2023; Calibre: 5.0.1; Author: harmtemolder; History: Yes; Platforms: Windows, OSX, Linux;[/I] diff --git a/releases/KOReader Sync v0.5-beta.zip b/releases/KOReader Sync v0.5.0-beta.zip similarity index 100% rename from releases/KOReader Sync v0.5-beta.zip rename to releases/KOReader Sync v0.5.0-beta.zip diff --git a/releases/KOReader Sync v0.5.2-beta.zip b/releases/KOReader Sync v0.5.2-beta.zip new file mode 100644 index 0000000..834d950 Binary files /dev/null and b/releases/KOReader Sync v0.5.2-beta.zip differ