Metadata-Version: 2.4
Name: mycloud-sdk
Version: 0.1.0
Summary: Official Python SDK for myCloud - your personal cloud storage
Author-email: myCloud <support@mysphere.co.in>
Project-URL: Homepage, https://mysphere.co.in
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.27.0

# myCloud Python SDK

![PyPI version](https://img.shields.io/pypi/v/mycloud-sdk.svg)
![Python Versions](https://img.shields.io/pypi/pyversions/mycloud-sdk.svg)
![License](https://img.shields.io/github/license/mysphere/mycloud-sdk)

The official Python SDK for **myCloud** by mySphere. This SDK provides a robust, fully-featured, and type-safe interface to interact with your myCloud storage, including both synchronous and highly concurrent asynchronous APIs.

## Features

- **Dual API:** Both blocking (`MyCloud`) and non-blocking (`AsyncMyCloud`) clients available out of the box.
- **Zero-Configuration:** Automatically detects and reads credentials generated by the myCloud CLI (`~/.mycloud/credentials.json`).
- **Comprehensive:** Full support for file lifecycles, folder management, sharing, batch operations, stash, search, and private storage nodes.
- **Type-Safe:** Written with complete type hints for seamless IDE integration and autocomplete.
- **Graceful Error Handling:** Automatically catches and parses server validation errors, mapping them to typed Python exceptions.

---

## Installation

Install via pip:

```bash
pip install mycloud-sdk
```

---

## Quick Start

If you are already logged in via the [myCloud CLI](https://github.com/mysphere/mycloud), the SDK will automatically read your existing credentials. You can jump straight into the action!

### Synchronous Usage

```python
from pathlib import Path
from mycloud import MyCloud

# Initialize the client (automatically uses ~/.mycloud/credentials.json if available)
cloud = MyCloud()

# 1. Login manually (if not using the CLI's auto-config)
cloud.auth.login("your_username", "your_password")

# 2. List your root files and folders
files = cloud.files.list()
for f in files:
    print(f"Found file: {f.name} ({f.size} bytes)")

# 3. Upload a new file
my_file = Path("important_document.pdf")
uploaded_file = cloud.files.upload(my_file)
print(f"Uploaded successfully! ID: {uploaded_file.id}")

# 4. Download a file
cloud.files.download(uploaded_file.id, dest="downloads/important_document.pdf")
```

### Asynchronous Usage (For high concurrency)

For applications requiring high throughput, discord bots, or web servers, use `AsyncMyCloud` to prevent blocking the event loop.

```python
import asyncio
from pathlib import Path
from mycloud import AsyncMyCloud

async def main():
    async with AsyncMyCloud() as cloud:
        # Assuming you're already logged in via CLI config
        
        # Concurrently upload multiple files
        paths = [Path(f"data_{i}.csv") for i in range(10)]
        
        # asyncio.gather fires all uploads in parallel
        upload_tasks = [cloud.files.upload(p) for p in paths]
        uploaded_files = await asyncio.gather(*upload_tasks)
        
        for f in uploaded_files:
            print(f"Uploaded: {f.name}")

asyncio.run(main())
```

---

## Core Modules Overview

The `MyCloud` client exposes multiple modules to handle different subsets of the API.

### `cloud.files`
- `upload(local_path: Path, folder_id: Optional[int], node: str)`: Upload a file.
- `download(file_id: int, dest: Union[str, Path])`: Download a file.
- `list(folder_id: Optional[int])`: List files in a directory.
- `rename(file_id: int, new_name: str)`: Rename a file.
- `move(file_id: int, dest_folder_id: Optional[int])`: Move a file to a new folder.
- `delete(file_id: int)`: Move a file to the trash.

### `cloud.folders`
- `create(name: str, parent_id: Optional[int])`: Create a new directory.
- `list(parent_id: Optional[int])`: List sub-directories.
- `rename(folder_id: int, new_name: str)`: Rename a directory.
- `move(folder_id: int, dest_folder_id: Optional[int])`: Move a folder.
- `delete(folder_id: int)`: Delete a folder.

### `cloud.share`
- `generate_link(item_id: int, item_type: str = "file") -> ShareInfo`: Create a public shareable URL.
- `info(item_id: int, item_type: str = "file") -> ShareInfo`: Get sharing metadata.
- `disable_link(share_id: int)`: Revoke access to a public link.

### `cloud.stash`
- `add(file_id: int)`: Secure a file in your stash.
- `remove(file_id: int)`: Remove a file from the stash.
- `list()`: List all stashed items.

### `cloud.servers`
- `list()`: List all registered private node servers.
- `add()`: Generate a new API key for a private node.
- `delete(node_id: int)`: Remove a private node from your account.

---

## Exception Handling

The SDK provides specific exceptions located in `mycloud.exceptions` so you can gracefully handle edge cases.

```python
from mycloud import MyCloud
from mycloud.exceptions import AuthError, NotFoundError, ValidationError

cloud = MyCloud()

try:
    cloud.files.download(9999999, dest=".")
except NotFoundError:
    print("The requested file does not exist!")
except AuthError:
    print("Your session has expired. Please login again.")
except ValidationError as e:
    print(f"Invalid input: {e}")
```

## Known Server API Limitations

The SDK is designed to be highly resilient. However, please note that certain backend functionalities currently have documented limitations:
- **Share Disabling**: The server API requires a `share_id` to revoke public links.
- **Private Node Key Generation**: The node generation endpoint currently returns an HTML page instead of a JSON payload on success. The SDK gracefully catches this limitation as a standard Python Exception.
- **Upload Filename Synchronization**: When deleting and recreating identical filenames quickly, the auto-increment tracker on the server may desync from `MAX(id)`.

*All known limitations are safely caught by the SDK to prevent hard application crashes.*

---

## License

This project is licensed under the MIT License.
