update codebase
This commit is contained in:
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"python.testing.pytestArgs": [
|
||||
"tests"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true
|
||||
}
|
||||
@@ -7,8 +7,19 @@ authors = [
|
||||
{ name = "WorldTeacher", email = "coding_contact@pm.me" }
|
||||
]
|
||||
requires-python = ">=3.13"
|
||||
dependencies = []
|
||||
dependencies = [
|
||||
"komconfig",
|
||||
"typing-extensions>=4.12.2",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[dependency-groups]
|
||||
test = [
|
||||
"pytest>=8.3.4",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
komconfig = { workspace = true }
|
||||
|
||||
@@ -3,6 +3,14 @@ from komgapi.errors import KomgaError, LoginError, ResultErrror
|
||||
from typing import Any, Union
|
||||
from limit import limit
|
||||
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add("logs/komga_api.log", rotation="1 week", retention="1 month")
|
||||
log.add(sys.stdout, level="INFO")
|
||||
|
||||
|
||||
class BaseAPI:
|
||||
def __init__(self, username, password, url, timeout=20, api_version=1) -> None:
|
||||
@@ -39,15 +47,16 @@ class BaseAPI:
|
||||
if params is None:
|
||||
params = {}
|
||||
try:
|
||||
# ic(url, params)
|
||||
response = requests.get(
|
||||
url,
|
||||
auth=(self._username, self._password),
|
||||
params=params,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
response.raise_for_status()
|
||||
if response.status_code != 200:
|
||||
self.getRequest(url, params)
|
||||
# print(response.content)
|
||||
log.debug(f"Response: {response.content}")
|
||||
return response.json()
|
||||
except ConnectionError as e:
|
||||
message = f"Connection Error: {e}"
|
||||
@@ -55,22 +64,37 @@ class BaseAPI:
|
||||
except requests.exceptions.Timeout as e:
|
||||
raise KomgaError(f"Timeout Error: {e}") from e
|
||||
|
||||
def postRequest(self, url, data: Union[dict, None] = None):
|
||||
def postRequest(self, url, data: Union[dict, None] = None, body: dict = None):
|
||||
if data is None:
|
||||
data = {}
|
||||
try:
|
||||
response = requests.post(
|
||||
url,
|
||||
auth=(self._username, self._password),
|
||||
json=data,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
if body is not None:
|
||||
response = requests.post(
|
||||
url,
|
||||
auth=(self._username, self._password),
|
||||
json=body,
|
||||
params=data,
|
||||
timeout=self.timeout,
|
||||
)
|
||||
else:
|
||||
response = requests.post(
|
||||
url,
|
||||
auth=(self._username, self._password),
|
||||
json=data,
|
||||
timeout=self.timeout,
|
||||
params=data,
|
||||
)
|
||||
response.raise_for_status()
|
||||
status_code = response.status_code
|
||||
if status_code != 202:
|
||||
raise ResultErrror(f"Result Error: {response.json()}")
|
||||
if status_code == 202:
|
||||
log.debug(f"Response: {response}")
|
||||
# raise ResultErrror(f"Result Error: {response}")
|
||||
elif status_code == 200:
|
||||
log.debug(f"Response: {response}")
|
||||
return response.json()
|
||||
else:
|
||||
log.debug(f"Response: {response}")
|
||||
raise ResultErrror(f"Result Error: {response.content}")
|
||||
except ConnectionError as e:
|
||||
message = f"Connection Error: {e}"
|
||||
raise KomgaError(message) from e
|
||||
@@ -81,6 +105,7 @@ class BaseAPI:
|
||||
if data is None:
|
||||
data = {}
|
||||
try:
|
||||
print("patching data", data, url)
|
||||
response = requests.patch(
|
||||
url,
|
||||
auth=(self._username, self._password),
|
||||
@@ -88,6 +113,10 @@ class BaseAPI:
|
||||
timeout=self.timeout,
|
||||
)
|
||||
response.raise_for_status()
|
||||
log.debug(
|
||||
f"Response: {response}, {response.status_code}, {response.content}"
|
||||
)
|
||||
print(response.status_code, response.content)
|
||||
if response.status_code != 204:
|
||||
raise ResultErrror(f"Result Error: {response.json()}")
|
||||
except ConnectionError as e:
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
from .baseapi import BaseAPI
|
||||
import pathlib
|
||||
import subprocess
|
||||
import requests
|
||||
import typing_extensions
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from komgapi.errors import KomgaError, LoginError, ResultErrror
|
||||
from komgapi.schemas import * # Progress, Series
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,14 @@ from typing import List, Optional, Dict, Any, Union
|
||||
|
||||
from komgapi.schemas import * # Progress, Series
|
||||
|
||||
import loguru
|
||||
import sys
|
||||
|
||||
log = loguru.logger
|
||||
log.remove()
|
||||
log.add("logs/komgapi.log", rotation="1 week", retention="1 month")
|
||||
log.add(sys.stdout)
|
||||
|
||||
|
||||
class SeriesController(BaseAPI):
|
||||
def __init__(self, username, password, url, timeout=20) -> None:
|
||||
@@ -14,56 +22,39 @@ class SeriesController(BaseAPI):
|
||||
|
||||
def getAllSeries(
|
||||
self,
|
||||
library_id: List[str] = None,
|
||||
collection_id: List[str] = None,
|
||||
status: List[str] = None,
|
||||
publisher: List[str] = None,
|
||||
lang: List[str] = None,
|
||||
genre: List[str] = None,
|
||||
tag: List[str] = None,
|
||||
age_rating: List[str] = None,
|
||||
release_year: List[str] = None,
|
||||
deleted: bool = None,
|
||||
complete: bool = None,
|
||||
unpaged: bool = True,
|
||||
sort: List[str] = None,
|
||||
author: List[str] = None,
|
||||
oneshot: bool = None,
|
||||
size: int = None,
|
||||
page: int = None,
|
||||
body: dict[Any, Any] = {},
|
||||
) -> list[Series]:
|
||||
"""Get all series from the server.
|
||||
By default, this will return all series in the server. You can filter the results by using the parameters.
|
||||
"""
|
||||
Get all series from the server that match the query.
|
||||
|
||||
Args:
|
||||
----
|
||||
- library_id (List[str], optional): The library to be queried. If None, all available libraries will be used. Defaults to None.
|
||||
- collection_id (List[str], optional): The collection to be queried. Defaults to None.
|
||||
- status (List[str], optional): The status of the series. Can be: ENDED,ONGOING,ABANDONED,HIATUS. Defaults to None.
|
||||
- publisher (List[str], optional): Publisher(s) to be searched for. Defaults to None.
|
||||
- lang (List[str], optional): Language to query for. Uses two-letter codec. Defaults to None.
|
||||
- genre (List[str], optional): Genre(s) to query for. Defaults to None.
|
||||
- tag (List[str], optional): Tag(s) to query. Defaults to None.
|
||||
- age_rating (List[str], optional): A custom age-rating to search for. Needs to be configured manually. Defaults to None.
|
||||
- release_year (List[str], optional): When the series were released. Defaults to None.
|
||||
- deleted (bool, optional): If the series is deleted. Defaults to None.
|
||||
- complete (bool, optional): Turn to true to only search series that are complete. Complete requires TotalBookCount to be set and equal to BookCount. Defaults to None.
|
||||
- unpaged (bool, optional): Set to False if a single Page of results should be returned. By default, a page contains 20 entries. Defaults to True.
|
||||
- sort (List[str], optional): Sorting of the returned data. Sort using asc|desc. Multiple sort criteria are supported. Defaults to None.
|
||||
- author (List[str], optional): Author(s) to include in the query. Defaults to None.
|
||||
- oneshot (bool, optional): If the series is categorized as oneshot. Defaults to None.
|
||||
- size (int, optional): The size of the page. Defaults to None.
|
||||
- page (int, optional): The page to be returned. Defaults to None.
|
||||
Parameters
|
||||
----------
|
||||
unpaged : bool, optional
|
||||
limits the amount of results. If set to false, 20 entries will be returned, by default True
|
||||
sort : List[str], optional
|
||||
sorting parameter, by default None
|
||||
size : int, optional
|
||||
How many entries should be returned. Set to None to get all entries, by default None
|
||||
page : int, optional
|
||||
If unpaged is False and size is set, this allows the selection of a subset of result, by default None
|
||||
body : dict, optional
|
||||
The query that requests the data from the API, by default {}
|
||||
|
||||
Returns:
|
||||
Returns
|
||||
-------
|
||||
- list[Series]: a list of all series that match the query. Each series is represented as a Series object.
|
||||
list[Series]
|
||||
The result of the query. Each series is represented as a Series object.
|
||||
"""
|
||||
|
||||
params = locals()
|
||||
body = params.pop("body")
|
||||
params = self.setParams(params)
|
||||
url = self.url + "series"
|
||||
data = self.getRequest(url, params)
|
||||
url = self.url + "series/list"
|
||||
data = self.postRequest(url, data=params, body=body)
|
||||
ret = []
|
||||
for series in data["content"]:
|
||||
ret.append(Series(**series))
|
||||
@@ -250,6 +241,8 @@ class SeriesController(BaseAPI):
|
||||
"""
|
||||
url = self.url + f"series/{series_id}/metadata"
|
||||
data = self.patchRequest(url, changed_metadata)
|
||||
log.debug("Changed metadata: {}", data)
|
||||
|
||||
return data
|
||||
|
||||
def refreshMetadata(self, series_id: str) -> Optional[Dict[str, Any]]:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
"""Generic API for KOMGA"""
|
||||
|
||||
import requests
|
||||
@@ -28,30 +27,37 @@ class KOMGAPI_REST:
|
||||
self.timeout = timeout
|
||||
if not url.endswith("/"):
|
||||
url += "/"
|
||||
self.common_book_controller = CommonBookController(username, password, url, timeout)
|
||||
self.common_book_controller = CommonBookController(
|
||||
username, password, url, timeout
|
||||
)
|
||||
self.series_controller = SeriesController(username, password, url, timeout)
|
||||
self.readlist_controller = ReadListController(username, password, url, timeout)
|
||||
self.page_hash_controller = None
|
||||
self.library_controller = LibraryController(username, password, url, timeout)
|
||||
self.series_collection_controller = SeriesCollectionController(username, password, url, timeout)
|
||||
self.series_collection_controller = SeriesCollectionController(
|
||||
username, password, url, timeout
|
||||
)
|
||||
self.book_controler = BookController(username, password, url, timeout)
|
||||
self.announcement_controller = AnnouncementController(username, password, url, timeout)
|
||||
self.announcement_controller = AnnouncementController(
|
||||
username, password, url, timeout
|
||||
)
|
||||
self.user_controller = UserController(username, password, url, timeout)
|
||||
self.transient_books_controller = None
|
||||
self.file_system_controller = None
|
||||
self.claim_controller = ClaimController(username, password, url, timeout)
|
||||
self.settings_controller = SettingsController(username, password, url, timeout)
|
||||
self.referential_controller = ReferentialController(username, password, url, timeout)
|
||||
self.referential_controller = ReferentialController(
|
||||
username, password, url, timeout
|
||||
)
|
||||
self.login_controller = None
|
||||
self.historical_events_controller = None
|
||||
self.task_controller = None
|
||||
self.sync_point_controller = None
|
||||
self.oauth2_controller = None
|
||||
|
||||
|
||||
def notImplemented(self):
|
||||
raise NotImplementedError("Not implemented yet")
|
||||
|
||||
|
||||
|
||||
def from_env(self):
|
||||
"""Create a KOMGA API object from environment variables.
|
||||
|
||||
@@ -65,7 +71,7 @@ class KOMGAPI_REST:
|
||||
os.environ["KOMGA_PASSWORD"],
|
||||
os.environ["KOMGA_URL"],
|
||||
)
|
||||
|
||||
|
||||
def test_connection(self):
|
||||
"""Test the connection to the KOMGA server.
|
||||
|
||||
@@ -73,18 +79,11 @@ class KOMGAPI_REST:
|
||||
bool: True if the connection is successful, False otherwise.
|
||||
"""
|
||||
try:
|
||||
requests.get(self.url, auth=(self._username, self._password), timeout=self.timeout)
|
||||
requests.get(
|
||||
self.url, auth=(self._username, self._password), timeout=self.timeout
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Book controller
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class AlternateTitle(BaseModel):
|
||||
@dataclass
|
||||
class AlternateTitle:
|
||||
label: str
|
||||
title: str
|
||||
|
||||
@@ -2,11 +2,12 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
from komgapi.schemas.Author import Author
|
||||
|
||||
|
||||
class AnnouncementItem(BaseModel):
|
||||
@dataclass
|
||||
class AnnouncementItem:
|
||||
id: str
|
||||
url: str
|
||||
title: str
|
||||
@@ -16,7 +17,8 @@ class AnnouncementItem(BaseModel):
|
||||
author: Author
|
||||
|
||||
|
||||
class Announcement(BaseModel):
|
||||
@dataclass
|
||||
class Announcement:
|
||||
version: str
|
||||
title: str
|
||||
home_page_url: str
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class UserAccess(BaseModel):
|
||||
@dataclass
|
||||
class UserAccess:
|
||||
userId: str
|
||||
email: str
|
||||
apiKeyId: str
|
||||
@@ -18,13 +19,15 @@ class UserAccess(BaseModel):
|
||||
source: str
|
||||
|
||||
|
||||
|
||||
class Sort(BaseModel):
|
||||
@dataclass
|
||||
class Sort:
|
||||
empty: bool
|
||||
sorted: bool
|
||||
unsorted: bool
|
||||
|
||||
class Pageable(BaseModel):
|
||||
|
||||
@dataclass
|
||||
class Pageable:
|
||||
offset: int
|
||||
sort: Sort
|
||||
pageNumber: int
|
||||
@@ -33,7 +36,8 @@ class Pageable(BaseModel):
|
||||
unpaged: bool
|
||||
|
||||
|
||||
class Authentication(BaseModel):
|
||||
@dataclass
|
||||
class Authentication:
|
||||
totalElements: int
|
||||
totalPages: int
|
||||
size: int
|
||||
@@ -45,34 +49,27 @@ class Authentication(BaseModel):
|
||||
numberOfElements: int
|
||||
pageable: Pageable
|
||||
empty: bool
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Sort(BaseModel):
|
||||
@dataclass
|
||||
class Sort:
|
||||
empty: bool
|
||||
sorted: bool
|
||||
unsorted: bool
|
||||
|
||||
|
||||
class Sort1(BaseModel):
|
||||
empty: bool
|
||||
sorted: bool
|
||||
unsorted: bool
|
||||
|
||||
|
||||
class Pageable(BaseModel):
|
||||
@dataclass
|
||||
class Pageable:
|
||||
offset: int
|
||||
sort: Sort1
|
||||
sort: Sort
|
||||
pageNumber: int
|
||||
pageSize: int
|
||||
paged: bool
|
||||
unpaged: bool
|
||||
|
||||
|
||||
class UserAuthActivity(BaseModel):
|
||||
@dataclass
|
||||
class UserAuthActivity:
|
||||
totalElements: int
|
||||
totalPages: int
|
||||
size: int
|
||||
@@ -83,4 +80,4 @@ class UserAuthActivity(BaseModel):
|
||||
last: bool
|
||||
numberOfElements: int
|
||||
pageable: Pageable
|
||||
empty: bool
|
||||
empty: bool
|
||||
|
||||
@@ -2,20 +2,24 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Author(BaseModel):
|
||||
@dataclass
|
||||
class Author:
|
||||
name: str
|
||||
role: str
|
||||
|
||||
|
||||
class Sort(BaseModel):
|
||||
@dataclass
|
||||
class Sort:
|
||||
empty: bool
|
||||
sorted: bool
|
||||
unsorted: bool
|
||||
|
||||
class Pageable(BaseModel):
|
||||
|
||||
@dataclass
|
||||
class Pageable:
|
||||
offset: int
|
||||
sort: Sort
|
||||
pageNumber: int
|
||||
@@ -24,7 +28,8 @@ class Pageable(BaseModel):
|
||||
unpaged: bool
|
||||
|
||||
|
||||
class AuthorResponse(BaseModel):
|
||||
@dataclass
|
||||
class AuthorResponse:
|
||||
totalElements: int
|
||||
totalPages: int
|
||||
size: int
|
||||
@@ -35,4 +40,4 @@ class AuthorResponse(BaseModel):
|
||||
last: bool
|
||||
numberOfElements: int
|
||||
pageable: Pageable
|
||||
empty: bool
|
||||
empty: bool
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .Author import Author
|
||||
from .BooksMetadata import BookMetadata
|
||||
@@ -10,7 +10,8 @@ from .Link import Link
|
||||
from .Media import Media
|
||||
|
||||
|
||||
class Book(BaseModel):
|
||||
@dataclass
|
||||
class Book:
|
||||
id: Optional[str] = None
|
||||
seriesId: Optional[str] = None
|
||||
seriesTitle: Optional[str] = None
|
||||
|
||||
@@ -2,13 +2,14 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .Author import Author
|
||||
from .Link import Link
|
||||
|
||||
|
||||
class BooksMetadata(BaseModel):
|
||||
@dataclass
|
||||
class BooksMetadata:
|
||||
authors: Optional[List[Author]] = None
|
||||
tags: Optional[List[str]] = None
|
||||
releaseDate: Optional[str] = None
|
||||
@@ -18,7 +19,8 @@ class BooksMetadata(BaseModel):
|
||||
lastModified: Optional[str] = None
|
||||
|
||||
|
||||
class BookMetadata(BaseModel):
|
||||
@dataclass
|
||||
class BookMetadata:
|
||||
title: Optional[str] = None
|
||||
titleLock: Optional[bool] = None
|
||||
summary: Optional[str] = None
|
||||
@@ -29,13 +31,13 @@ class BookMetadata(BaseModel):
|
||||
numberSortLock: Optional[bool] = None
|
||||
releaseDate: Optional[str] = None
|
||||
releaseDateLock: Optional[bool] = None
|
||||
authors: List[Author]
|
||||
authors: List[Author] = None
|
||||
authorsLock: Optional[bool] = None
|
||||
tags: List
|
||||
tags: List = None
|
||||
tagsLock: Optional[bool] = None
|
||||
isbn: Optional[str] = None
|
||||
isbnLock: Optional[bool] = None
|
||||
links: List[Link]
|
||||
links: List[Link] = None
|
||||
linksLock: Optional[bool] = None
|
||||
created: Optional[str] = None
|
||||
lastModified: Optional[str] = None
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Collection(BaseModel):
|
||||
@dataclass
|
||||
class Collection:
|
||||
id: str
|
||||
name: str
|
||||
ordered: bool
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
from __future__ import annotations
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
from .Sort import Sort
|
||||
from .Pageable import Pageable
|
||||
from .Book import Book
|
||||
|
||||
class Duplicate(BaseModel):
|
||||
|
||||
@dataclass
|
||||
class Duplicate:
|
||||
totalElements: Optional[int] = None
|
||||
totalPages: Optional[int] = None
|
||||
size: Optional[int] = None
|
||||
@@ -16,4 +18,4 @@ class Duplicate(BaseModel):
|
||||
last: Optional[bool] = None
|
||||
numberOfElements: Optional[int] = None
|
||||
pageable: Optional[Pageable] = None
|
||||
empty: Optional[bool] = None
|
||||
empty: Optional[bool] = None
|
||||
|
||||
@@ -2,14 +2,15 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .Pageable import Pageable
|
||||
from .Series import Series
|
||||
from .Sort import Sort
|
||||
|
||||
|
||||
class LatestSeriesData(BaseModel):
|
||||
@dataclass
|
||||
class LatestSeriesData:
|
||||
totalElements: Optional[int] = None
|
||||
totalPages: Optional[int] = None
|
||||
size: Optional[int] = None
|
||||
|
||||
@@ -2,13 +2,14 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Library(BaseModel):
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
root: Optional[str] = None
|
||||
@dataclass
|
||||
class Library:
|
||||
id: str
|
||||
name: str
|
||||
root: str
|
||||
importComicInfoBook: Optional[bool] = None
|
||||
importComicInfoSeries: Optional[bool] = None
|
||||
importComicInfoCollection: Optional[bool] = None
|
||||
@@ -35,8 +36,10 @@ class Library(BaseModel):
|
||||
analyzeDimensions: Optional[bool] = None
|
||||
oneshotsDirectory: Optional[str] = None
|
||||
unavailable: Optional[bool] = None
|
||||
|
||||
class CreateLibrary(BaseModel):
|
||||
hashKoreader: Optional[bool] = None
|
||||
|
||||
|
||||
class CreateLibrary:
|
||||
name: str
|
||||
root: str
|
||||
importComicInfoBook: bool
|
||||
@@ -64,4 +67,3 @@ class CreateLibrary(BaseModel):
|
||||
hashPages: bool
|
||||
analyzeDimensions: bool
|
||||
oneshotsDirectory: str
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Link(BaseModel):
|
||||
@dataclass
|
||||
class Link:
|
||||
label: str
|
||||
url: str
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
from __future__ import annotations
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
class Locations(BaseModel):
|
||||
|
||||
@dataclass
|
||||
class Locations:
|
||||
fragments: Optional[List[str]] = None
|
||||
progression: Optional[int] = None
|
||||
position: Optional[int] = None
|
||||
totalProgression: Optional[int] = None
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Link(BaseModel):
|
||||
@dataclass
|
||||
class Link:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -15,30 +16,35 @@ class Link(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class Series(BaseModel):
|
||||
@dataclass
|
||||
class Series:
|
||||
name: Optional[str] = None
|
||||
position: Optional[int] = None
|
||||
links: Optional[List[Link]] = None
|
||||
|
||||
|
||||
class CollectionItem(BaseModel):
|
||||
@dataclass
|
||||
class CollectionItem:
|
||||
name: Optional[str] = None
|
||||
position: Optional[int] = None
|
||||
links: Optional[List[Link]] = None
|
||||
|
||||
|
||||
class BelongsTo(BaseModel):
|
||||
@dataclass
|
||||
class BelongsTo:
|
||||
series: Optional[List[Series]] = None
|
||||
collection: Optional[List[CollectionItem]] = None
|
||||
|
||||
|
||||
class Rendition(BaseModel):
|
||||
@dataclass
|
||||
class Rendition:
|
||||
additionalProp1: Dict[str, Any]
|
||||
additionalProp2: Dict[str, Any]
|
||||
additionalProp3: Dict[str, Any]
|
||||
|
||||
|
||||
class Metadata(BaseModel):
|
||||
@dataclass
|
||||
class Metadata:
|
||||
title: Optional[str] = None
|
||||
identifier: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
@@ -67,7 +73,8 @@ class Metadata(BaseModel):
|
||||
rendition: Optional[Rendition] = None
|
||||
|
||||
|
||||
class Image(BaseModel):
|
||||
@dataclass
|
||||
class Image:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -77,7 +84,8 @@ class Image(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class ReadingOrderItem(BaseModel):
|
||||
@dataclass
|
||||
class ReadingOrderItem:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -87,7 +95,8 @@ class ReadingOrderItem(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class Resource(BaseModel):
|
||||
@dataclass
|
||||
class Resource:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -97,7 +106,8 @@ class Resource(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class TocItem(BaseModel):
|
||||
@dataclass
|
||||
class TocItem:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -107,7 +117,8 @@ class TocItem(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class Landmark(BaseModel):
|
||||
@dataclass
|
||||
class Landmark:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -117,7 +128,8 @@ class Landmark(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class PageListItem(BaseModel):
|
||||
@dataclass
|
||||
class PageListItem:
|
||||
title: Optional[str] = None
|
||||
rel: Optional[str] = None
|
||||
href: Optional[str] = None
|
||||
@@ -127,7 +139,8 @@ class PageListItem(BaseModel):
|
||||
height: Optional[int] = None
|
||||
|
||||
|
||||
class Manifest(BaseModel):
|
||||
@dataclass
|
||||
class Manifest:
|
||||
context: Optional[str] = None
|
||||
metadata: Optional[Metadata] = None
|
||||
links: Optional[List[Link]] = None
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Media(BaseModel):
|
||||
@dataclass
|
||||
class Media:
|
||||
status: str
|
||||
mediaType: str
|
||||
pagesCount: int
|
||||
|
||||
@@ -1,41 +1,45 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from typing import List, Optional, Union
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .AlternateTitle import AlternateTitle
|
||||
from .Link import Link
|
||||
from .status import Status
|
||||
|
||||
|
||||
class Metadata(BaseModel):
|
||||
status: Optional[str] | None = None
|
||||
statusLock: bool
|
||||
@dataclass
|
||||
class Metadata:
|
||||
# set status to be either ENDED, ONGOING, HIATUS, ABANDONED
|
||||
# status has to be one of the status listed in enum status
|
||||
status: Optional[Status] | None = None
|
||||
|
||||
statusLock: bool = None
|
||||
title: Optional[str] | None = None
|
||||
titleLock: bool
|
||||
titleLock: bool = None
|
||||
titleSort: Optional[str] | None = None
|
||||
titleSortLock: bool
|
||||
titleSortLock: bool = None
|
||||
summary: Optional[str] | None = None
|
||||
summaryLock: bool
|
||||
summaryLock: bool = None
|
||||
readingDirection: Optional[str] | None = None
|
||||
readingDirectionLock: bool
|
||||
readingDirectionLock: bool = None
|
||||
publisher: Optional[str] | None = None
|
||||
publisherLock: bool
|
||||
publisherLock: bool = None
|
||||
ageRating: Optional[int] | None = None
|
||||
ageRatingLock: bool
|
||||
ageRatingLock: bool = None
|
||||
language: Optional[str] | None = None
|
||||
languageLock: bool
|
||||
genres: List[str] | None
|
||||
genresLock: bool
|
||||
languageLock: bool = None
|
||||
genres: List[str] | None = None
|
||||
genresLock: bool = None
|
||||
tags: List[str] | None = None
|
||||
tagsLock: bool
|
||||
tagsLock: bool = None
|
||||
totalBookCount: Optional[int] | None = None
|
||||
totalBookCountLock: bool
|
||||
totalBookCountLock: bool = None
|
||||
sharingLabels: List[str | None] = None
|
||||
sharingLabelsLock: bool
|
||||
sharingLabelsLock: bool = None
|
||||
links: List[Link] | None = None
|
||||
linksLock: bool
|
||||
linksLock: bool = None
|
||||
alternateTitles: List[AlternateTitle] | None = None
|
||||
alternateTitlesLock: bool
|
||||
alternateTitlesLock: bool = None
|
||||
created: Optional[str] | None = None
|
||||
lastModified: Optional[str] | None = None
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Page(BaseModel):
|
||||
@dataclass
|
||||
class Page:
|
||||
number: Optional[int] = None
|
||||
fileName: Optional[str] = None
|
||||
mediaType: Optional[str] = None
|
||||
|
||||
@@ -2,15 +2,16 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .Sort import Sort
|
||||
|
||||
|
||||
class Pageable(BaseModel):
|
||||
@dataclass
|
||||
class Pageable:
|
||||
offset: int
|
||||
sort: Sort
|
||||
pageNumber: int
|
||||
pageSize: int
|
||||
paged: bool
|
||||
unpaged: bool
|
||||
unpaged: bool
|
||||
|
||||
@@ -2,14 +2,13 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
from .Text import Text
|
||||
from .Locations import Locations
|
||||
|
||||
|
||||
|
||||
|
||||
class Position(BaseModel):
|
||||
@dataclass
|
||||
class Position:
|
||||
href: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
@@ -17,6 +16,7 @@ class Position(BaseModel):
|
||||
text: Optional[Text] = None
|
||||
|
||||
|
||||
class Position(BaseModel):
|
||||
@dataclass
|
||||
class Position:
|
||||
total: Optional[int] = None
|
||||
positions: Optional[List[Position]] = None
|
||||
|
||||
@@ -2,17 +2,19 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
from .Text import Text
|
||||
from .Locations import Locations
|
||||
|
||||
|
||||
class Device(BaseModel):
|
||||
@dataclass
|
||||
class Device:
|
||||
id: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
|
||||
|
||||
class Locator(BaseModel):
|
||||
|
||||
@dataclass
|
||||
class Locator:
|
||||
href: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
title: Optional[str] = None
|
||||
@@ -20,7 +22,8 @@ class Locator(BaseModel):
|
||||
text: Optional[Text] = None
|
||||
|
||||
|
||||
class Progress(BaseModel):
|
||||
@dataclass
|
||||
class Progress:
|
||||
modified: Optional[str] = None
|
||||
device: Optional[Device] = None
|
||||
locator: Optional[Locator] = None
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Readlist(BaseModel):
|
||||
@dataclass
|
||||
class Readlist:
|
||||
name: str
|
||||
summary: str
|
||||
ordered: bool
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .AlternateTitle import AlternateTitle
|
||||
from typing import Optional
|
||||
from dataclasses import dataclass
|
||||
from .BooksMetadata import BooksMetadata
|
||||
from .Link import Link
|
||||
from .Metadata import Metadata
|
||||
|
||||
|
||||
class Series(BaseModel):
|
||||
id: Optional[str] = None
|
||||
libraryId: Optional[str] = None
|
||||
name: Optional[str] = None
|
||||
url: Optional[str] = None
|
||||
created: Optional[str] = None
|
||||
lastModified: Optional[str] = None
|
||||
fileLastModified: Optional[str] = None
|
||||
booksCount: Optional[int] = None
|
||||
booksReadCount: Optional[int] = None
|
||||
booksUnreadCount: Optional[int] = None
|
||||
booksInProgressCount: Optional[int] = None
|
||||
metadata: Optional[Metadata] = None
|
||||
booksMetadata: Optional[BooksMetadata] = None
|
||||
deleted: Optional[bool] = None
|
||||
oneshot: Optional[bool] = None
|
||||
@dataclass
|
||||
class Series:
|
||||
id: str = None
|
||||
libraryId: str = None
|
||||
name: str = None
|
||||
url: str = None
|
||||
created: str = None
|
||||
lastModified: str = None
|
||||
fileLastModified: str = None
|
||||
booksCount: int = 0
|
||||
booksReadCount: int = None
|
||||
booksUnreadCount: int = None
|
||||
booksInProgressCount: int = None
|
||||
metadata: Metadata = None
|
||||
booksMetadata: BooksMetadata = None
|
||||
deleted: bool = None
|
||||
oneshot: bool = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.metadata is None:
|
||||
self.metadata = Metadata()
|
||||
if self.booksMetadata is None:
|
||||
self.booksMetadata = BooksMetadata()
|
||||
if self.deleted is None:
|
||||
self.deleted = False
|
||||
if self.oneshot is None:
|
||||
self.oneshot = False
|
||||
if self.metadata:
|
||||
self.metadata = Metadata(**self.metadata)
|
||||
if self.booksMetadata:
|
||||
self.booksMetadata = BooksMetadata(**self.booksMetadata)
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Sort(BaseModel):
|
||||
@dataclass
|
||||
class Sort:
|
||||
empty: bool
|
||||
sorted: bool
|
||||
unsorted: bool
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
from __future__ import annotations
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
class Text(BaseModel):
|
||||
|
||||
@dataclass
|
||||
class Text:
|
||||
after: Optional[str] = None
|
||||
before: Optional[str] = None
|
||||
before: Optional[str] = None
|
||||
highlight: Optional[str] = None
|
||||
|
||||
@@ -2,10 +2,11 @@ from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Thumbnail(BaseModel):
|
||||
@dataclass
|
||||
class Thumbnail:
|
||||
id: str
|
||||
seriesId: str
|
||||
type: str
|
||||
@@ -16,7 +17,8 @@ class Thumbnail(BaseModel):
|
||||
height: int
|
||||
|
||||
|
||||
class ReadlistThumbnail(BaseModel):
|
||||
@dataclass
|
||||
class ReadlistThumbnail:
|
||||
id: str
|
||||
readlistId: str
|
||||
type: str
|
||||
|
||||
@@ -2,25 +2,24 @@ from __future__ import annotations
|
||||
|
||||
from typing import List, Dict
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
class User(BaseModel):
|
||||
"""Object representing a User
|
||||
|
||||
Args:
|
||||
BaseModel (Model): Pydantic BaseModel
|
||||
"""
|
||||
id:str
|
||||
email:str
|
||||
roles:List[str]
|
||||
sharedAllLibraries:bool
|
||||
sharedLibrariesIds:List[str]
|
||||
labelsAllow:List[str]
|
||||
labelsExclude:List[str]
|
||||
ageRestriction:Dict[str, int|str] | None
|
||||
|
||||
|
||||
class CreateUser(BaseModel):
|
||||
email:str
|
||||
password:str
|
||||
roles:List[str]
|
||||
@dataclass
|
||||
class User:
|
||||
"""Object representing a User"""
|
||||
|
||||
id: str
|
||||
email: str
|
||||
roles: List[str]
|
||||
sharedAllLibraries: bool
|
||||
sharedLibrariesIds: List[str]
|
||||
labelsAllow: List[str]
|
||||
labelsExclude: List[str]
|
||||
ageRestriction: Dict[str, int | str] | None
|
||||
|
||||
|
||||
class CreateUser:
|
||||
email: str
|
||||
password: str
|
||||
roles: List[str]
|
||||
|
||||
@@ -25,6 +25,6 @@ from .User import User, CreateUser
|
||||
from .Authentication import Authentication, UserAuthActivity
|
||||
from .apikey import APIKey
|
||||
from .settings import Settings
|
||||
from .status import Status
|
||||
|
||||
|
||||
from .violation import Violation
|
||||
from .violation import Violation
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class APIKey(BaseModel):
|
||||
@dataclass
|
||||
class APIKey:
|
||||
id: str
|
||||
userId: str
|
||||
key: str
|
||||
comment: str
|
||||
createdDate: str
|
||||
lastModifiedDate: str
|
||||
lastModifiedDate: str
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class ServerPort(BaseModel):
|
||||
@dataclass
|
||||
class ServerPort:
|
||||
configurationSource: int
|
||||
databaseSource: int
|
||||
effectiveValue: int
|
||||
|
||||
|
||||
class ServerContextPath(BaseModel):
|
||||
@dataclass
|
||||
class ServerContextPath:
|
||||
configurationSource: str
|
||||
databaseSource: str
|
||||
effectiveValue: str
|
||||
|
||||
|
||||
class KepubifyPath(BaseModel):
|
||||
@dataclass
|
||||
class KepubifyPath:
|
||||
configurationSource: str
|
||||
databaseSource: str
|
||||
effectiveValue: str
|
||||
|
||||
|
||||
class Settings(BaseModel):
|
||||
@dataclass
|
||||
class Settings:
|
||||
deleteEmptyCollections: bool
|
||||
deleteEmptyReadLists: bool
|
||||
rememberMeDurationDays: int
|
||||
|
||||
15
src/komgapi/schemas/status.py
Normal file
15
src/komgapi/schemas/status.py
Normal file
@@ -0,0 +1,15 @@
|
||||
# enum
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Status(str, Enum):
|
||||
ENDED = "ENDED"
|
||||
ONGOING = "ONGOING"
|
||||
HIATUS = "HIATUS"
|
||||
ABANDONED = "ABANDONED"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
options = [e.name for e in Status]
|
||||
print(options)
|
||||
print(Status("ENDED"))
|
||||
@@ -1,11 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
from pydantic import BaseModel
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Violation(BaseModel):
|
||||
@dataclass
|
||||
class Violation:
|
||||
fieldName: str
|
||||
message: str
|
||||
|
||||
|
||||
|
||||
19
tests/test_api.py
Normal file
19
tests/test_api.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from komgapi import komgapi
|
||||
from komconfig import KomConfig
|
||||
|
||||
config = KomConfig()
|
||||
|
||||
client = komgapi(
|
||||
url=config.komga.url,
|
||||
username=config.komga.user,
|
||||
password=config.komga.password,
|
||||
)
|
||||
|
||||
|
||||
def test_series_controller(client: komgapi):
|
||||
assert client.series_controller.getAllSeries() is not None
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_series_controller(client)
|
||||
print("All tests passed!")
|
||||
Reference in New Issue
Block a user