karakeep-python-api 1.7.0__tar.gz → 1.8.0__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 (18) hide show
  1. {karakeep_python_api-1.7.0/karakeep_python_api.egg-info → karakeep_python_api-1.8.0}/PKG-INFO +1 -1
  2. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api/datatypes.py +18 -2
  3. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api/karakeep_api.py +252 -4
  4. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api/openapi_reference.json +1617 -690
  5. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0/karakeep_python_api.egg-info}/PKG-INFO +1 -1
  6. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/setup.py +1 -1
  7. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/tests/test_karakeep_api.py +99 -0
  8. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/LICENSE +0 -0
  9. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/MANIFEST.in +0 -0
  10. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/README.md +0 -0
  11. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api/__init__.py +0 -0
  12. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api/__main__.py +0 -0
  13. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
  14. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
  15. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api.egg-info/entry_points.txt +0 -0
  16. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api.egg-info/requires.txt +0 -0
  17. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/karakeep_python_api.egg-info/top_level.txt +0 -0
  18. {karakeep_python_api-1.7.0 → karakeep_python_api-1.8.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karakeep_python_api
3
- Version: 1.7.0
3
+ Version: 1.8.0
4
4
  Summary: Community python client for the Karakeep API.
5
5
  Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
6
6
  Keywords: rss,karakeep,hoarder,data-hoarding,python,api,feeds,openapi
@@ -100,13 +100,18 @@ class BookmarkAsset(BaseModel):
100
100
  fileName: Optional[str] = None
101
101
 
102
102
 
103
- class Asset(BaseModel):
103
+ class UploadedAsset(BaseModel):
104
104
  assetId: str
105
105
  contentType: str
106
106
  size: float
107
107
  fileName: str
108
108
 
109
109
 
110
+ # Backwards-compatible alias: the upstream OpenAPI schema was renamed
111
+ # from "Asset" to "UploadedAsset".
112
+ Asset = UploadedAsset
113
+
114
+
110
115
  class Bookmark(BaseModel):
111
116
  id: str
112
117
  createdAt: str
@@ -114,7 +119,7 @@ class Bookmark(BaseModel):
114
119
  title: Optional[str] = None
115
120
  archived: bool
116
121
  favourited: bool
117
- taggingStatus: Literal["success", "failure", "pending"]
122
+ taggingStatus: Optional[Literal["success", "failure", "pending"]] = None
118
123
  summarizationStatus: Optional[Literal["success", "failure", "pending"]] = None
119
124
  note: Optional[str] = None
120
125
  summary: Optional[str] = None
@@ -184,3 +189,14 @@ class Backup(BaseModel):
184
189
  bookmarkCount: int
185
190
  status: Literal["pending", "success", "failure"]
186
191
  errorMessage: Optional[str] = None
192
+
193
+
194
+ class Feed(BaseModel):
195
+ id: str
196
+ name: str
197
+ url: str
198
+ enabled: bool
199
+ importTags: bool
200
+ lastFetchedStatus: Optional[Literal["success", "failure", "pending"]]
201
+ lastFetchedAt: Optional[str]
202
+ lastSuccessfulFetchAt: Optional[str]
@@ -85,7 +85,7 @@ class KarakeepAPI:
85
85
  """
86
86
 
87
87
  # Version reflects the client library version, updated by bumpver
88
- VERSION: str = "1.7.0"
88
+ VERSION: str = "1.8.0"
89
89
 
90
90
  def __init__(
91
91
  self,
@@ -2182,7 +2182,7 @@ class KarakeepAPI:
2182
2182
  @optional_typecheck
2183
2183
  def upload_a_new_asset(
2184
2184
  self, file: str
2185
- ) -> Union[datatypes.Asset, Dict[str, Any], List[Any]]:
2185
+ ) -> Union[datatypes.UploadedAsset, Dict[str, Any], List[Any]]:
2186
2186
  """
2187
2187
  Upload a new asset file. Corresponds to POST /assets.
2188
2188
 
@@ -2190,7 +2190,7 @@ class KarakeepAPI:
2190
2190
  file: Path to the file to upload.
2191
2191
 
2192
2192
  Returns:
2193
- datatypes.Asset: Details about the uploaded asset (assetId, contentType, size, fileName).
2193
+ datatypes.UploadedAsset: Details about the uploaded asset (assetId, contentType, size, fileName).
2194
2194
  If response validation is disabled, returns the raw API response (dict/list).
2195
2195
 
2196
2196
  Raises:
@@ -2233,7 +2233,7 @@ class KarakeepAPI:
2233
2233
  return response_data
2234
2234
  else:
2235
2235
  # Response should match Asset schema
2236
- return datatypes.Asset.model_validate(response_data)
2236
+ return datatypes.UploadedAsset.model_validate(response_data)
2237
2237
 
2238
2238
  @optional_typecheck
2239
2239
  def get_all_backups(
@@ -2480,3 +2480,251 @@ class KarakeepAPI:
2480
2480
 
2481
2481
  logger.error(error_msg)
2482
2482
  raise APIError(error_msg)
2483
+
2484
+ # --- Admin: Job Triggers ---
2485
+
2486
+ @optional_typecheck
2487
+ def admin_trigger_recrawl(
2488
+ self,
2489
+ crawl_status: Literal["success", "failure", "pending", "all"] = "all",
2490
+ run_inference: bool = False,
2491
+ ) -> Dict[str, Any]:
2492
+ """
2493
+ Trigger a recrawl of link bookmarks. Admin only.
2494
+ Corresponds to POST /admin/jobs/trigger/recrawl.
2495
+
2496
+ Args:
2497
+ crawl_status: Filter bookmarks by their current crawl status.
2498
+ Use "failure" to retry only failed crawls. Default: "all".
2499
+ run_inference: Whether to run AI inference after crawling. Default: False.
2500
+
2501
+ Returns:
2502
+ dict: A dictionary with a "success" boolean field.
2503
+
2504
+ Raises:
2505
+ APIError: If the API request fails (e.g., 403 admin access required).
2506
+ """
2507
+ body = {"crawlStatus": crawl_status, "runInference": run_inference}
2508
+ return self._call("POST", "admin/jobs/trigger/recrawl", data=body)
2509
+
2510
+ @optional_typecheck
2511
+ def admin_trigger_reindex(self) -> Dict[str, Any]:
2512
+ """
2513
+ Trigger a reindex of all bookmarks in the search engine. Admin only.
2514
+ Corresponds to POST /admin/jobs/trigger/reindex.
2515
+
2516
+ Clears the existing index and re-queues all bookmarks for indexing.
2517
+
2518
+ Returns:
2519
+ dict: A dictionary with a "success" boolean field.
2520
+
2521
+ Raises:
2522
+ APIError: If the API request fails (e.g., 403 admin access required).
2523
+ """
2524
+ return self._call("POST", "admin/jobs/trigger/reindex")
2525
+
2526
+ @optional_typecheck
2527
+ def admin_trigger_inference(
2528
+ self,
2529
+ type: Literal["tag", "summarize"],
2530
+ status: Literal["success", "failure", "pending", "all"] = "all",
2531
+ ) -> Dict[str, Any]:
2532
+ """
2533
+ Trigger AI inference (tagging or summarization) on bookmarks. Admin only.
2534
+ Corresponds to POST /admin/jobs/trigger/inference.
2535
+
2536
+ Args:
2537
+ type: The type of inference to run: "tag" for AI tagging,
2538
+ "summarize" for AI summarization.
2539
+ status: Filter bookmarks by their current inference status.
2540
+ Use "failure" to retry only failed ones. Default: "all".
2541
+
2542
+ Returns:
2543
+ dict: A dictionary with a "success" boolean field.
2544
+
2545
+ Raises:
2546
+ APIError: If the API request fails (e.g., 403 admin access required).
2547
+ """
2548
+ body = {"type": type, "status": status}
2549
+ return self._call("POST", "admin/jobs/trigger/inference", data=body)
2550
+
2551
+ # --- Feeds ---
2552
+
2553
+ @optional_typecheck
2554
+ def get_all_feeds(
2555
+ self,
2556
+ ) -> Union[List[datatypes.Feed], Dict[str, Any], List[Any]]:
2557
+ """
2558
+ Get all RSS feed subscriptions for the current user. Corresponds to GET /feeds.
2559
+
2560
+ Returns:
2561
+ List[datatypes.Feed]: A list of feed objects.
2562
+ If response validation is disabled, returns the raw API response (dict/list).
2563
+
2564
+ Raises:
2565
+ APIError: If the API request fails.
2566
+ pydantic.ValidationError: If response validation fails (and is not disabled).
2567
+ """
2568
+ response_data = self._call("GET", "feeds")
2569
+
2570
+ if self.disable_response_validation:
2571
+ logger.debug("Skipping response validation as requested.")
2572
+ return response_data
2573
+ if (
2574
+ isinstance(response_data, dict)
2575
+ and "feeds" in response_data
2576
+ and isinstance(response_data["feeds"], list)
2577
+ ):
2578
+ return [
2579
+ datatypes.Feed.model_validate(feed) for feed in response_data["feeds"]
2580
+ ]
2581
+ raise APIError(
2582
+ f"Unexpected response format for get_all_feeds when validation is enabled: {response_data}"
2583
+ )
2584
+
2585
+ @optional_typecheck
2586
+ def create_a_new_feed(
2587
+ self,
2588
+ name: str,
2589
+ url: str,
2590
+ enabled: bool = True,
2591
+ import_tags: bool = False,
2592
+ ) -> Union[datatypes.Feed, Dict[str, Any], List[Any]]:
2593
+ """
2594
+ Create a new RSS feed subscription. Corresponds to POST /feeds.
2595
+
2596
+ Args:
2597
+ name: Display name for the feed (1-100 characters).
2598
+ url: The RSS feed URL.
2599
+ enabled: Whether the feed is active and will be fetched (default: True).
2600
+ import_tags: Whether to import tags from the feed items (default: False).
2601
+
2602
+ Returns:
2603
+ datatypes.Feed: The created feed object.
2604
+ If response validation is disabled, returns the raw API response (dict/list).
2605
+
2606
+ Raises:
2607
+ APIError: If the API request fails (e.g., 400 quota exceeded).
2608
+ pydantic.ValidationError: If response validation fails (and is not disabled).
2609
+ """
2610
+ feed_data = {
2611
+ "name": name,
2612
+ "url": url,
2613
+ "enabled": enabled,
2614
+ "importTags": import_tags,
2615
+ }
2616
+ response_data = self._call("POST", "feeds", data=feed_data)
2617
+
2618
+ if self.disable_response_validation:
2619
+ logger.debug("Skipping response validation as requested.")
2620
+ return response_data
2621
+ return datatypes.Feed.model_validate(response_data)
2622
+
2623
+ @optional_typecheck
2624
+ def get_a_single_feed(
2625
+ self, feed_id: str
2626
+ ) -> Union[datatypes.Feed, Dict[str, Any], List[Any]]:
2627
+ """
2628
+ Get a single RSS feed by its ID. Corresponds to GET /feeds/{feedId}.
2629
+
2630
+ Args:
2631
+ feed_id: The ID (string) of the feed to retrieve.
2632
+
2633
+ Returns:
2634
+ datatypes.Feed: The requested feed object.
2635
+ If response validation is disabled, returns the raw API response (dict/list).
2636
+
2637
+ Raises:
2638
+ APIError: If the API request fails (e.g., 404 feed not found).
2639
+ """
2640
+ response_data = self._call("GET", f"feeds/{feed_id}")
2641
+
2642
+ if self.disable_response_validation:
2643
+ logger.debug("Skipping response validation as requested.")
2644
+ return response_data
2645
+ return datatypes.Feed.model_validate(response_data)
2646
+
2647
+ @optional_typecheck
2648
+ def update_a_feed(
2649
+ self,
2650
+ feed_id: str,
2651
+ name: Optional[str] = None,
2652
+ url: Optional[str] = None,
2653
+ enabled: Optional[bool] = None,
2654
+ import_tags: Optional[bool] = None,
2655
+ ) -> Union[datatypes.Feed, Dict[str, Any], List[Any]]:
2656
+ """
2657
+ Update an RSS feed subscription. Corresponds to PATCH /feeds/{feedId}.
2658
+
2659
+ Args:
2660
+ feed_id: The ID (string) of the feed to update.
2661
+ name: Optional new display name for the feed (1-100 characters).
2662
+ url: Optional new feed URL.
2663
+ enabled: Optional new enabled state.
2664
+ import_tags: Optional new importTags flag.
2665
+
2666
+ Returns:
2667
+ datatypes.Feed: The updated feed object.
2668
+ If response validation is disabled, returns the raw API response (dict/list).
2669
+
2670
+ Raises:
2671
+ ValueError: If no fields are provided to update.
2672
+ APIError: If the API request fails (e.g., 404 feed not found).
2673
+ """
2674
+ update_data: Dict[str, Any] = {}
2675
+ if name is not None:
2676
+ update_data["name"] = name
2677
+ if url is not None:
2678
+ update_data["url"] = url
2679
+ if enabled is not None:
2680
+ update_data["enabled"] = enabled
2681
+ if import_tags is not None:
2682
+ update_data["importTags"] = import_tags
2683
+
2684
+ if not update_data:
2685
+ raise ValueError("At least one field must be provided to update.")
2686
+
2687
+ response_data = self._call("PATCH", f"feeds/{feed_id}", data=update_data)
2688
+
2689
+ if self.disable_response_validation:
2690
+ logger.debug("Skipping response validation as requested.")
2691
+ return response_data
2692
+ return datatypes.Feed.model_validate(response_data)
2693
+
2694
+ @optional_typecheck
2695
+ def delete_a_feed(self, feed_id: str) -> None:
2696
+ """
2697
+ Delete an RSS feed subscription. Corresponds to DELETE /feeds/{feedId}.
2698
+
2699
+ Previously imported bookmarks are not affected.
2700
+
2701
+ Args:
2702
+ feed_id: The ID (string) of the feed to delete.
2703
+
2704
+ Returns:
2705
+ None: Returns None upon successful deletion (204 No Content).
2706
+
2707
+ Raises:
2708
+ APIError: If the API request fails (e.g., 404 feed not found).
2709
+ """
2710
+ self._call("DELETE", f"feeds/{feed_id}")
2711
+ return None
2712
+
2713
+ @optional_typecheck
2714
+ def fetch_a_feed(self, feed_id: str) -> None:
2715
+ """
2716
+ Trigger an immediate fetch of an RSS feed. Corresponds to POST /feeds/{feedId}/fetch.
2717
+
2718
+ The fetch is enqueued and processed asynchronously by the server.
2719
+
2720
+ Args:
2721
+ feed_id: The ID (string) of the feed to fetch.
2722
+
2723
+ Returns:
2724
+ None: Returns None upon successful enqueue (204 No Content).
2725
+
2726
+ Raises:
2727
+ APIError: If the API request fails (e.g., 404 feed not found).
2728
+ """
2729
+ self._call("POST", f"feeds/{feed_id}/fetch")
2730
+ return None