- Introduced bug report and feature request templates for better issue tracking. - Added release workflow for automated versioning and package publishing. - Updated dependencies in `pyproject.toml` for improved functionality. - Refactored API endpoints to use `httpx` for better performance and error handling. - Added new methods in `BookController` and `KOMGAPI_REST` for enhanced book and series management. - Updated schemas to include optional fields for better data handling.
180 lines
5.8 KiB
Python
180 lines
5.8 KiB
Python
import httpx
|
|
from httpx_retries import Retry, RetryTransport
|
|
from komgapi.errors import KomgaError, ResultErrror
|
|
from typing import Any, Union
|
|
from limit import limit # type:ignore
|
|
|
|
import loguru
|
|
import sys
|
|
import json
|
|
|
|
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: str,
|
|
password: str,
|
|
url: str,
|
|
timeout: int = 20,
|
|
api_version: int = 1,
|
|
) -> None:
|
|
self._username = username
|
|
self._password = password
|
|
self.url = url + f"api/v{api_version}/"
|
|
self.timeout = timeout
|
|
self.headers = {
|
|
"Content-Type": "application/json",
|
|
"Accept": "application/json",
|
|
}
|
|
|
|
def setParams(self, locals: dict[Any, Any]) -> dict[Any, Any]:
|
|
return {
|
|
param_name: param
|
|
for param_name, param in locals.items()
|
|
if param is not None
|
|
and param_name not in ["self", "series_idurl", "query", "url"]
|
|
}
|
|
|
|
def test_connection(self):
|
|
try:
|
|
with httpx.Client(timeout=self.timeout) as client:
|
|
client.get(self.url, headers=self.headers)
|
|
return True
|
|
except httpx.RequestError:
|
|
return False
|
|
|
|
def overwriteVersion(self, version: int):
|
|
self.url = self.url.replace("api/v1/", f"api/v{version}/")
|
|
return self
|
|
|
|
@limit(1, 1)
|
|
def getRequest(self, url: str, params: Union[dict[Any, Any], None] = None) -> Any:
|
|
if params is None:
|
|
params = {}
|
|
try:
|
|
with httpx.Client(
|
|
timeout=self.timeout,
|
|
auth=(self._username, self._password),
|
|
transport=RetryTransport(retry=Retry(total=5, backoff_factor=0.5)),
|
|
) as client:
|
|
response = client.get(url, params=params, headers=self.headers)
|
|
if response.status_code != 200:
|
|
return self.getRequest(url, params)
|
|
|
|
return response.json()
|
|
except httpx.ConnectError as e:
|
|
raise KomgaError(f"Connection Error: {e}") from e
|
|
except httpx.TimeoutException as e:
|
|
raise KomgaError(f"Timeout Error: {e}") from e
|
|
|
|
def postRequest(
|
|
self,
|
|
url: str,
|
|
data: Union[dict[Any, Any], None] = None,
|
|
body: Union[dict[Any, Any], None] = None,
|
|
):
|
|
if data is None:
|
|
data = {}
|
|
try:
|
|
with httpx.Client(
|
|
timeout=self.timeout, auth=(self._username, self._password), transport=RetryTransport(retry=Retry(total=5, backoff_factor=0.5))
|
|
) as client:
|
|
response = client.post(
|
|
url,
|
|
params=data,
|
|
json=body if body is not None else {},
|
|
headers=self.headers,
|
|
)
|
|
log.debug(
|
|
"POST request to {} with data: {}, json: {}",
|
|
url,
|
|
json.dumps(data),
|
|
json.dumps(body),
|
|
)
|
|
response.raise_for_status()
|
|
status_code = response.status_code
|
|
if status_code == 202:
|
|
return None
|
|
elif status_code == 200:
|
|
return response.json()
|
|
else:
|
|
raise ResultErrror(f"Result Error: {response.content}")
|
|
except httpx.ConnectError as e:
|
|
raise KomgaError(f"Connection Error: {e}") from e
|
|
except httpx.TimeoutException as e:
|
|
raise KomgaError(f"Timeout Error: {e}") from e
|
|
|
|
def patchRequest(self, url: str, data: Union[dict[Any, Any], None] = None):
|
|
"""Send PATCH request to API endpoint.
|
|
|
|
Args:
|
|
url (str): API endpoint URL
|
|
data (Union[dict[Any, Any], None]): Data to send in request body
|
|
|
|
Returns:
|
|
dict: JSON response from server
|
|
|
|
Raises:
|
|
KomgaError: For connection/timeout errors
|
|
ResultError: For invalid responses
|
|
"""
|
|
if data is None:
|
|
data = {}
|
|
|
|
try:
|
|
log.debug("PATCH request to {} with data: {}", url, json.dumps(data))
|
|
|
|
with httpx.Client(
|
|
timeout=self.timeout,
|
|
auth=(self._username, self._password),
|
|
headers=self.headers,
|
|
) as client:
|
|
response = client.patch(url, json=data)
|
|
|
|
response.raise_for_status()
|
|
|
|
if response.status_code == 204:
|
|
return None
|
|
|
|
return response.json() if response.content else None
|
|
|
|
except httpx.ConnectError as e:
|
|
log.error("Connection error during PATCH to {}: {}", url, str(e))
|
|
raise KomgaError(f"Connection Error: {e}") from e
|
|
|
|
except httpx.TimeoutException as e:
|
|
log.error("Timeout during PATCH to {}: {}", url, str(e))
|
|
raise KomgaError(f"Timeout Error: {e}") from e
|
|
|
|
except httpx.HTTPStatusError as e:
|
|
log.error("HTTP error during PATCH to {}: {}", url, e.response.text)
|
|
raise ResultErrror(f"Result Error: {e.response.text}") from e
|
|
|
|
def deleteRequest(self, url: str):
|
|
try:
|
|
with httpx.Client(
|
|
timeout=self.timeout, auth=(self._username, self._password)
|
|
) as client:
|
|
response = client.delete(url, headers=self.headers)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
except httpx.ConnectError as e:
|
|
raise KomgaError(f"Connection Error: {e}") from e
|
|
except httpx.TimeoutException as e:
|
|
raise KomgaError(f"Timeout Error: {e}") from e
|
|
|
|
@classmethod
|
|
def from_env(cls):
|
|
import os
|
|
|
|
return cls(
|
|
os.environ["KOMGA_USERNAME"],
|
|
os.environ["KOMGA_PASSWORD"],
|
|
os.environ["KOMGA_URL"],
|
|
)
|