karakeep-python-api 0.2.3__py3-none-any.whl → 1.1.0__py3-none-any.whl
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.
- karakeep_python_api/__main__.py +14 -12
- karakeep_python_api/datatypes.py +1 -0
- karakeep_python_api/karakeep_api.py +149 -62
- karakeep_python_api/openapi_reference.json +8 -1
- {karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/METADATA +22 -17
- karakeep_python_api-1.1.0.dist-info/RECORD +11 -0
- karakeep_python_api-0.2.3.dist-info/RECORD +0 -11
- {karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/WHEEL +0 -0
- {karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/entry_points.txt +0 -0
- {karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/top_level.txt +0 -0
karakeep_python_api/__main__.py
CHANGED
|
@@ -59,9 +59,9 @@ def serialize_output(data: Any) -> Any:
|
|
|
59
59
|
# Shared options for the API client
|
|
60
60
|
shared_options = [
|
|
61
61
|
click.option(
|
|
62
|
-
"--
|
|
63
|
-
envvar="
|
|
64
|
-
help="Full Karakeep API
|
|
62
|
+
"--api-endpoint",
|
|
63
|
+
envvar="KARAKEEP_PYTHON_API_ENDPOINT",
|
|
64
|
+
help="Full Karakeep API endpoint URL, including /api/v1/ (e.g., https://instance.com/api/v1/).",
|
|
65
65
|
),
|
|
66
66
|
click.option(
|
|
67
67
|
"--api-key",
|
|
@@ -151,7 +151,7 @@ def print_openapi_spec(ctx, param, value):
|
|
|
151
151
|
@click.pass_context
|
|
152
152
|
def cli(
|
|
153
153
|
ctx,
|
|
154
|
-
|
|
154
|
+
api_endpoint,
|
|
155
155
|
api_key,
|
|
156
156
|
verify_ssl,
|
|
157
157
|
verbose,
|
|
@@ -167,7 +167,7 @@ def cli(
|
|
|
167
167
|
# Ensure the context object exists
|
|
168
168
|
ctx.ensure_object(dict)
|
|
169
169
|
|
|
170
|
-
# --- Strict Check for API Key and
|
|
170
|
+
# --- Strict Check for API Key and Endpoint ---
|
|
171
171
|
# Check for API key (must be provided via arg or env)
|
|
172
172
|
resolved_api_key = api_key or os.environ.get("KARAKEEP_PYTHON_API_KEY")
|
|
173
173
|
if not resolved_api_key:
|
|
@@ -175,16 +175,18 @@ def cli(
|
|
|
175
175
|
"API Key is required. Provide --api-key option or set KARAKEEP_PYTHON_API_KEY environment variable."
|
|
176
176
|
)
|
|
177
177
|
|
|
178
|
-
# Check for
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
# Check for API endpoint (must be provided via arg or env)
|
|
179
|
+
resolved_api_endpoint = api_endpoint or os.environ.get(
|
|
180
|
+
"KARAKEEP_PYTHON_API_ENDPOINT"
|
|
181
|
+
)
|
|
182
|
+
if not resolved_api_endpoint:
|
|
181
183
|
raise click.UsageError(
|
|
182
|
-
"API
|
|
184
|
+
"API endpoint is required. Provide --api-endpoint option or set KARAKEEP_PYTHON_API_ENDPOINT environment variable. "
|
|
183
185
|
"The URL must include the API path, e.g., 'https://your-instance.com/api/v1/'."
|
|
184
186
|
)
|
|
185
187
|
|
|
186
188
|
# Store common API parameters in the context for commands to use
|
|
187
|
-
ctx.obj["
|
|
189
|
+
ctx.obj["API_ENDPOINT"] = resolved_api_endpoint # Store the resolved endpoint
|
|
188
190
|
ctx.obj["API_KEY"] = resolved_api_key # Store the resolved key
|
|
189
191
|
ctx.obj["VERIFY_SSL"] = verify_ssl
|
|
190
192
|
ctx.obj["VERBOSE"] = verbose
|
|
@@ -231,7 +233,7 @@ def create_click_command(
|
|
|
231
233
|
def command_func(ctx, **kwargs):
|
|
232
234
|
"""Dynamically generated command function wrapper."""
|
|
233
235
|
# Retrieve API parameters from context, ensuring API key is present now
|
|
234
|
-
|
|
236
|
+
api_endpoint = ctx.obj["API_ENDPOINT"]
|
|
235
237
|
api_key = ctx.obj["API_KEY"]
|
|
236
238
|
verify_ssl = ctx.obj["VERIFY_SSL"]
|
|
237
239
|
verbose = ctx.obj["VERBOSE"]
|
|
@@ -250,7 +252,7 @@ def create_click_command(
|
|
|
250
252
|
# Method generation already happened during inspection phase or initial load
|
|
251
253
|
api = KarakeepAPI(
|
|
252
254
|
api_key=api_key,
|
|
253
|
-
|
|
255
|
+
api_endpoint=api_endpoint,
|
|
254
256
|
verify_ssl=verify_ssl,
|
|
255
257
|
verbose=verbose,
|
|
256
258
|
disable_response_validation=disable_validation, # Pass flag to constructor
|
karakeep_python_api/datatypes.py
CHANGED
|
@@ -77,7 +77,7 @@ class KarakeepAPI:
|
|
|
77
77
|
|
|
78
78
|
Attributes:
|
|
79
79
|
api_key (str): The API key used for authentication.
|
|
80
|
-
|
|
80
|
+
api_endpoint (str): The endpoint of the Karakeep API instance, including /api/v1 (e.g., https://instance.com/api/v1/).
|
|
81
81
|
openapi_spec (dict): The parsed content of the OpenAPI specification file.
|
|
82
82
|
verify_ssl (bool): Whether SSL verification is enabled.
|
|
83
83
|
verbose (bool): Whether verbose logging is enabled.
|
|
@@ -85,17 +85,19 @@ class KarakeepAPI:
|
|
|
85
85
|
"""
|
|
86
86
|
|
|
87
87
|
# Version reflects the client library version, updated by bumpver
|
|
88
|
-
VERSION: str = "
|
|
88
|
+
VERSION: str = "1.1.0"
|
|
89
89
|
|
|
90
90
|
def __init__(
|
|
91
91
|
self,
|
|
92
92
|
api_key: Optional[str] = None,
|
|
93
|
-
|
|
93
|
+
api_endpoint: Optional[str] = None,
|
|
94
94
|
openapi_spec_path: Optional[str] = None, # Allow None, default handled below
|
|
95
95
|
verify_ssl: bool = True,
|
|
96
96
|
verbose: bool = False,
|
|
97
97
|
disable_response_validation: Optional[bool] = None,
|
|
98
|
-
rate_limit:
|
|
98
|
+
rate_limit: Union[
|
|
99
|
+
float, int
|
|
100
|
+
] = 0.0, # Minimum interval between API calls in seconds
|
|
99
101
|
):
|
|
100
102
|
"""
|
|
101
103
|
Initialize the Karakeep API client.
|
|
@@ -103,8 +105,9 @@ class KarakeepAPI:
|
|
|
103
105
|
Args:
|
|
104
106
|
api_key: Karakeep API key (Bearer token).
|
|
105
107
|
Defaults to KARAKEEP_PYTHON_API_KEY environment variable if not provided.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
+
api_endpoint: Override the base URL for the API. Must be provided either as an argument
|
|
109
|
+
or via the KARAKEEP_PYTHON_API_ENDPOINT environment variable.
|
|
110
|
+
Example: 'https://karakeep.domain.com/api/v1/'
|
|
108
111
|
openapi_spec_path: Path to the OpenAPI JSON specification file.
|
|
109
112
|
Defaults to 'openapi_reference.json' alongside the package code if not provided.
|
|
110
113
|
The loaded spec is available via the `openapi_spec` attribute.
|
|
@@ -129,46 +132,46 @@ class KarakeepAPI:
|
|
|
129
132
|
logger.debug("API Key loaded successfully.")
|
|
130
133
|
|
|
131
134
|
# --- Base URL Validation ---
|
|
132
|
-
|
|
135
|
+
env_endpoint = os.environ.get("KARAKEEP_PYTHON_API_ENDPOINT")
|
|
133
136
|
logger.debug(
|
|
134
|
-
f"Checked
|
|
137
|
+
f"Checked KARAKEEP_PYTHON_API_ENDPOINT environment variable, found: '{env_endpoint}'"
|
|
135
138
|
)
|
|
136
|
-
logger.debug(f"Base URL provided as argument: '{
|
|
139
|
+
logger.debug(f"Base URL provided as argument: '{api_endpoint}'")
|
|
137
140
|
|
|
138
|
-
if
|
|
139
|
-
self.
|
|
140
|
-
logger.info(f"Using provided base URL: {self.
|
|
141
|
-
elif
|
|
142
|
-
self.
|
|
141
|
+
if api_endpoint:
|
|
142
|
+
self.api_endpoint = api_endpoint
|
|
143
|
+
logger.info(f"Using provided base URL: {self.api_endpoint}")
|
|
144
|
+
elif env_endpoint:
|
|
145
|
+
self.api_endpoint = env_endpoint
|
|
143
146
|
logger.info(
|
|
144
|
-
f"Using base URL from
|
|
147
|
+
f"Using base URL from KARAKEEP_PYTHON_API_ENDPOINT: {self.api_endpoint}"
|
|
145
148
|
)
|
|
146
149
|
else:
|
|
147
|
-
# No
|
|
150
|
+
# No api_endpoint from arg or env var - raise error as per requirement
|
|
148
151
|
raise ValueError(
|
|
149
|
-
"API base URL is required. Provide '
|
|
152
|
+
"API base URL is required. Provide 'api_endpoint' argument or set KARAKEEP_PYTHON_API_ENDPOINT environment variable."
|
|
150
153
|
)
|
|
151
154
|
|
|
152
|
-
# Ensure base URL ends with /v1/
|
|
153
|
-
resolved_url = self.
|
|
154
|
-
if resolved_url.endswith("/v1"):
|
|
155
|
-
# Ends with /v1, needs a slash
|
|
156
|
-
self.
|
|
155
|
+
# Ensure base URL ends with /api/v1/
|
|
156
|
+
resolved_url = self.api_endpoint # Use a temporary variable for checks
|
|
157
|
+
if resolved_url.endswith("/api/v1"):
|
|
158
|
+
# Ends with /api/v1, needs a slash
|
|
159
|
+
self.api_endpoint = resolved_url + "/"
|
|
157
160
|
logger.info(
|
|
158
|
-
f"Appended trailing slash to base URL ending in /v1: {self.
|
|
161
|
+
f"Appended trailing slash to base URL ending in /api/v1: {self.api_endpoint}"
|
|
159
162
|
)
|
|
160
|
-
elif resolved_url.endswith("/v1/"):
|
|
163
|
+
elif resolved_url.endswith("/api/v1/"):
|
|
161
164
|
# Already ends correctly, do nothing
|
|
162
|
-
logger.debug(f"Base URL already ends with /v1/: {self.
|
|
165
|
+
logger.debug(f"Base URL already ends with /api/v1/: {self.api_endpoint}")
|
|
163
166
|
else:
|
|
164
|
-
# Doesn't end with /v1 or /v1/, append /v1/
|
|
165
|
-
# First, remove any existing trailing slash to avoid //v1/
|
|
167
|
+
# Doesn't end with /api/v1 or /api/v1/, append /api/v1/
|
|
168
|
+
# First, remove any existing trailing slash to avoid //api/v1/
|
|
166
169
|
if resolved_url.endswith("/"):
|
|
167
170
|
resolved_url = resolved_url[:-1]
|
|
168
|
-
self.
|
|
169
|
-
logger.info(f"Appended /v1/ to base URL: {self.
|
|
171
|
+
self.api_endpoint = resolved_url + "/api/v1/"
|
|
172
|
+
logger.info(f"Appended /api/v1/ to base URL: {self.api_endpoint}")
|
|
170
173
|
|
|
171
|
-
logger.debug(f"Final API Base URL after /v1/ check: {self.
|
|
174
|
+
logger.debug(f"Final API Base URL after /api/v1/ check: {self.api_endpoint}")
|
|
172
175
|
|
|
173
176
|
# --- Load and Parse OpenAPI Spec ---
|
|
174
177
|
if openapi_spec_path is None:
|
|
@@ -255,7 +258,7 @@ class KarakeepAPI:
|
|
|
255
258
|
# self.verbose is still used for conditional logging within the class methods.
|
|
256
259
|
|
|
257
260
|
logger.debug("KarakeepAPI client initialized.")
|
|
258
|
-
logger.debug(f" Base URL: {self.
|
|
261
|
+
logger.debug(f" Base URL: {self.api_endpoint}")
|
|
259
262
|
logger.debug(f" Verify SSL: {self.verify_ssl}")
|
|
260
263
|
logger.debug(f" Verbose: {self.verbose}")
|
|
261
264
|
logger.debug(
|
|
@@ -317,9 +320,9 @@ class KarakeepAPI:
|
|
|
317
320
|
AuthenticationError: If authentication fails (401).
|
|
318
321
|
APIError: For other HTTP errors or request issues.
|
|
319
322
|
"""
|
|
320
|
-
# Ensure endpoint doesn't start with / if
|
|
323
|
+
# Ensure endpoint doesn't start with / if endpoint ends with /
|
|
321
324
|
safe_endpoint = endpoint.lstrip("/")
|
|
322
|
-
url = urljoin(self.
|
|
325
|
+
url = urljoin(self.api_endpoint, safe_endpoint)
|
|
323
326
|
|
|
324
327
|
# Default headers
|
|
325
328
|
headers = {
|
|
@@ -905,15 +908,21 @@ class KarakeepAPI:
|
|
|
905
908
|
return None # Explicitly return None for 204
|
|
906
909
|
|
|
907
910
|
@optional_typecheck
|
|
908
|
-
def update_a_bookmark(
|
|
911
|
+
def update_a_bookmark(
|
|
912
|
+
self,
|
|
913
|
+
bookmark_id: str,
|
|
914
|
+
update_data: Dict[str, Any],
|
|
915
|
+
) -> Dict[str, Any]:
|
|
909
916
|
"""
|
|
910
917
|
Update a bookmark by its ID. Corresponds to PATCH /bookmarks/{bookmarkId}.
|
|
911
|
-
Allows updating
|
|
918
|
+
Allows updating various bookmark fields including metadata, content, and status.
|
|
912
919
|
|
|
913
920
|
Args:
|
|
914
921
|
bookmark_id: The ID (string) of the bookmark to update.
|
|
915
|
-
update_data:
|
|
916
|
-
|
|
922
|
+
update_data: Dictionary containing the fields to update. Supported keys include:
|
|
923
|
+
'title', 'archived', 'favourited', 'note', 'summary', 'createdAt',
|
|
924
|
+
'url', 'description', 'author', 'publisher', 'datePublished',
|
|
925
|
+
'dateModified', 'text', 'assetContent'.
|
|
917
926
|
|
|
918
927
|
Returns:
|
|
919
928
|
dict: A dictionary representing the updated bookmark (partial representation).
|
|
@@ -922,8 +931,13 @@ class KarakeepAPI:
|
|
|
922
931
|
Validation is not performed on this response type by default.
|
|
923
932
|
|
|
924
933
|
Raises:
|
|
934
|
+
ValueError: If update_data is empty or no valid fields are provided to update.
|
|
925
935
|
APIError: If the API request fails (e.g., 404 bookmark not found).
|
|
926
936
|
"""
|
|
937
|
+
# Ensure at least one field is being updated
|
|
938
|
+
if not update_data:
|
|
939
|
+
raise ValueError("update_data must contain at least one field to update.")
|
|
940
|
+
|
|
927
941
|
endpoint = f"bookmarks/{bookmark_id}"
|
|
928
942
|
response_data = self._call("PATCH", endpoint, data=update_data)
|
|
929
943
|
# The response schema is a subset of Bookmark, return as dict as specified in spec
|
|
@@ -979,7 +993,9 @@ class KarakeepAPI:
|
|
|
979
993
|
"""
|
|
980
994
|
# Validate that at least one tag source is provided
|
|
981
995
|
if not tag_ids and not tag_names:
|
|
982
|
-
raise ValueError(
|
|
996
|
+
raise ValueError(
|
|
997
|
+
"At least one of 'tag_ids' or 'tag_names' must be provided"
|
|
998
|
+
)
|
|
983
999
|
|
|
984
1000
|
# Validate input types
|
|
985
1001
|
if tag_ids is not None and not isinstance(tag_ids, list):
|
|
@@ -997,7 +1013,9 @@ class KarakeepAPI:
|
|
|
997
1013
|
if tag_names:
|
|
998
1014
|
for i, tag_name in enumerate(tag_names):
|
|
999
1015
|
if not isinstance(tag_name, str) or not tag_name.strip():
|
|
1000
|
-
raise ValueError(
|
|
1016
|
+
raise ValueError(
|
|
1017
|
+
f"Tag name at index {i} must be a non-empty string"
|
|
1018
|
+
)
|
|
1001
1019
|
|
|
1002
1020
|
# Construct the tags_data dict in the format expected by the API
|
|
1003
1021
|
tags_list = []
|
|
@@ -1050,7 +1068,9 @@ class KarakeepAPI:
|
|
|
1050
1068
|
"""
|
|
1051
1069
|
# Validate that at least one tag source is provided
|
|
1052
1070
|
if not tag_ids and not tag_names:
|
|
1053
|
-
raise ValueError(
|
|
1071
|
+
raise ValueError(
|
|
1072
|
+
"At least one of 'tag_ids' or 'tag_names' must be provided"
|
|
1073
|
+
)
|
|
1054
1074
|
|
|
1055
1075
|
# Validate input types
|
|
1056
1076
|
if tag_ids is not None and not isinstance(tag_ids, list):
|
|
@@ -1068,7 +1088,9 @@ class KarakeepAPI:
|
|
|
1068
1088
|
if tag_names:
|
|
1069
1089
|
for i, tag_name in enumerate(tag_names):
|
|
1070
1090
|
if not isinstance(tag_name, str) or not tag_name.strip():
|
|
1071
|
-
raise ValueError(
|
|
1091
|
+
raise ValueError(
|
|
1092
|
+
f"Tag name at index {i} must be a non-empty string"
|
|
1093
|
+
)
|
|
1072
1094
|
|
|
1073
1095
|
# Construct the tags_data dict in the format expected by the API
|
|
1074
1096
|
tags_list = []
|
|
@@ -1145,16 +1167,28 @@ class KarakeepAPI:
|
|
|
1145
1167
|
|
|
1146
1168
|
@optional_typecheck
|
|
1147
1169
|
def attach_asset(
|
|
1148
|
-
self,
|
|
1170
|
+
self,
|
|
1171
|
+
bookmark_id: str,
|
|
1172
|
+
asset_id: str,
|
|
1173
|
+
asset_type: Literal[
|
|
1174
|
+
"screenshot",
|
|
1175
|
+
"assetScreenshot",
|
|
1176
|
+
"bannerImage",
|
|
1177
|
+
"fullPageArchive",
|
|
1178
|
+
"video",
|
|
1179
|
+
"bookmarkAsset",
|
|
1180
|
+
"precrawledArchive",
|
|
1181
|
+
"unknown",
|
|
1182
|
+
],
|
|
1149
1183
|
) -> Union[datatypes.Asset, Dict[str, Any], List[Any]]:
|
|
1150
1184
|
"""
|
|
1151
1185
|
Attach a new asset to a bookmark. Corresponds to POST /bookmarks/{bookmarkId}/assets.
|
|
1152
1186
|
|
|
1153
1187
|
Args:
|
|
1154
1188
|
bookmark_id: The ID (string) of the bookmark.
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1189
|
+
asset_id: The ID (string) of the asset to attach.
|
|
1190
|
+
asset_type: The type of asset being attached. Must be one of: "screenshot", "assetScreenshot",
|
|
1191
|
+
"bannerImage", "fullPageArchive", "video", "bookmarkAsset", "precrawledArchive", "unknown".
|
|
1158
1192
|
|
|
1159
1193
|
Returns:
|
|
1160
1194
|
datatypes.Asset: The attached asset object.
|
|
@@ -1164,6 +1198,9 @@ class KarakeepAPI:
|
|
|
1164
1198
|
APIError: If the API request fails (e.g., 404 bookmark not found).
|
|
1165
1199
|
pydantic.ValidationError: If response validation fails (and is not disabled).
|
|
1166
1200
|
"""
|
|
1201
|
+
# Construct the asset data dict as expected by the API
|
|
1202
|
+
asset_data = {"id": asset_id, "assetType": asset_type}
|
|
1203
|
+
|
|
1167
1204
|
endpoint = f"bookmarks/{bookmark_id}/assets"
|
|
1168
1205
|
response_data = self._call("POST", endpoint, data=asset_data)
|
|
1169
1206
|
|
|
@@ -1175,9 +1212,7 @@ class KarakeepAPI:
|
|
|
1175
1212
|
return datatypes.Asset.model_validate(response_data)
|
|
1176
1213
|
|
|
1177
1214
|
@optional_typecheck
|
|
1178
|
-
def replace_asset(
|
|
1179
|
-
self, bookmark_id: str, asset_id: str, new_asset_data: dict
|
|
1180
|
-
) -> None:
|
|
1215
|
+
def replace_asset(self, bookmark_id: str, asset_id: str, new_asset_id: str) -> None:
|
|
1181
1216
|
"""
|
|
1182
1217
|
Replace an existing asset associated with a bookmark with a new one.
|
|
1183
1218
|
Corresponds to PUT /bookmarks/{bookmarkId}/assets/{assetId}.
|
|
@@ -1185,8 +1220,7 @@ class KarakeepAPI:
|
|
|
1185
1220
|
Args:
|
|
1186
1221
|
bookmark_id: The ID (string) of the bookmark.
|
|
1187
1222
|
asset_id: The ID (string) of the asset to be replaced.
|
|
1188
|
-
|
|
1189
|
-
Example: `{"assetId": "new_asset_id_string"}`
|
|
1223
|
+
new_asset_id: The ID (string) of the new asset to replace with.
|
|
1190
1224
|
|
|
1191
1225
|
Returns:
|
|
1192
1226
|
None: Returns None upon successful replacement (204 No Content).
|
|
@@ -1194,6 +1228,9 @@ class KarakeepAPI:
|
|
|
1194
1228
|
Raises:
|
|
1195
1229
|
APIError: If the API request fails (e.g., 404 bookmark or asset not found).
|
|
1196
1230
|
"""
|
|
1231
|
+
# Construct the request body as expected by the API
|
|
1232
|
+
new_asset_data = {"assetId": new_asset_id}
|
|
1233
|
+
|
|
1197
1234
|
endpoint = f"bookmarks/{bookmark_id}/assets/{asset_id}"
|
|
1198
1235
|
self._call("PUT", endpoint, data=new_asset_data) # Expects 204 No Content
|
|
1199
1236
|
return None # Explicitly return None for 204
|
|
@@ -1270,6 +1307,7 @@ class KarakeepAPI:
|
|
|
1270
1307
|
parent_id: Optional[str] = None,
|
|
1271
1308
|
list_type: Optional[Literal["manual", "smart"]] = "manual",
|
|
1272
1309
|
query: Optional[str] = None,
|
|
1310
|
+
public: bool = False,
|
|
1273
1311
|
) -> Union[datatypes.ListModel, Dict[str, Any], List[Any]]:
|
|
1274
1312
|
"""
|
|
1275
1313
|
Create a new list (manual or smart). Corresponds to POST /lists.
|
|
@@ -1281,6 +1319,7 @@ class KarakeepAPI:
|
|
|
1281
1319
|
parent_id: Optional parent list ID for nested lists.
|
|
1282
1320
|
list_type: The type of list ('manual' or 'smart'). Default is 'manual'.
|
|
1283
1321
|
query: Optional query string for smart lists (required if list_type is 'smart').
|
|
1322
|
+
public: Whether the list is public (default: False).
|
|
1284
1323
|
|
|
1285
1324
|
Returns:
|
|
1286
1325
|
datatypes.ListModel: The created list object.
|
|
@@ -1300,6 +1339,7 @@ class KarakeepAPI:
|
|
|
1300
1339
|
"name": name,
|
|
1301
1340
|
"icon": icon,
|
|
1302
1341
|
"type": list_type,
|
|
1342
|
+
"public": public,
|
|
1303
1343
|
}
|
|
1304
1344
|
|
|
1305
1345
|
# Add optional fields if provided
|
|
@@ -1367,25 +1407,56 @@ class KarakeepAPI:
|
|
|
1367
1407
|
|
|
1368
1408
|
@optional_typecheck
|
|
1369
1409
|
def update_a_list(
|
|
1370
|
-
self,
|
|
1410
|
+
self,
|
|
1411
|
+
list_id: str,
|
|
1412
|
+
name: Optional[str] = None,
|
|
1413
|
+
description: Optional[str] = None,
|
|
1414
|
+
icon: Optional[str] = None,
|
|
1415
|
+
parent_id: Optional[str] = None,
|
|
1416
|
+
query: Optional[str] = None,
|
|
1417
|
+
public: Optional[bool] = None,
|
|
1371
1418
|
) -> Union[datatypes.ListModel, Dict[str, Any], List[Any]]:
|
|
1372
1419
|
"""
|
|
1373
1420
|
Update a list by its ID. Corresponds to PATCH /lists/{listId}.
|
|
1374
|
-
Allows updating fields
|
|
1421
|
+
Allows updating various list fields including name, description, icon, parent relationship, query, and public status.
|
|
1375
1422
|
|
|
1376
1423
|
Args:
|
|
1377
1424
|
list_id: The ID (string) of the list to update.
|
|
1378
|
-
|
|
1379
|
-
|
|
1425
|
+
name: Optional new name for the list (1-40 characters).
|
|
1426
|
+
description: Optional new description for the list (0-100 characters, can be None to clear).
|
|
1427
|
+
icon: Optional new icon for the list.
|
|
1428
|
+
parent_id: Optional new parent list ID (can be None to remove parent relationship).
|
|
1429
|
+
query: Optional new query string for smart lists (minimum 1 character).
|
|
1430
|
+
public: Optional new public status for the list.
|
|
1380
1431
|
|
|
1381
1432
|
Returns:
|
|
1382
1433
|
datatypes.ListModel: The updated list object.
|
|
1383
1434
|
If response validation is disabled, returns the raw API response (dict/list).
|
|
1384
1435
|
|
|
1385
1436
|
Raises:
|
|
1437
|
+
ValueError: If no fields are provided to update.
|
|
1386
1438
|
APIError: If the API request fails (e.g., 404 list not found).
|
|
1387
1439
|
pydantic.ValidationError: If response validation fails (and is not disabled).
|
|
1388
1440
|
"""
|
|
1441
|
+
# Construct update_data from provided arguments, excluding None values that weren't explicitly passed
|
|
1442
|
+
update_data = {}
|
|
1443
|
+
if name is not None:
|
|
1444
|
+
update_data["name"] = name
|
|
1445
|
+
if description is not None:
|
|
1446
|
+
update_data["description"] = description
|
|
1447
|
+
if icon is not None:
|
|
1448
|
+
update_data["icon"] = icon
|
|
1449
|
+
if parent_id is not None:
|
|
1450
|
+
update_data["parentId"] = parent_id
|
|
1451
|
+
if query is not None:
|
|
1452
|
+
update_data["query"] = query
|
|
1453
|
+
if public is not None:
|
|
1454
|
+
update_data["public"] = public
|
|
1455
|
+
|
|
1456
|
+
# Ensure at least one field is being updated
|
|
1457
|
+
if not update_data:
|
|
1458
|
+
raise ValueError("At least one field must be provided to update.")
|
|
1459
|
+
|
|
1389
1460
|
endpoint = f"lists/{list_id}"
|
|
1390
1461
|
response_data = self._call("PATCH", endpoint, data=update_data)
|
|
1391
1462
|
|
|
@@ -1594,23 +1665,28 @@ class KarakeepAPI:
|
|
|
1594
1665
|
return None # Explicitly return None for 204
|
|
1595
1666
|
|
|
1596
1667
|
@optional_typecheck
|
|
1597
|
-
def update_a_tag(self, tag_id: str, update_data:
|
|
1668
|
+
def update_a_tag(self, tag_id: str, update_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
1598
1669
|
"""
|
|
1599
1670
|
Update a tag by its ID. Currently only supports updating the "name".
|
|
1600
1671
|
Corresponds to PATCH /tags/{tagId}.
|
|
1601
1672
|
|
|
1602
1673
|
Args:
|
|
1603
1674
|
tag_id: The ID (string) of the tag to update.
|
|
1604
|
-
update_data:
|
|
1605
|
-
|
|
1675
|
+
update_data: Dictionary containing the fields to update. Supported keys include:
|
|
1676
|
+
'name' (string).
|
|
1606
1677
|
|
|
1607
1678
|
Returns:
|
|
1608
1679
|
dict: A dictionary containing the updated tag information with "id" and "name" fields.
|
|
1609
1680
|
Validation is not performed on this response type by default.
|
|
1610
1681
|
|
|
1611
1682
|
Raises:
|
|
1683
|
+
ValueError: If update_data is empty or no valid fields are provided to update.
|
|
1612
1684
|
APIError: If the API request fails (e.g., 404 tag not found).
|
|
1613
1685
|
"""
|
|
1686
|
+
# Ensure at least one field is being updated
|
|
1687
|
+
if not update_data:
|
|
1688
|
+
raise ValueError("update_data must contain at least one field to update.")
|
|
1689
|
+
|
|
1614
1690
|
endpoint = f"tags/{tag_id}"
|
|
1615
1691
|
response_data = self._call("PATCH", endpoint, data=update_data)
|
|
1616
1692
|
# Response schema is a simple dict with id and name, return as dict
|
|
@@ -1693,8 +1769,8 @@ class KarakeepAPI:
|
|
|
1693
1769
|
def create_a_new_highlight(
|
|
1694
1770
|
self,
|
|
1695
1771
|
bookmark_id: str,
|
|
1696
|
-
start_offset: float,
|
|
1697
|
-
end_offset: float,
|
|
1772
|
+
start_offset: Union[float, int],
|
|
1773
|
+
end_offset: Union[float, int],
|
|
1698
1774
|
color: Optional[Literal["yellow", "red", "green", "blue"]] = "yellow",
|
|
1699
1775
|
text: Optional[str] = None,
|
|
1700
1776
|
note: Optional[str] = None,
|
|
@@ -1796,7 +1872,9 @@ class KarakeepAPI:
|
|
|
1796
1872
|
|
|
1797
1873
|
@optional_typecheck
|
|
1798
1874
|
def update_a_highlight(
|
|
1799
|
-
self,
|
|
1875
|
+
self,
|
|
1876
|
+
highlight_id: str,
|
|
1877
|
+
color: Optional[Literal["yellow", "red", "green", "blue"]] = None,
|
|
1800
1878
|
) -> Union[datatypes.Highlight, Dict[str, Any], List[Any]]:
|
|
1801
1879
|
"""
|
|
1802
1880
|
Update a highlight by its ID. Currently only supports updating the "color".
|
|
@@ -1804,17 +1882,26 @@ class KarakeepAPI:
|
|
|
1804
1882
|
|
|
1805
1883
|
Args:
|
|
1806
1884
|
highlight_id: The ID (string) of the highlight to update.
|
|
1807
|
-
|
|
1808
|
-
See `datatypes.Color` enum. Example: `{"color": "red"}`
|
|
1885
|
+
color: Optional new color for the highlight ("yellow", "red", "green", "blue").
|
|
1809
1886
|
|
|
1810
1887
|
Returns:
|
|
1811
1888
|
datatypes.Highlight: The updated highlight object.
|
|
1812
1889
|
If response validation is disabled, returns the raw API response (dict/list).
|
|
1813
1890
|
|
|
1814
1891
|
Raises:
|
|
1892
|
+
ValueError: If no fields are provided to update.
|
|
1815
1893
|
APIError: If the API request fails (e.g., 404 highlight not found).
|
|
1816
1894
|
pydantic.ValidationError: If response validation fails (and is not disabled).
|
|
1817
1895
|
"""
|
|
1896
|
+
# Construct update_data from provided arguments, excluding None values
|
|
1897
|
+
update_data = {}
|
|
1898
|
+
if color is not None:
|
|
1899
|
+
update_data["color"] = color
|
|
1900
|
+
|
|
1901
|
+
# Ensure at least one field is being updated
|
|
1902
|
+
if not update_data:
|
|
1903
|
+
raise ValueError("At least one field must be provided to update.")
|
|
1904
|
+
|
|
1818
1905
|
endpoint = f"highlights/{highlight_id}"
|
|
1819
1906
|
response_data = self._call("PATCH", endpoint, data=update_data)
|
|
1820
1907
|
|
|
@@ -426,13 +426,17 @@
|
|
|
426
426
|
"query": {
|
|
427
427
|
"type": "string",
|
|
428
428
|
"nullable": true
|
|
429
|
+
},
|
|
430
|
+
"public": {
|
|
431
|
+
"type": "boolean"
|
|
429
432
|
}
|
|
430
433
|
},
|
|
431
434
|
"required": [
|
|
432
435
|
"id",
|
|
433
436
|
"name",
|
|
434
437
|
"icon",
|
|
435
|
-
"parentId"
|
|
438
|
+
"parentId",
|
|
439
|
+
"public"
|
|
436
440
|
]
|
|
437
441
|
},
|
|
438
442
|
"Tag": {
|
|
@@ -1982,6 +1986,9 @@
|
|
|
1982
1986
|
"query": {
|
|
1983
1987
|
"type": "string",
|
|
1984
1988
|
"minLength": 1
|
|
1989
|
+
},
|
|
1990
|
+
"public": {
|
|
1991
|
+
"type": "boolean"
|
|
1985
1992
|
}
|
|
1986
1993
|
}
|
|
1987
1994
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karakeep_python_api
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Community python client for the Karakeep API.
|
|
5
5
|
Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
|
|
6
6
|
License: GPLv3
|
|
@@ -52,6 +52,7 @@ A community-developed Python client for the [Karakeep](https://karakeep.app/) AP
|
|
|
52
52
|
- [Environment Variables](#environment-variables)
|
|
53
53
|
- [Command Line Interface (CLI)](#command-line-interface-cli)
|
|
54
54
|
- [Python Library](#python-library)
|
|
55
|
+
- [Community Scripts](#community-scripts)
|
|
55
56
|
- [Development](#development)
|
|
56
57
|
- [License](#license)
|
|
57
58
|
|
|
@@ -136,7 +137,7 @@ This package can be used as a Python library or as a command-line interface (CLI
|
|
|
136
137
|
|
|
137
138
|
The client can be configured using the following environment variables:
|
|
138
139
|
|
|
139
|
-
* `
|
|
140
|
+
* `KARAKEEP_PYTHON_API_ENDPOINT`: **Required**. The full URL of your Karakeep API, including the `/api/v1/` path (e.g., `https://karakeep.domain.com/api/v1/` or `https://try.karakeep.app/api/v1/`).
|
|
140
141
|
* `KARAKEEP_PYTHON_API_KEY`: **Required**. Your Karakeep API key (Bearer token).
|
|
141
142
|
* `KARAKEEP_PYTHON_API_VERIFY_SSL`: Set to `false` to disable SSL certificate verification (default: `true`).
|
|
142
143
|
* `KARAKEEP_PYTHON_API_VERBOSE`: Set to `true` to enable verbose debug logging for the client and CLI (default: `false`).
|
|
@@ -145,7 +146,7 @@ The client can be configured using the following environment variables:
|
|
|
145
146
|
|
|
146
147
|
### Command Line Interface (CLI)
|
|
147
148
|
|
|
148
|
-
The CLI dynamically generates commands based on the API methods. You need to provide your API key and
|
|
149
|
+
The CLI dynamically generates commands based on the API methods. You need to provide your API key and endpoint either via environment variables (recommended) or command-line options.
|
|
149
150
|
|
|
150
151
|
**Basic Structure:**
|
|
151
152
|
|
|
@@ -170,8 +171,8 @@ python -m karakeep_python_api get-all-bookmarks --help
|
|
|
170
171
|
python -m karakeep_python_api get-all-tags
|
|
171
172
|
|
|
172
173
|
# Get the first page of bookmarks with a limit, overriding env vars if needed
|
|
173
|
-
# Note:
|
|
174
|
-
python -m karakeep_python_api --base-url https://
|
|
174
|
+
# Note: The /api/v1/ path will be automatically appended if not present
|
|
175
|
+
python -m karakeep_python_api --base-url https://karakeep.domain.com/api/v1/ --api-key YOUR_API_KEY get-all-bookmarks --limit 10
|
|
175
176
|
|
|
176
177
|
# Get all lists and pipe the JSON output to jq to extract the first list
|
|
177
178
|
python -m karakeep_python_api get-all-lists | jq '.[0]'
|
|
@@ -195,14 +196,14 @@ import os
|
|
|
195
196
|
from karakeep_python_api import KarakeepAPI, APIError, AuthenticationError, datatypes
|
|
196
197
|
|
|
197
198
|
# Ensure required environment variables are set
|
|
198
|
-
# Example: os.environ["
|
|
199
|
+
# Example: os.environ["KARAKEEP_PYTHON_API_ENDPOINT"] = "https://karakeep.domain.com/api/v1/"
|
|
199
200
|
# Example: os.environ["KARAKEEP_PYTHON_API_KEY"] = "your_secret_api_key"
|
|
200
201
|
|
|
201
202
|
try:
|
|
202
203
|
# Initialize the client (reads from env vars by default)
|
|
203
204
|
client = KarakeepAPI(
|
|
204
205
|
# Optionally override env vars:
|
|
205
|
-
#
|
|
206
|
+
# api_endpoint="https://karakeep.domain.com/api/v1/",
|
|
206
207
|
# api_key="another_key",
|
|
207
208
|
# verbose=True,
|
|
208
209
|
# disable_response_validation=False
|
|
@@ -232,23 +233,27 @@ except AuthenticationError as e:
|
|
|
232
233
|
except APIError as e:
|
|
233
234
|
print(f"An API error occurred: {e}")
|
|
234
235
|
except ValueError as e:
|
|
235
|
-
# Handles missing API key/
|
|
236
|
+
# Handles missing API key/endpoint during initialization
|
|
236
237
|
print(f"Configuration error: {e}")
|
|
237
238
|
except Exception as e:
|
|
238
239
|
print(f"An unexpected error occurred: {e}")
|
|
239
240
|
|
|
240
241
|
```
|
|
241
242
|
|
|
242
|
-
|
|
243
|
+
## Community Scripts
|
|
243
244
|
|
|
244
|
-
|
|
245
|
+
Community Scripts are a bunch of scripts made to solve specific issues. They are made by the community so don't hesitate to submit yours or open an issue if you have a bug. They also serve as example of how to use the API.
|
|
245
246
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
|
249
|
-
|
|
250
|
-
| **
|
|
251
|
-
| **
|
|
247
|
+
They can be found in the [./community_scripts](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts) folder. Don't hesitate to submit yours, the contribution guidelines are in the community_scripts directory README.md file.
|
|
248
|
+
|
|
249
|
+
| Community Script | Description | Documentation |
|
|
250
|
+
|----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|
|
|
251
|
+
| **Karakeep-Time-Tagger** | Automatically adds time-to-read tags (`0-5m`, `5-10m`, etc.) to bookmarks based on content length analysis. Includes systemd service and timer files for automated periodic execution. | [`Link`](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts/karakeep-time-tagger) |
|
|
252
|
+
| **Karakeep-List-To-Tag** | Converts a Karakeep list into tags by adding a specified tag to all bookmarks within that list. | [`Link`](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts/karakeep-list-to-tag) |
|
|
253
|
+
| **Omnivore2Karakeep-Highlights** | Imports highlights from Omnivore export data to Karakeep, with intelligent position detection and bookmark matching. Supports dry-run mode for testing. | [`Link`](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts/omnivore2karakeep-highlights) |
|
|
254
|
+
| **Omnivore2Karakeep-Archived** | (Should not be needed anymore) Fixes the archived status of bookmarks imported from Omnivore by reading export data and updating Karakeep accordingly. | [`Link`](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts/omnivore2karakeep-archived) |
|
|
255
|
+
| **pocket2karakeep-archived** by [@youenchene](https://github.com/youenchene) | (Should not be needed anymore) Fixes the archived status of bookmarks imported from Pocket by reading export data and updating Karakeep accordingly. | [`Link`](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts/pocket2karakeep-archived) |
|
|
256
|
+
| **karakeep-archive-before-date** by [@youenchene](https://github.com/youenchene) | Allow you to archive all not archived post before a given date | [`Link`](https://github.com/thiswillbeyourgithub/karakeep_python_api/tree/main/community_scripts/karakeep-archive-before-date) |
|
|
252
257
|
|
|
253
258
|
## Development
|
|
254
259
|
|
|
@@ -259,7 +264,7 @@ Examples of the API being used can be found in the [`./examples`](./examples) fo
|
|
|
259
264
|
```bash
|
|
260
265
|
uv pip install -e ".[dev]"
|
|
261
266
|
```
|
|
262
|
-
4. Set the required environment variables (`
|
|
267
|
+
4. Set the required environment variables (`KARAKEEP_PYTHON_API_ENDPOINT`, `KARAKEEP_PYTHON_API_KEY`) for running tests against a live instance.
|
|
263
268
|
5. Run tests:
|
|
264
269
|
|
|
265
270
|
```bash
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
karakeep_python_api/__init__.py,sha256=qk3MeIIvnCNAyYzJrv8dMKVXvA4ejLU3mhB3xFzdLDY,606
|
|
2
|
+
karakeep_python_api/__main__.py,sha256=X1GXwRvAAw8qFmMm2MKdmPNYkmlmEUUQalJX9kWxrS0,32402
|
|
3
|
+
karakeep_python_api/datatypes.py,sha256=vcDqS8r_LRMLqrIvHZIqkvUSiucprz58jOAeC82QwHY,3415
|
|
4
|
+
karakeep_python_api/karakeep_api.py,sha256=nvpynAk4S5G1-sjf0T4sl9kYjHyV12bQobd8fiMLVlg,85276
|
|
5
|
+
karakeep_python_api/openapi_reference.json,sha256=9bvTkzHbgtUg-xg6T6zF3bSdxBMW1Rn5nC-l2jH6FoQ,80553
|
|
6
|
+
karakeep_python_api-1.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
7
|
+
karakeep_python_api-1.1.0.dist-info/METADATA,sha256=bGJws2bLSvGXZwi6mFlpnh0NhVBXtjsSDZqybwgyQ8E,15732
|
|
8
|
+
karakeep_python_api-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
karakeep_python_api-1.1.0.dist-info/entry_points.txt,sha256=n0leQp_IX2NivoWuUcaR9ZHwAvl_6yt-NcHWuCJyxNo,62
|
|
10
|
+
karakeep_python_api-1.1.0.dist-info/top_level.txt,sha256=X3VKqh9YAbPQp144db0Ko2C5Q2y6-nwpPgtnuCiSDmU,20
|
|
11
|
+
karakeep_python_api-1.1.0.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
karakeep_python_api/__init__.py,sha256=qk3MeIIvnCNAyYzJrv8dMKVXvA4ejLU3mhB3xFzdLDY,606
|
|
2
|
-
karakeep_python_api/__main__.py,sha256=UfL6R-S3rJB7PH3iqTicNq0cvtumnrG_FVyKK36bqdE,32327
|
|
3
|
-
karakeep_python_api/datatypes.py,sha256=CScuMq5oT4YwvGH8Af_Oo60xGmC622d74f4WPUw7l8Q,3398
|
|
4
|
-
karakeep_python_api/karakeep_api.py,sha256=NYGvU0UIK95k841cz7Y9thWnoTAaWMRVuny7JGLjYQA,81921
|
|
5
|
-
karakeep_python_api/openapi_reference.json,sha256=Ku1cO_N4QPLQc7sK8L7FRt2T68QyPCwTGA7RK-046iU,80379
|
|
6
|
-
karakeep_python_api-0.2.3.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
7
|
-
karakeep_python_api-0.2.3.dist-info/METADATA,sha256=b6rRzFPf1OvnALa37p7CXKnzWQt6Aa3yUQK9k7Lq1UQ,13649
|
|
8
|
-
karakeep_python_api-0.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
karakeep_python_api-0.2.3.dist-info/entry_points.txt,sha256=n0leQp_IX2NivoWuUcaR9ZHwAvl_6yt-NcHWuCJyxNo,62
|
|
10
|
-
karakeep_python_api-0.2.3.dist-info/top_level.txt,sha256=X3VKqh9YAbPQp144db0Ko2C5Q2y6-nwpPgtnuCiSDmU,20
|
|
11
|
-
karakeep_python_api-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
{karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{karakeep_python_api-0.2.3.dist-info → karakeep_python_api-1.1.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|