karakeep-python-api 0.1.7__tar.gz → 0.2.1__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.
Files changed (19) hide show
  1. {karakeep_python_api-0.1.7/karakeep_python_api.egg-info → karakeep_python_api-0.2.1}/PKG-INFO +4 -3
  2. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/README.md +3 -2
  3. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api/__main__.py +130 -65
  4. karakeep_python_api-0.2.1/karakeep_python_api/datatypes.py +142 -0
  5. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api/karakeep_api.py +251 -83
  6. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api/openapi_reference.json +157 -7
  7. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1/karakeep_python_api.egg-info}/PKG-INFO +4 -3
  8. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/setup.py +1 -1
  9. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/tests/test_karakeep_api.py +10 -6
  10. karakeep_python_api-0.1.7/karakeep_python_api/datatypes.py +0 -199
  11. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/LICENSE +0 -0
  12. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/MANIFEST.in +0 -0
  13. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api/__init__.py +0 -0
  14. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
  15. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
  16. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api.egg-info/entry_points.txt +0 -0
  17. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api.egg-info/requires.txt +0 -0
  18. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/karakeep_python_api.egg-info/top_level.txt +0 -0
  19. {karakeep_python_api-0.1.7 → karakeep_python_api-0.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 0.1.7
3
+ Version: 0.2.1
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
@@ -97,14 +97,15 @@ Methods or CLI commands marked with ❌ should be used with caution as their beh
97
97
  | `get_a_single_list` | ✅ | ❌ | |
98
98
  | `delete_a_list` | ✅ | ❌ | |
99
99
  | `update_a_list` | ❌ | ❌ | |
100
- | `get_a_bookmarks_in_a_list` | ❌ | ❌ | |
100
+ | `get_bookmarks_in_the_list` | ❌ | ❌ | |
101
101
  | `add_a_bookmark_to_a_list` | ❌ | ❌ | |
102
102
  | `remove_a_bookmark_from_a_list` | ❌ | ❌ | |
103
103
  | `get_all_tags` | ✅ | ✅ | |
104
+ | `create_a_new_tag` | ❌ | ❌ | |
104
105
  | `get_a_single_tag` | ✅ | ❌ | |
105
106
  | `delete_a_tag` | ✅ | ❌ | |
106
107
  | `update_a_tag` | ✅ | ❌ | No output validation due to [server bug](https://github.com/karakeep-app/karakeep/issues/1365). |
107
- | `get_a_bookmarks_with_the_tag` | ❌ | ❌ | |
108
+ | `get_bookmarks_with_the_tag` | ❌ | ❌ | |
108
109
  | `get_all_highlights` | ✅ | ✅ | Tested with pagination. |
109
110
  | `create_a_new_highlight` | ❌ | ❌ | |
110
111
  | `get_a_single_highlight` | ❌ | ❌ | |
@@ -62,14 +62,15 @@ Methods or CLI commands marked with ❌ should be used with caution as their beh
62
62
  | `get_a_single_list` | ✅ | ❌ | |
63
63
  | `delete_a_list` | ✅ | ❌ | |
64
64
  | `update_a_list` | ❌ | ❌ | |
65
- | `get_a_bookmarks_in_a_list` | ❌ | ❌ | |
65
+ | `get_bookmarks_in_the_list` | ❌ | ❌ | |
66
66
  | `add_a_bookmark_to_a_list` | ❌ | ❌ | |
67
67
  | `remove_a_bookmark_from_a_list` | ❌ | ❌ | |
68
68
  | `get_all_tags` | ✅ | ✅ | |
69
+ | `create_a_new_tag` | ❌ | ❌ | |
69
70
  | `get_a_single_tag` | ✅ | ❌ | |
70
71
  | `delete_a_tag` | ✅ | ❌ | |
71
72
  | `update_a_tag` | ✅ | ❌ | No output validation due to [server bug](https://github.com/karakeep-app/karakeep/issues/1365). |
72
- | `get_a_bookmarks_with_the_tag` | ❌ | ❌ | |
73
+ | `get_bookmarks_with_the_tag` | ❌ | ❌ | |
73
74
  | `get_all_highlights` | ✅ | ✅ | Tested with pagination. |
74
75
  | `create_a_new_highlight` | ❌ | ❌ | |
75
76
  | `get_a_single_highlight` | ❌ | ❌ | |
@@ -7,7 +7,17 @@ import functools
7
7
  import re # Import re module
8
8
  import click
9
9
  import traceback # Moved import to top
10
- from typing import Any, List, Dict, Optional, Callable, Union, get_origin, get_args, Literal
10
+ from typing import (
11
+ Any,
12
+ List,
13
+ Dict,
14
+ Optional,
15
+ Callable,
16
+ Union,
17
+ get_origin,
18
+ get_args,
19
+ Literal,
20
+ )
11
21
  from pydantic import BaseModel, ValidationError
12
22
  from loguru import logger # Import logger
13
23
 
@@ -82,9 +92,9 @@ shared_options = [
82
92
  ),
83
93
  click.option(
84
94
  "--ascii",
85
- "ensure_ascii", # Use 'ensure_ascii' as the destination variable name
95
+ "ensure_ascii", # Use 'ensure_ascii' as the destination variable name
86
96
  is_flag=True,
87
- default=False, # Default is False, meaning ensure_ascii=False by default
97
+ default=False, # Default is False, meaning ensure_ascii=False by default
88
98
  envvar="KARAKEEP_PYTHON_API_ENSURE_ASCII",
89
99
  help="Escape non-ASCII characters in the JSON output (default: keep Unicode).",
90
100
  ),
@@ -135,9 +145,19 @@ def print_openapi_spec(ctx, param, value):
135
145
  is_eager=True, # Process this option before others
136
146
  help="Dump the OpenAPI specification JSON to stdout and exit.",
137
147
  )
138
- @add_options(shared_options) # Apply shared options to the group (ensure_ascii is now included)
148
+ @add_options(
149
+ shared_options
150
+ ) # Apply shared options to the group (ensure_ascii is now included)
139
151
  @click.pass_context
140
- def cli(ctx, base_url, api_key, verify_ssl, verbose, disable_response_validation, ensure_ascii):
152
+ def cli(
153
+ ctx,
154
+ base_url,
155
+ api_key,
156
+ verify_ssl,
157
+ verbose,
158
+ disable_response_validation,
159
+ ensure_ascii,
160
+ ):
141
161
  """
142
162
  Karakeep Python API Command Line Interface.
143
163
 
@@ -171,7 +191,7 @@ def cli(ctx, base_url, api_key, verify_ssl, verbose, disable_response_validation
171
191
  ctx.obj["DISABLE_RESPONSE_VALIDATION"] = (
172
192
  disable_response_validation # Store the flag
173
193
  )
174
- ctx.obj["ENSURE_ASCII"] = ensure_ascii # Store the ensure_ascii flag
194
+ ctx.obj["ENSURE_ASCII"] = ensure_ascii # Store the ensure_ascii flag
175
195
 
176
196
  # --- Configure Logger ---
177
197
  log_level = "DEBUG" if verbose else "INFO"
@@ -184,7 +204,7 @@ def cli(ctx, base_url, api_key, verify_ssl, verbose, disable_response_validation
184
204
  )
185
205
  logger.debug("Verbose logging enabled with detailed format.")
186
206
  else:
187
- logger.add(sys.stderr, level=log_level) # Default format for INFO
207
+ logger.add(sys.stderr, level=log_level) # Default format for INFO
188
208
  logger.debug("Logger configured for level: {}", log_level)
189
209
  logger.debug("CLI context initialized.")
190
210
 
@@ -216,7 +236,7 @@ def create_click_command(
216
236
  verify_ssl = ctx.obj["VERIFY_SSL"]
217
237
  verbose = ctx.obj["VERBOSE"]
218
238
  disable_validation = ctx.obj["DISABLE_RESPONSE_VALIDATION"] # Retrieve flag
219
- ensure_ascii_output = ctx.obj["ENSURE_ASCII"] # Retrieve ensure_ascii flag
239
+ ensure_ascii_output = ctx.obj["ENSURE_ASCII"] # Retrieve ensure_ascii flag
220
240
 
221
241
  if not api_key:
222
242
  click.echo(
@@ -253,9 +273,7 @@ def create_click_command(
253
273
  # Remove arguments that are not part of the method signature
254
274
  # (e.g., if extra options were somehow passed)
255
275
  valid_arg_names = set(signature.parameters.keys())
256
- call_args = {
257
- k: v for k, v in call_args.items() if k in valid_arg_names
258
- }
276
+ call_args = {k: v for k, v in call_args.items() if k in valid_arg_names}
259
277
 
260
278
  # --- JSON Parsing for Dict/List Parameters ---
261
279
  # Iterate through the expected parameters from the signature
@@ -268,13 +286,15 @@ def create_click_command(
268
286
  # Check if the annotation is dict/list or typing.Dict/List
269
287
  # and if the received value is a string (needs parsing)
270
288
  if (
271
- (param_annotation in (dict, list) or origin in (dict, list, Dict, List))
272
- and isinstance(param_value, str)
273
- ):
289
+ param_annotation in (dict, list)
290
+ or origin in (dict, list, Dict, List)
291
+ ) and isinstance(param_value, str):
274
292
  try:
275
293
  # Attempt to parse the JSON string
276
294
  call_args[param_name] = json.loads(param_value)
277
- logger.debug(f"Parsed JSON string for parameter '{param_name}'.")
295
+ logger.debug(
296
+ f"Parsed JSON string for parameter '{param_name}'."
297
+ )
278
298
  except json.JSONDecodeError as json_err:
279
299
  # Handle invalid JSON input from the user
280
300
  click.echo(
@@ -287,89 +307,122 @@ def create_click_command(
287
307
  # Call the API method
288
308
  try:
289
309
  if method_name == "get_all_bookmarks":
290
- logger.debug(f"Special CLI pagination handling for '{method_name}'.")
291
- cli_total_limit = call_args.pop('limit', None)
310
+ logger.debug(
311
+ f"Special CLI pagination handling for '{method_name}'."
312
+ )
313
+ cli_total_limit = call_args.pop("limit", None)
292
314
  # Other relevant params for get_all_bookmarks
293
- archived_filter = call_args.get('archived')
294
- favourited_filter = call_args.get('favourited')
295
- include_content_cli = call_args.get('include_content', True)
296
-
297
- call_args.pop('cursor', None) # Ignore CLI cursor
315
+ archived_filter = call_args.get("archived")
316
+ favourited_filter = call_args.get("favourited")
317
+ include_content_cli = call_args.get("include_content", True)
318
+
319
+ call_args.pop("cursor", None) # Ignore CLI cursor
298
320
 
299
321
  all_bookmarks_data = []
300
322
  current_page_api_cursor = None
301
323
  fetched_count = 0
302
- API_INTERNAL_PAGE_SIZE = 50 # Define a page size for API calls
324
+ API_INTERNAL_PAGE_SIZE = 50 # Define a page size for API calls
303
325
 
304
326
  while True:
305
327
  api_call_limit = API_INTERNAL_PAGE_SIZE
306
328
  if cli_total_limit is not None:
307
329
  remaining_needed = cli_total_limit - fetched_count
308
330
  if remaining_needed <= 0:
309
- break # Reached or exceeded CLI total limit
310
- api_call_limit = min(API_INTERNAL_PAGE_SIZE, remaining_needed)
311
-
312
- if api_call_limit <= 0 and cli_total_limit is not None : # Avoid asking for 0 or negative items unless fetching all
331
+ break # Reached or exceeded CLI total limit
332
+ api_call_limit = min(
333
+ API_INTERNAL_PAGE_SIZE, remaining_needed
334
+ )
335
+
336
+ if (
337
+ api_call_limit <= 0 and cli_total_limit is not None
338
+ ): # Avoid asking for 0 or negative items unless fetching all
313
339
  break
314
340
 
341
+ logger.debug(
342
+ f"Fetching page for '{method_name}' with cursor: {current_page_api_cursor}, api_limit: {api_call_limit}"
343
+ )
315
344
 
316
- logger.debug(f"Fetching page for '{method_name}' with cursor: {current_page_api_cursor}, api_limit: {api_call_limit}")
317
-
318
345
  page_call_args = {
319
- 'archived': archived_filter,
320
- 'favourited': favourited_filter,
321
- 'limit': api_call_limit,
322
- 'cursor': current_page_api_cursor,
323
- 'include_content': include_content_cli,
346
+ "archived": archived_filter,
347
+ "favourited": favourited_filter,
348
+ "limit": api_call_limit,
349
+ "cursor": current_page_api_cursor,
350
+ "include_content": include_content_cli,
351
+ }
352
+ page_call_args_filtered = {
353
+ k: v for k, v in page_call_args.items() if v is not None
324
354
  }
325
- page_call_args_filtered = {k: v for k, v in page_call_args.items() if v is not None}
326
355
 
327
356
  try:
328
- page_result_obj = instance_method(**page_call_args_filtered)
357
+ page_result_obj = instance_method(
358
+ **page_call_args_filtered
359
+ )
329
360
  except TypeError as call_error_page:
330
- logger.error(f"Error calling API method '{method_name}' (paginated): {call_error_page}")
331
- logger.error(f"Provided arguments for page: {page_call_args_filtered}")
332
- if verbose: logger.debug(traceback.format_exc())
361
+ logger.error(
362
+ f"Error calling API method '{method_name}' (paginated): {call_error_page}"
363
+ )
364
+ logger.error(
365
+ f"Provided arguments for page: {page_call_args_filtered}"
366
+ )
367
+ if verbose:
368
+ logger.debug(traceback.format_exc())
333
369
  ctx.exit(1)
334
370
 
335
371
  bookmarks_on_this_page = []
336
372
  next_api_cursor = None
337
373
 
338
374
  # Convert Pydantic model to dict using model_dump if available
339
- if hasattr(page_result_obj, 'model_dump'):
375
+ if hasattr(page_result_obj, "model_dump"):
340
376
  result_dict = page_result_obj.model_dump()
341
377
  elif isinstance(page_result_obj, dict):
342
378
  result_dict = page_result_obj
343
379
  else:
344
- logger.warning(f"Unexpected result type: {type(page_result_obj)}")
380
+ logger.warning(
381
+ f"Unexpected result type: {type(page_result_obj)}"
382
+ )
345
383
  result_dict = {}
346
384
 
347
385
  # Extract data and cursor from the dict
348
386
  bookmarks_on_this_page = result_dict.get("bookmarks", [])
349
387
  next_api_cursor = result_dict.get("nextCursor")
350
388
 
351
- logger.debug(f"Extracted {len(bookmarks_on_this_page)} bookmarks and cursor: {next_api_cursor}")
352
-
389
+ logger.debug(
390
+ f"Extracted {len(bookmarks_on_this_page)} bookmarks and cursor: {next_api_cursor}"
391
+ )
392
+
353
393
  if not isinstance(bookmarks_on_this_page, list):
354
- logger.warning(f"Expected a list of bookmarks, got {type(bookmarks_on_this_page)}. Stopping pagination.")
394
+ logger.warning(
395
+ f"Expected a list of bookmarks, got {type(bookmarks_on_this_page)}. Stopping pagination."
396
+ )
355
397
  break
356
-
398
+
357
399
  all_bookmarks_data.extend(bookmarks_on_this_page)
358
400
  fetched_count += len(bookmarks_on_this_page)
359
- logger.debug(f"Fetched {len(bookmarks_on_this_page)} bookmarks this page. Total fetched: {fetched_count}.")
401
+ logger.debug(
402
+ f"Fetched {len(bookmarks_on_this_page)} bookmarks this page. Total fetched: {fetched_count}."
403
+ )
360
404
 
361
405
  current_page_api_cursor = next_api_cursor
362
406
  if not current_page_api_cursor:
363
- logger.debug("No nextCursor from API, pagination complete.")
407
+ logger.debug(
408
+ "No nextCursor from API, pagination complete."
409
+ )
364
410
  break
365
- if cli_total_limit is not None and fetched_count >= cli_total_limit:
366
- logger.debug(f"CLI total limit of {cli_total_limit} reached or exceeded.")
411
+ if (
412
+ cli_total_limit is not None
413
+ and fetched_count >= cli_total_limit
414
+ ):
415
+ logger.debug(
416
+ f"CLI total limit of {cli_total_limit} reached or exceeded."
417
+ )
367
418
  break
368
419
  if not bookmarks_on_this_page and api_call_limit > 0:
369
- logger.debug("API returned an empty list of bookmarks while a positive limit was set, assuming end of data.")
420
+ logger.debug(
421
+ "API returned an empty list of bookmarks while a positive limit was set, assuming end of data."
422
+ )
370
423
  break
371
-
372
- result = all_bookmarks_data # This will be a list of Bookmark models or dicts
424
+
425
+ result = all_bookmarks_data # This will be a list of Bookmark models or dicts
373
426
  else:
374
427
  # Original behavior for other commands
375
428
  logger.debug(
@@ -393,7 +446,9 @@ def create_click_command(
393
446
  output_data = serialize_output(result)
394
447
  # Use ensure_ascii_output flag to control JSON encoding
395
448
  click.echo(
396
- json.dumps(output_data, indent=2, ensure_ascii=ensure_ascii_output)
449
+ json.dumps(
450
+ output_data, indent=2, ensure_ascii=ensure_ascii_output
451
+ )
397
452
  )
398
453
  else:
399
454
  # Handle None result (e.g., 204 No Content) gracefully
@@ -434,7 +489,9 @@ def create_click_command(
434
489
  docstring = api_method.__doc__ or f"Execute the {api_method_name} API operation."
435
490
  docstring = dedent(docstring)
436
491
  docstring_lines = docstring.split("\n")
437
- help_text = " ".join(docstring.split("\n\n")[0].splitlines()).strip() # First lines as short help
492
+ help_text = " ".join(
493
+ docstring.split("\n\n")[0].splitlines()
494
+ ).strip() # First lines as short help
438
495
  # Full docstring as help
439
496
  full_help = docstring
440
497
 
@@ -452,9 +509,7 @@ def create_click_command(
452
509
  stripped_line = line.strip()
453
510
  if stripped_line == "Args:":
454
511
  in_args_section = True
455
- elif (
456
- stripped_line == "Returns:" or stripped_line == "Raises:"
457
- ):
512
+ elif stripped_line == "Returns:" or stripped_line == "Raises:":
458
513
  in_args_section = False # Stop capturing when Returns/Raises section starts
459
514
  elif in_args_section and stripped_line:
460
515
  args_section_lines.append(stripped_line)
@@ -465,7 +520,9 @@ def create_click_command(
465
520
  param_name = match.group(1)
466
521
  description = match.group(2).strip()
467
522
  param_descriptions[param_name] = description
468
- logger.trace(f"Parsed docstring param: '{param_name}' -> '{description}'")
523
+ logger.trace(
524
+ f"Parsed docstring param: '{param_name}' -> '{description}'"
525
+ )
469
526
  else:
470
527
  param_descriptions[param_name] += " " + stripped_line
471
528
 
@@ -524,7 +581,7 @@ def create_click_command(
524
581
  logger.warning(
525
582
  f"Parameter '{param.name}' is Literal but contains non-string types. Treating as STRING."
526
583
  )
527
- click_type = click.STRING # Fallback
584
+ click_type = click.STRING # Fallback
528
585
 
529
586
  # Determine option name(s) and help text
530
587
  option_names = [f"--{param_name_cli}"]
@@ -543,7 +600,7 @@ def create_click_command(
543
600
  ):
544
601
  param_help += " (Provide as JSON string)"
545
602
  elif isinstance(click_type, click.Choice):
546
- param_help += f" (Choices: {', '.join(click_type.choices)})"
603
+ param_help += f" (Choices: {', '.join(click_type.choices)})"
547
604
 
548
605
  click_required = is_required_in_sig and default_value is None and not is_flag
549
606
 
@@ -551,14 +608,22 @@ def create_click_command(
551
608
  current_param_help = param_help
552
609
  current_click_required = click_required
553
610
  current_default_value = default_value
554
- current_is_flag = is_flag # Though is_flag interpretation might change help/required
555
-
611
+ current_is_flag = (
612
+ is_flag # Though is_flag interpretation might change help/required
613
+ )
614
+
556
615
  # Special handling for 'get_all_bookmarks' command parameters
557
616
  if api_method_name == "get_all_bookmarks":
558
617
  if param.name == "cursor":
559
- current_param_help = "[Ignored by CLI for get-all-bookmarks] " + param_help
560
- current_click_required = False # Cursor is handled by CLI, not required from user
561
- current_default_value = None # Explicitly set default to None for ignored param
618
+ current_param_help = (
619
+ "[Ignored by CLI for get-all-bookmarks] " + param_help
620
+ )
621
+ current_click_required = (
622
+ False # Cursor is handled by CLI, not required from user
623
+ )
624
+ current_default_value = (
625
+ None # Explicitly set default to None for ignored param
626
+ )
562
627
  elif param.name == "limit":
563
628
  current_param_help = "Total maximum number of bookmarks to fetch across pages for get-all-bookmarks. If omitted, all are fetched."
564
629
  # For 'limit', required status and default remain as derived from its Optional[int] type hint
@@ -0,0 +1,142 @@
1
+ """
2
+ The datatype file was originally generated by datamodel-codegen, then refactored and manually (or via LLMs) kept up to date with upstream
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+ from typing import List, Optional, Union, Literal
9
+
10
+ from pydantic import BaseModel, Field, RootModel
11
+
12
+
13
+ class StatusTypes(str, Enum):
14
+ success = "success"
15
+ failure = "failure"
16
+ pending = "pending"
17
+
18
+
19
+ class NumBookmarksByAttachedType(BaseModel):
20
+ ai: Optional[float] = None
21
+ human: Optional[float] = None
22
+
23
+
24
+ class TagShort(BaseModel):
25
+ id: str
26
+ name: str
27
+ attachedBy: Literal["ai", "human"]
28
+
29
+
30
+ class Tag(BaseModel):
31
+ id: str
32
+ name: str
33
+ numBookmarks: float
34
+ numBookmarksByAttachedType: NumBookmarksByAttachedType
35
+
36
+
37
+ class Type(str, Enum):
38
+ link = "link"
39
+
40
+
41
+ class ContentTypeLink(BaseModel):
42
+ type: Literal["link"] = "link"
43
+ url: str
44
+ title: Optional[str] = None
45
+ description: Optional[str] = None
46
+ imageUrl: Optional[str] = None
47
+ imageAssetId: Optional[str] = None
48
+ screenshotAssetId: Optional[str] = None
49
+ fullPageArchiveAssetId: Optional[str] = None
50
+ precrawledArchiveAssetId: Optional[str] = None
51
+ videoAssetId: Optional[str] = None
52
+ favicon: Optional[str] = None
53
+ htmlContent: Optional[str] = None
54
+ crawledAt: Optional[str] = None
55
+ author: Optional[str] = None
56
+ publisher: Optional[str] = None
57
+ datePublished: Optional[str] = None
58
+ dateModified: Optional[str] = None
59
+
60
+
61
+ class ContentTypeUnknown(BaseModel):
62
+ type: Literal["unknown"] = "unknown"
63
+
64
+
65
+ class ContentTypeText(BaseModel):
66
+ type: Literal["text"] = "text"
67
+ text: str
68
+ sourceUrl: Optional[str] = None
69
+
70
+
71
+ class ContentTypeAsset(BaseModel):
72
+ type: Literal["asset"] = "asset"
73
+ assetType: Literal["image", "pdf"]
74
+ assetId: str
75
+ fileName: Optional[str] = None
76
+ sourceUrl: Optional[str] = None
77
+ size: Optional[float] = None
78
+ content: Optional[str] = None
79
+
80
+
81
+ class Asset(BaseModel):
82
+ id: str
83
+ assetType: Literal[
84
+ "screenshot",
85
+ "assetScreenshot",
86
+ "bannerImage",
87
+ "fullPageArchive",
88
+ "video",
89
+ "bookmarkAsset",
90
+ "precrawledArchive",
91
+ "unknown",
92
+ ]
93
+
94
+
95
+ class Bookmark(BaseModel):
96
+ id: str
97
+ createdAt: str
98
+ modifiedAt: Optional[str]
99
+ title: Optional[str] = None
100
+ archived: bool
101
+ favourited: bool
102
+ taggingStatus: Literal["success", "failure", "pending"]
103
+ summarizationStatus: Optional[Literal["success", "failure", "pending"]] = None
104
+ note: Optional[str] = None
105
+ summary: Optional[str] = None
106
+ tags: List[TagShort]
107
+ content: Union[
108
+ ContentTypeLink, ContentTypeText, ContentTypeAsset, ContentTypeUnknown
109
+ ]
110
+ assets: List[Asset]
111
+
112
+
113
+ class PaginatedBookmarks(BaseModel):
114
+ bookmarks: List[Bookmark]
115
+ nextCursor: Optional[str]
116
+
117
+
118
+ class Highlight(BaseModel):
119
+ bookmarkId: str
120
+ startOffset: float
121
+ endOffset: float
122
+ color: Optional[Literal["yellow", "red", "green", "blue"]] = "yellow"
123
+ text: Optional[str]
124
+ note: Optional[str]
125
+ id: str
126
+ userId: str
127
+ createdAt: str
128
+
129
+
130
+ class ListModel(BaseModel):
131
+ id: str
132
+ name: str
133
+ description: Optional[str] = None
134
+ icon: str
135
+ parentId: Optional[str]
136
+ type: Optional[Literal["manual", "smart"]] = "manual"
137
+ query: Optional[str] = None
138
+
139
+
140
+ class PaginatedHighlights(BaseModel):
141
+ highlights: List[Highlight]
142
+ nextCursor: Optional[str]