Source code for nyora.services.library

"""Library, history, favourites, bookmarks, and categories."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from nyora.models import Category, HistoryEntry, Manga

if TYPE_CHECKING:
    from nyora.client import Nyora


[docs] class LibraryService: """Manage reading history, favourites, bookmarks, and categories. Attached to a client as ``client.library``. """ def __init__(self, client: Nyora) -> None: """Bind the service to a helper client. Args: client: The owning :class:`nyora.client.Nyora` instance. """ self._client = client
[docs] def history(self, limit: int = 100) -> list[HistoryEntry]: """Return recent reading history. Args: limit: Maximum number of entries to return. Returns: The most recent :class:`~nyora.models.HistoryEntry` records. """ data = self._client.get("/library/history", params={"limit": limit}) return [HistoryEntry.from_json(item) for item in data.get("entries", [])]
[docs] def record_history( self, *, manga_id: str, chapter_id: str, page: int, percent: float, ) -> dict[str, Any]: """Record reading progress for a chapter. Args: manga_id: Identifier of the manga. chapter_id: Identifier of the chapter read. page: Last-read page index. percent: Read progress fraction within the chapter. Returns: The raw helper response. """ return self._client.post( "/library/history/record", params={ "mangaId": manga_id, "chapterId": chapter_id, "page": page, "percent": percent, }, )
[docs] def remove_history(self, manga_id: str, chapter_id: str | None = None) -> dict[str, Any]: """Remove history for a manga, optionally narrowed to one chapter. Args: manga_id: Identifier of the manga. chapter_id: Optional chapter to remove; omit to remove all history for the manga. Returns: The raw helper response. """ params = {"mangaId": manga_id} if chapter_id: params["chapterId"] = chapter_id return self._client.post("/library/history/remove", params=params)
[docs] def clear_history(self) -> dict[str, Any]: """Clear all reading history. Returns: The raw helper response. """ return self._client.post("/library/history/clear")
[docs] def favourites(self, category_id: int | None = None) -> list[Manga]: """List favourited manga, optionally filtered by category. Args: category_id: Optional category to filter by. Returns: The favourited :class:`~nyora.models.Manga` entries. """ params = {"categoryId": category_id} if category_id is not None else None data = self._client.get("/library/favourites", params=params) return [Manga.from_json(item) for item in data.get("entries", [])]
[docs] def toggle_favourite(self, manga_id: str) -> dict[str, Any]: """Toggle a manga's favourite state. Args: manga_id: Identifier of the manga. Returns: The raw helper response. """ return self._client.post("/library/favourites/toggle", params={"mangaId": manga_id})
[docs] def is_favourite(self, manga_id: str) -> bool: """Check whether a manga is favourited. Args: manga_id: Identifier of the manga. Returns: ``True`` if the manga is favourited. """ data = self._client.get("/library/favourites/check", params={"mangaId": manga_id}) return bool(data.get("isFavourite", data.get("present", False)))
[docs] def bookmarks(self, manga_id: str | None = None) -> list[dict[str, Any]]: """List bookmarks, optionally scoped to one manga. Args: manga_id: Optional manga to scope to. Returns: Raw bookmark dicts. """ params = {"mangaId": manga_id} if manga_id else None data = self._client.get("/library/bookmarks", params=params) return data.get("entries", [])
[docs] def add_bookmark( self, *, manga_id: str, chapter_id: str, page: int, title: str = "", ) -> dict[str, Any]: """Add a bookmark at a specific page. Args: manga_id: Identifier of the manga. chapter_id: Identifier of the chapter. page: Page index to bookmark. title: Optional bookmark label. Returns: The raw helper response. """ return self._client.post( "/library/bookmarks/add", params={ "mangaId": manga_id, "chapterId": chapter_id, "page": page, "title": title, }, )
[docs] def remove_bookmark(self, *, manga_id: str, chapter_id: str, page: int) -> dict[str, Any]: """Remove a bookmark at a specific page. Args: manga_id: Identifier of the manga. chapter_id: Identifier of the chapter. page: Page index of the bookmark to remove. Returns: The raw helper response. """ return self._client.post( "/library/bookmarks/remove", params={"mangaId": manga_id, "chapterId": chapter_id, "page": page}, )
[docs] def categories(self) -> list[Category]: """List the user's library categories. Returns: The :class:`~nyora.models.Category` records. """ data = self._client.get("/library/categories") return [Category.from_json(item) for item in data.get("categories", [])]
[docs] def create_category(self, title: str) -> Category | dict[str, Any]: """Create a new library category. Args: title: Title for the new category. Returns: The created :class:`~nyora.models.Category`, or the raw response. """ data = self._client.post("/library/categories/create", params={"title": title}) return Category.from_json(data["category"]) if "category" in data else data
[docs] def rename_category(self, category_id: int, title: str) -> dict[str, Any]: """Rename a category. Args: category_id: Identifier of the category. title: New title. Returns: The raw helper response. """ return self._client.post( "/library/categories/rename", params={"id": category_id, "title": title}, )
[docs] def delete_category(self, category_id: int) -> dict[str, Any]: """Delete a category. Args: category_id: Identifier of the category to delete. Returns: The raw helper response. """ return self._client.post("/library/categories/delete", params={"id": category_id})
[docs] def add_to_category(self, manga_id: str, category_id: int) -> dict[str, Any]: """Add a manga to a category. Args: manga_id: Identifier of the manga. category_id: Identifier of the category. Returns: The raw helper response. """ return self._client.post( "/library/categories/add", params={"mangaId": manga_id, "categoryId": category_id}, )
[docs] def remove_from_category(self, manga_id: str, category_id: int) -> dict[str, Any]: """Remove a manga from a category. Args: manga_id: Identifier of the manga. category_id: Identifier of the category. Returns: The raw helper response. """ return self._client.post( "/library/categories/remove", params={"mangaId": manga_id, "categoryId": category_id}, )
[docs] def updates(self) -> list[dict[str, Any]]: """List pending library update entries. Returns: Raw update-entry dicts. """ return self._client.get("/library/updates").get("entries", [])
[docs] def refresh_updates(self) -> dict[str, Any]: """Trigger a refresh of library updates. Returns: The raw helper response. """ return self._client.post("/library/updates/refresh")
[docs] def mark_update_seen(self, update_id: str) -> dict[str, Any]: """Mark a library update as seen. Args: update_id: Identifier of the update entry. Returns: The raw helper response. """ return self._client.post("/library/updates/seen", params={"id": update_id})