|
7 | 7 | """ |
8 | 8 |
|
9 | 9 | from http import HTTPStatus |
| 10 | +import json |
10 | 11 | from json import JSONDecodeError |
11 | 12 | import logging |
12 | 13 | import traceback |
|
15 | 16 | import aiohttp |
16 | 17 |
|
17 | 18 | from .exceptions import CannotConnect, InvalidAuth, ParserError |
18 | | -from .types import LoginResponse, PyLoadCommand, StatusServerResponse |
| 19 | +from .types import Destination, LoginResponse, PyLoadCommand, StatusServerResponse |
19 | 20 |
|
20 | 21 | _LOGGER = logging.getLogger(__name__) |
21 | 22 |
|
@@ -167,6 +168,91 @@ async def get( |
167 | 168 | "Executing command {command} failed due to request exception" |
168 | 169 | ) from e |
169 | 170 |
|
| 171 | + async def post( |
| 172 | + self, |
| 173 | + command: PyLoadCommand, |
| 174 | + data: dict[str, Any], |
| 175 | + ) -> Any: |
| 176 | + """Execute a pyLoad API command using a POST request. |
| 177 | +
|
| 178 | + Parameters |
| 179 | + ---------- |
| 180 | + command : PyLoadCommand |
| 181 | + The pyLoad command to execute. |
| 182 | + data : dict[str, Any] |
| 183 | + Data to include in the request body. The values in the dictionary |
| 184 | + will be JSON encoded. |
| 185 | +
|
| 186 | + Returns |
| 187 | + ------- |
| 188 | + Any |
| 189 | + The response data from the API. |
| 190 | +
|
| 191 | + Raises |
| 192 | + ------ |
| 193 | + CannotConnect |
| 194 | + If the request to the API fails due to a connection issue. |
| 195 | + InvalidAuth |
| 196 | + If the request fails due to invalid or expired authentication. |
| 197 | + ParserError |
| 198 | + If there's an error parsing the API response. |
| 199 | +
|
| 200 | + Notes |
| 201 | + ----- |
| 202 | + This method sends an asynchronous POST request to the pyLoad API endpoint |
| 203 | + specified by `command`, with the provided `data` dictionary. It handles |
| 204 | + authentication errors, HTTP status checks, and parses the JSON response. |
| 205 | +
|
| 206 | +
|
| 207 | + Example |
| 208 | + ------- |
| 209 | + To add a new package to pyLoad, use: |
| 210 | + ```python |
| 211 | + status = await pyload_api.post(PyLoadCommand.ADD_PACKAGE, data={...} |
| 212 | + ``` |
| 213 | +
|
| 214 | + """ |
| 215 | + url = f"{self.api_url}api/{command}" |
| 216 | + data = { |
| 217 | + k: str(v) if isinstance(v, bytes) else json.dumps(v) |
| 218 | + for k, v in data.items() |
| 219 | + } |
| 220 | + |
| 221 | + try: |
| 222 | + async with self._session.post(url, data=data) as r: |
| 223 | + _LOGGER.debug( |
| 224 | + "Response from %s [%s]: %s", r.url, r.status, await r.text() |
| 225 | + ) |
| 226 | + |
| 227 | + if r.status == HTTPStatus.UNAUTHORIZED: |
| 228 | + raise InvalidAuth( |
| 229 | + "Request failed due invalid or expired authentication cookie." |
| 230 | + ) |
| 231 | + r.raise_for_status() |
| 232 | + try: |
| 233 | + data = await r.json() |
| 234 | + except JSONDecodeError as e: |
| 235 | + _LOGGER.debug( |
| 236 | + "Exception: Cannot parse response for %s:\n %s", |
| 237 | + command, |
| 238 | + traceback.format_exc(), |
| 239 | + ) |
| 240 | + raise ParserError( |
| 241 | + f"Get {command} failed during parsing of request response." |
| 242 | + ) from e |
| 243 | + |
| 244 | + return data |
| 245 | + |
| 246 | + except (TimeoutError, aiohttp.ClientError) as e: |
| 247 | + _LOGGER.debug( |
| 248 | + "Exception: Cannot execute command %s:\n %s", |
| 249 | + command, |
| 250 | + traceback.format_exc(), |
| 251 | + ) |
| 252 | + raise CannotConnect( |
| 253 | + f"Executing command {command} failed due to request exception" |
| 254 | + ) from e |
| 255 | + |
170 | 256 | async def get_status(self) -> StatusServerResponse: |
171 | 257 | """Get general status information of pyLoad. |
172 | 258 |
|
@@ -529,3 +615,107 @@ async def free_space(self) -> int: |
529 | 615 | return int(r) |
530 | 616 | except CannotConnect as e: |
531 | 617 | raise CannotConnect("Get free space failed due to request exception") from e |
| 618 | + |
| 619 | + async def add_package( |
| 620 | + self, |
| 621 | + name: str, |
| 622 | + links: list[str], |
| 623 | + destination: Destination = Destination.COLLECTOR, |
| 624 | + ) -> int: |
| 625 | + """Add a new package to pyLoad from a list of links. |
| 626 | +
|
| 627 | + Parameters |
| 628 | + ---------- |
| 629 | + name : str |
| 630 | + The name of the package to be added. |
| 631 | + links : list[str] |
| 632 | + A list of download links to be included in the package. |
| 633 | + destination : Destination, optional |
| 634 | + The destination where the package should be stored, by default Destination.COLLECTOR. |
| 635 | +
|
| 636 | + Returns |
| 637 | + ------- |
| 638 | + int |
| 639 | + The ID of the newly created package. |
| 640 | +
|
| 641 | + Raises |
| 642 | + ------ |
| 643 | + CannotConnect |
| 644 | + If the request to add the package fails due to a connection issue. |
| 645 | + InvalidAuth |
| 646 | + If the request fails due to invalid or expired authentication. |
| 647 | + ParserError |
| 648 | + If there's an issue parsing the response from the server. |
| 649 | +
|
| 650 | + Example |
| 651 | + ------- |
| 652 | + To add a new package with a couple of links to the pyLoad collector: |
| 653 | + ```python |
| 654 | + package_id = await pyload_api.add_package( |
| 655 | + "test_package", |
| 656 | + [ |
| 657 | + "https://example.com/file1.zip", |
| 658 | + "https://example.com/file2.iso", |
| 659 | + ] |
| 660 | + ) |
| 661 | + ``` |
| 662 | + """ |
| 663 | + |
| 664 | + try: |
| 665 | + r = await self.post( |
| 666 | + PyLoadCommand.ADD_PACKAGE, |
| 667 | + data={ |
| 668 | + "name": name, |
| 669 | + "links": links, |
| 670 | + "dest": destination, |
| 671 | + }, |
| 672 | + ) |
| 673 | + return int(r) |
| 674 | + except CannotConnect as e: |
| 675 | + raise CannotConnect("Adding package failed due to request exception") from e |
| 676 | + |
| 677 | + async def upload_container( |
| 678 | + self, |
| 679 | + filename: str, |
| 680 | + binary_data: bytes, |
| 681 | + ) -> None: |
| 682 | + """Upload a container file to pyLoad. |
| 683 | +
|
| 684 | + Parameters |
| 685 | + ---------- |
| 686 | + filename : str |
| 687 | + The name of the file to be uploaded. |
| 688 | + binary_data : bytes |
| 689 | + The binary content of the file. |
| 690 | +
|
| 691 | + Returns |
| 692 | + ------- |
| 693 | + None |
| 694 | +
|
| 695 | + Raises |
| 696 | + ------ |
| 697 | + CannotConnect |
| 698 | + If the request to upload the container fails due to a connection issue. |
| 699 | + InvalidAuth |
| 700 | + If the request fails due to invalid or expired authentication. |
| 701 | +
|
| 702 | + Example |
| 703 | + ------- |
| 704 | + To upload a container file to pyLoad: |
| 705 | + ```python |
| 706 | + await pyload_api.upload_container( |
| 707 | + "example_container.dlc", |
| 708 | + b"binary data of the file" |
| 709 | + ) |
| 710 | + ``` |
| 711 | + """ |
| 712 | + try: |
| 713 | + await self.post( |
| 714 | + PyLoadCommand.UPLOAD_CONTAINER, |
| 715 | + data={"filename": filename, "data": binary_data}, |
| 716 | + ) |
| 717 | + |
| 718 | + except CannotConnect as e: |
| 719 | + raise CannotConnect( |
| 720 | + "Uploading container to pyLoad failed due to request exception" |
| 721 | + ) from e |
0 commit comments