kobo-highlights 1.0.2__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/PKG-INFO +6 -10
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/kobo_highlights/config.py +6 -5
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/kobo_highlights/functions.py +12 -12
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/kobo_highlights/main.py +11 -5
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/pyproject.toml +14 -16
- kobo_highlights-1.0.2/setup.py +0 -37
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/LICENSE +0 -0
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/README.md +0 -0
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/kobo_highlights/__init__.py +0 -0
- {kobo_highlights-1.0.2 → kobo_highlights-1.0.4}/kobo_highlights/console.py +0 -0
@@ -1,20 +1,16 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.3
|
2
2
|
Name: kobo-highlights
|
3
|
-
Version: 1.0.
|
3
|
+
Version: 1.0.4
|
4
4
|
Summary: Kobo Highlights is a CLI application to manage the bookmarks of your Kobo ereader. It can import them into a human-friendly markdown database.
|
5
|
-
Home-page: https://github.com/videbar/kobo-highlights
|
6
5
|
License: GPL-3.0-only
|
7
6
|
Author: Pedro Videgain Barranco
|
8
|
-
|
7
|
+
Author-email: pedro@vide.bar
|
8
|
+
Requires-Python: ~=3.10
|
9
9
|
Classifier: Environment :: Console
|
10
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
11
10
|
Classifier: Operating System :: POSIX :: Linux
|
12
|
-
Classifier: Programming Language :: Python :: 3
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
15
11
|
Classifier: Topic :: Multimedia
|
16
|
-
Requires-Dist: pydantic (>=
|
17
|
-
Requires-Dist: rich (>=12.
|
12
|
+
Requires-Dist: pydantic (>=2.0,<3.0)
|
13
|
+
Requires-Dist: rich (>=12.4.0,<12.5.0)
|
18
14
|
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
19
15
|
Requires-Dist: typer[all] (>=0.4.1,<0.5.0)
|
20
16
|
Description-Content-Type: text/markdown
|
@@ -15,7 +15,7 @@ but a valid `Config` object can't be created.
|
|
15
15
|
from __future__ import annotations
|
16
16
|
from pathlib import Path
|
17
17
|
|
18
|
-
from pydantic import BaseModel,
|
18
|
+
from pydantic import BaseModel, ValidationError
|
19
19
|
from rich.prompt import Prompt
|
20
20
|
from rich.table import Table
|
21
21
|
import toml
|
@@ -24,7 +24,7 @@ from kobo_highlights.console import console
|
|
24
24
|
|
25
25
|
|
26
26
|
# Definitions
|
27
|
-
class Config(BaseModel, extra=
|
27
|
+
class Config(BaseModel, extra="forbid"):
|
28
28
|
"""
|
29
29
|
Config: Class that holds the configuration of the program. It is based on pydantic
|
30
30
|
to ensure a correct structure and facilitate parsing. It used as a singleton in the
|
@@ -74,7 +74,7 @@ class Config(BaseModel, extra=Extra.forbid):
|
|
74
74
|
Config: `Config` object representing the config file.
|
75
75
|
"""
|
76
76
|
try:
|
77
|
-
return cls.
|
77
|
+
return cls.model_validate(toml.load(config_path))
|
78
78
|
|
79
79
|
except FileNotFoundError:
|
80
80
|
raise ConfigError(f"No config file was found in: {config_path}")
|
@@ -145,10 +145,11 @@ class Config(BaseModel, extra=Extra.forbid):
|
|
145
145
|
"""
|
146
146
|
# Convert the path attributes to strings:
|
147
147
|
toml_representation: dict[str, str] = {
|
148
|
-
field: str(path) for field, path in self.
|
148
|
+
field: str(path) for field, path in self.model_dump().items()
|
149
149
|
}
|
150
150
|
|
151
|
-
config_filepath.
|
151
|
+
config_filepath.parent.mkdir(parents=True, exist_ok=True)
|
152
|
+
config_filepath.write_text(toml.dumps(toml_representation), encoding="utf-8")
|
152
153
|
return self
|
153
154
|
|
154
155
|
def __rich__(self) -> Table:
|
@@ -49,7 +49,6 @@ def setup_missing_config(config_path: Path) -> Config:
|
|
49
49
|
|
50
50
|
error_console.print("[bold]No valid configuration file was found")
|
51
51
|
if Confirm.ask("would you like to create one?"):
|
52
|
-
|
53
52
|
try:
|
54
53
|
config = Config.create_interactively().save_file(config_path)
|
55
54
|
console.print(
|
@@ -122,7 +121,6 @@ def query_bookmarks_from_ereader(
|
|
122
121
|
all_bookmarks: list[Bookmark] = []
|
123
122
|
|
124
123
|
for bookmark_data in cursor_bookmark.execute(BM_QUERY):
|
125
|
-
|
126
124
|
current_bookmark: dict = {
|
127
125
|
"id": bookmark_data[0],
|
128
126
|
"text": bookmark_data[1].strip(),
|
@@ -146,13 +144,15 @@ def query_bookmarks_from_ereader(
|
|
146
144
|
|
147
145
|
# Query string used on the "content" table.
|
148
146
|
CONTENT_QUERY: str = (
|
149
|
-
|
147
|
+
"SELECT Title, Attribution FROM content WHERE ContentID = ?"
|
150
148
|
" AND ContentType = '6' LIMIT 1;"
|
151
149
|
)
|
152
150
|
|
153
151
|
# The same book can appear multiple times on the "content" table, but in order
|
154
152
|
# to retrieve the data there's no need to query more than one result.
|
155
|
-
content_query_result: tuple = cursor_content.execute(
|
153
|
+
content_query_result: tuple = cursor_content.execute(
|
154
|
+
CONTENT_QUERY, (volume_id,)
|
155
|
+
).fetchone()
|
156
156
|
|
157
157
|
current_bookmark["title"] = content_query_result[0]
|
158
158
|
|
@@ -188,9 +188,8 @@ def query_bookmark_ids_from_json(json_filepath: Path) -> list[Bookmark_id]:
|
|
188
188
|
list[Bookmark_id]: List of bookamrk IDs.
|
189
189
|
"""
|
190
190
|
try:
|
191
|
-
|
192
191
|
json_content: dict[str, list[Bookmark_id]] = json.loads(
|
193
|
-
json_filepath.read_text()
|
192
|
+
json_filepath.read_text(encoding="utf-8")
|
194
193
|
)
|
195
194
|
|
196
195
|
# The JSON file should correspond to a dictionary with the key
|
@@ -202,9 +201,8 @@ def query_bookmark_ids_from_json(json_filepath: Path) -> list[Bookmark_id]:
|
|
202
201
|
|
203
202
|
# If no JSON file exists, create one.
|
204
203
|
except FileNotFoundError:
|
205
|
-
|
206
204
|
json_content: dict[str, list[Bookmark_id]] = {"imported_bookmark_ids": []}
|
207
|
-
json_filepath.write_text(json.dumps(json_content))
|
205
|
+
json_filepath.write_text(json.dumps(json_content), encoding="utf-8")
|
208
206
|
|
209
207
|
# If there is a JSON file but the structure is wrong, the user will be asked if
|
210
208
|
# they want to create a new one.
|
@@ -221,7 +219,7 @@ def query_bookmark_ids_from_json(json_filepath: Path) -> list[Bookmark_id]:
|
|
221
219
|
console=error_console,
|
222
220
|
):
|
223
221
|
json_content: dict[str, list[Bookmark_id]] = {"imported_bookmark_ids": []}
|
224
|
-
json_filepath.write_text(json.dumps(json_content))
|
222
|
+
json_filepath.write_text(json.dumps(json_content), encoding="utf-8")
|
225
223
|
|
226
224
|
else:
|
227
225
|
raise typer.Abort()
|
@@ -242,7 +240,9 @@ def write_bookmark_id_to_json(json_filepath: Path, id: Bookmark_id):
|
|
242
240
|
if id not in stored_ids:
|
243
241
|
stored_ids.append(id)
|
244
242
|
|
245
|
-
json_filepath.write_text(
|
243
|
+
json_filepath.write_text(
|
244
|
+
json.dumps({"imported_bookmark_ids": stored_ids}), encoding="utf-8"
|
245
|
+
)
|
246
246
|
|
247
247
|
|
248
248
|
def add_bookmark_to_md(bookmark: Bookmark, md_dir: Path):
|
@@ -283,8 +283,8 @@ def add_bookmark_to_md(bookmark: Bookmark, md_dir: Path):
|
|
283
283
|
text_existing_file: str = f"\n\n***\n\n{md_blockquote}"
|
284
284
|
|
285
285
|
if md_filepath.is_file():
|
286
|
-
with md_filepath.open("a") as md_file:
|
286
|
+
with md_filepath.open("a", encoding="utf-8") as md_file:
|
287
287
|
md_file.write(text_existing_file)
|
288
288
|
|
289
289
|
else:
|
290
|
-
md_filepath.write_text(text_new_file)
|
290
|
+
md_filepath.write_text(text_new_file, encoding="utf-8")
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"""This module contains the typer app (the CLI app) and the definitions for all the
|
2
2
|
programs commands.
|
3
3
|
"""
|
4
|
+
|
4
5
|
# Imports:
|
5
6
|
from pathlib import Path
|
6
7
|
|
@@ -34,6 +35,7 @@ app = typer.Typer(
|
|
34
35
|
config_app = typer.Typer(help="Manage the program configuration.")
|
35
36
|
app.add_typer(config_app, name="config")
|
36
37
|
|
38
|
+
|
37
39
|
# Initial setup:
|
38
40
|
@app.callback()
|
39
41
|
def setup():
|
@@ -142,8 +144,8 @@ def import_highlights(
|
|
142
144
|
JSON_PATH
|
143
145
|
)
|
144
146
|
|
145
|
-
# Filter the
|
146
|
-
#
|
147
|
+
# Filter the bookmarks to import only those that are in the ereader but
|
148
|
+
# not on the markdown files.
|
147
149
|
bookmarks_to_save = [
|
148
150
|
bm for bm in ereader_bookmarks if bm["id"] not in md_bookmarks_ids
|
149
151
|
]
|
@@ -157,13 +159,14 @@ def import_highlights(
|
|
157
159
|
case "all":
|
158
160
|
for bookmark in ereader_bookmarks:
|
159
161
|
add_bookmark_to_md(bookmark, config.target_dir)
|
162
|
+
write_bookmark_id_to_json(JSON_PATH, bookmark["id"])
|
160
163
|
|
161
164
|
console.print("[cyan]All Bookmarks have been imported")
|
162
165
|
|
163
166
|
case _:
|
164
167
|
|
165
|
-
# If the value is neither all nor new, the program checks in order
|
166
|
-
#
|
168
|
+
# If the value is neither all nor new, the program checks in order to see if
|
169
|
+
# it is an id, a list of ids, a book title, or a book author.
|
167
170
|
|
168
171
|
all_ids, all_titles, all_authors = [], [], []
|
169
172
|
|
@@ -172,13 +175,14 @@ def import_highlights(
|
|
172
175
|
all_titles.append(bookmark["title"])
|
173
176
|
all_authors.append(bookmark["author"])
|
174
177
|
|
175
|
-
# Check first if
|
178
|
+
# Check first if the input is composed of ids:
|
176
179
|
selected_ids: list = bookmark_selection.split()
|
177
180
|
if all(id in all_ids for id in selected_ids):
|
178
181
|
|
179
182
|
for bookmark in ereader_bookmarks:
|
180
183
|
if bookmark["id"] in selected_ids:
|
181
184
|
add_bookmark_to_md(bookmark, config.target_dir)
|
185
|
+
write_bookmark_id_to_json(JSON_PATH, bookmark["id"])
|
182
186
|
|
183
187
|
# To print to the console:
|
184
188
|
id_list_to_print: str = "\n".join(selected_ids)
|
@@ -192,6 +196,7 @@ def import_highlights(
|
|
192
196
|
for bookmark in ereader_bookmarks:
|
193
197
|
if bookmark["title"] == bookmark_selection:
|
194
198
|
add_bookmark_to_md(bookmark, config.target_dir)
|
199
|
+
write_bookmark_id_to_json(JSON_PATH, bookmark["id"])
|
195
200
|
console.print(
|
196
201
|
f"[green]All bookmarks from the book {bookmark_selection} have"
|
197
202
|
" been imported"
|
@@ -202,6 +207,7 @@ def import_highlights(
|
|
202
207
|
for bookmark in ereader_bookmarks:
|
203
208
|
if bookmark["author"] == bookmark_selection:
|
204
209
|
add_bookmark_to_md(bookmark, config.target_dir)
|
210
|
+
write_bookmark_id_to_json(JSON_PATH, bookmark["id"])
|
205
211
|
console.print(
|
206
212
|
f"[green]All bookmarks from the Author {bookmark_selection} have"
|
207
213
|
" been imported"
|
@@ -1,12 +1,12 @@
|
|
1
|
-
[
|
1
|
+
[project]
|
2
2
|
name = "kobo-highlights"
|
3
|
-
version = "1.0.
|
3
|
+
version = "1.0.4"
|
4
4
|
description = """\
|
5
5
|
Kobo Highlights is a CLI application to manage the bookmarks of your Kobo ereader. \
|
6
6
|
It can import them into a human-friendly markdown database.\
|
7
7
|
"""
|
8
8
|
license = "GPL-3.0-only"
|
9
|
-
authors = ["Pedro Videgain Barranco"]
|
9
|
+
authors = [{ name = "Pedro Videgain Barranco", email = "pedro@vide.bar" }]
|
10
10
|
readme = "README.md"
|
11
11
|
homepage = "https://github.com/videbar/kobo-highlights"
|
12
12
|
classifiers = [
|
@@ -14,20 +14,18 @@ classifiers = [
|
|
14
14
|
"Operating System :: POSIX :: Linux",
|
15
15
|
"Topic :: Multimedia",
|
16
16
|
]
|
17
|
+
scripts = { kh = "kobo_highlights.main:app" }
|
18
|
+
requires-python = "~=3.10"
|
19
|
+
dependencies = [
|
20
|
+
"toml~=0.10.2",
|
21
|
+
"pydantic~=2.0",
|
22
|
+
"rich~=12.4.0",
|
23
|
+
"typer[all] (~=0.4.1)",
|
24
|
+
]
|
17
25
|
|
18
|
-
[tool.poetry.
|
19
|
-
|
20
|
-
|
21
|
-
[tool.poetry.dependencies]
|
22
|
-
python = "^3.10"
|
23
|
-
typer = {extras = ["all"], version = "^0.4.1"}
|
24
|
-
toml = "^0.10.2"
|
25
|
-
pydantic = "^1.9.0"
|
26
|
-
rich = "^12.2.0"
|
27
|
-
|
28
|
-
[tool.poetry.dev-dependencies]
|
29
|
-
pytest = "^7.1"
|
30
|
-
black = "^22.3.0"
|
26
|
+
[tool.poetry.group.dev]
|
27
|
+
dependencies = { pytest = "~=7.1", black = "~=22.3.0" }
|
28
|
+
optional = true
|
31
29
|
|
32
30
|
[build-system]
|
33
31
|
requires = ["poetry-core>=1.0.0"]
|
kobo_highlights-1.0.2/setup.py
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
from setuptools import setup
|
3
|
-
|
4
|
-
packages = \
|
5
|
-
['kobo_highlights']
|
6
|
-
|
7
|
-
package_data = \
|
8
|
-
{'': ['*']}
|
9
|
-
|
10
|
-
install_requires = \
|
11
|
-
['pydantic>=1.9.0,<2.0.0',
|
12
|
-
'rich>=12.2.0,<13.0.0',
|
13
|
-
'toml>=0.10.2,<0.11.0',
|
14
|
-
'typer[all]>=0.4.1,<0.5.0']
|
15
|
-
|
16
|
-
entry_points = \
|
17
|
-
{'console_scripts': ['kh = kobo_highlights.main:app']}
|
18
|
-
|
19
|
-
setup_kwargs = {
|
20
|
-
'name': 'kobo-highlights',
|
21
|
-
'version': '1.0.2',
|
22
|
-
'description': 'Kobo Highlights is a CLI application to manage the bookmarks of your Kobo ereader. It can import them into a human-friendly markdown database.',
|
23
|
-
'long_description': "**DISCLAIMER:** This is an unofficial project that I developed independently from Kobo\nand without any contact with them.\n\n# Kobo highlights \n\nKobo highlights is a simple CLI application to manage bookmarks from a Kobo erader. It\ncan import them into a markdown database where they can be easily accessed.\n\nKobo Highlights was developed in Python using [Typer](https://typer.tiangolo.com/) for\nthe CLI functionality and [Rich](https://github.com/Textualize/rich) for writing nice\ntext to the terminal.\n\nKobo highlights was develop as a personal project and it's design is based on how my\nparticular ereader handles bookmarks, so there are no guarantees that it will work on\nother models.\n\n# Requirements\n\nKobo highlights was developed and tested in [Fedora](https://getfedora.org/) and the\nautomatic tests are run in Ubuntu as macOS. I expect it to work properly on most\nLinux distributions and on macOS. It has not been testes on Windows, but you can try\nit.\n\nKobo highlights requires Python 3.10.\n\n# Installation\n\nThis project was developed using [Poetry](https://python-poetry.org/) and there are\nmultiple ways of installing it:\n\n* The recommended installation method is [downloading it from pypi](\n https://pypi.org/project/kobo-highlights/): `pip install kobo-highlights`.\n\n* If you want to install Kobo Highlights directly from this repo note that development\ntakes place in the main branch, so make sure to install it from the commit corresponding\nto the last release.\nIn this case you cant install it with Poetry (run `poetry install` inside the repo) or\nwith pip (run `pip install .` inside the repo).\n\n# Quick guide\n\nOnce Kobo Highlights has been installed, it can be accessed running the `kh` command.\nIf you run `kh --help` you should see something like this:\n\nFrom that message, you can see that the available options from Kobo Highlights are:\n\n* `kh config` to manage your configuration.\n\n* `kh ls` to list your bookmarks. By default is prints only new bookmarks, you can use\nthe `--all` option to print all bookmarks instead.\n\n* `kh import` to import your bookmarks. It can be called with `all`, `new` (default),\na bookmark ID, a list of bookmarks IDs, a book title or a book author. Use\n`kh import --help` for more information.\n\nYou can run any of these commands with the `--help` option to find out more on how to\nuse them.\n\nThe first time you run Kobo Highlights you will be probably told that you need to create\na configuration file before doing anything else. You can just follow the instructions\nto create a valid configuration file. In the next section the program configuration is\nexplained in more detail.\n\n# Configuration\n\nKobo Highlights uses a [toml](https://github.com/toml-lang/toml) configuration file that\nis stored in the default directory \n[designated by Typer](https://typer.tiangolo.com/tutorial/app-dir/). In most Linux\nsystems this is in `~/.config/kobo_highlights`. The configuration file contains two\nfields:\n\n* `ereader_dir`: Is the absolute path to the directory where your erader is mounter.\nNotably, your ereader doesn't need to be mounted when you create the config file,\nbut you should specify the directory where you expect it to be mounted when you manage\nyour bookmarks.\n\n* `target_dir`: Is the absolute path to the directory where you want your markdown\ndatabase to be created. This database will contain one markdown file per book with\nthe highlighted text stored in block quotes.\n\nEvert time you run Kobo Highlights it will try to find a configuration file, if it\nfails, it will ask you if you want to create one interactively and save it. If you\ndon't want to create a configuration file, Kobo Highlights will stop.\n\nYou can manage your configuration file with the `kh config` command. In particular\nyou cant use `config show` to show your current configuration and `config new` to\ncreate and save a new configuration.\n\n# The markdown database\n\nThe main goal of Kobo highlights is to read the bookmarks from the ereader and format\nthem in a way in which they are easy to work with. I choose to do this by creating\na markdown database of the bookmarks. This database is located in the directory\nspecified in the configuration and will have a markdown file per book. The names\nof these files follow the convention `<book title> - <book author(s)>.md`.\n\nThe markdown files will contain, for each bookmark in that book, a\n[markdown block quote](https://spec.commonmark.org/0.30/#block-quotes) that contains\nthe text highlighted in the ereader potentially followed by a set of paragraphs\ncontaining the possible annotations that were made about the highlighted text.\n\nNote that Kobo highlights by default only imports new bookmarks to the markdown\ndatabase. To determine if a bookmark is already in the database, Kobo highlights creates\na hidden JSON file inside the markdown directory. Inside this hidden file Kobo\nHighlights stores the IDs of the bookmarks that have already been imported. This means\nthat even if you modify the markdown files (or even delete the completely), Kobo\nHighlights will remember that bookmarks that you had imported and they will not be\nconsidered new.",
|
24
|
-
'author': 'Pedro Videgain Barranco',
|
25
|
-
'author_email': 'None',
|
26
|
-
'maintainer': 'None',
|
27
|
-
'maintainer_email': 'None',
|
28
|
-
'url': 'https://github.com/videbar/kobo-highlights',
|
29
|
-
'packages': packages,
|
30
|
-
'package_data': package_data,
|
31
|
-
'install_requires': install_requires,
|
32
|
-
'entry_points': entry_points,
|
33
|
-
'python_requires': '>=3.10,<4.0',
|
34
|
-
}
|
35
|
-
|
36
|
-
|
37
|
-
setup(**setup_kwargs)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|