Metadata-Version: 2.5
Name: pyVHL
Version: 0.2.0
Summary: A Python library for uploading short clips to video and file hosting services.
Author-email: problemxl <email@github.com>
Requires-Python: >=3.10
Requires-Dist: beautifulsoup4
Requires-Dist: loguru
Requires-Dist: pydantic
Requires-Dist: requests
Requires-Dist: requests-toolbelt
Requires-Dist: retry
Requires-Dist: youtube-dl
Description-Content-Type: text/markdown

# pyVHL

[![CI](https://github.com/problemxl/pyVHL/actions/workflows/ci.yml/badge.svg)](https://github.com/problemxl/pyVHL/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/pyVHL)](https://pypi.org/project/pyVHL/)

A Python library for uploading short clips to video and file hosting services.

pyVHL can also download clips from a growing list of sources (via
[youtube-dl](https://github.com/ytdl-org/youtube-dl)) and upload them to a mirror
host of your choice — or pick one at random for you.

## Supported Hosts

| Host         | Identifier   | Result type     |
| ------------ | ------------ | --------------- |
| Streamable   | `streamable` | `StreamableVideo` |
| Catbox       | `catbox`     | `CatboxVideo`   |
| qu.ax        | `qu.ax`      | `QuaxVideo`     |
| pone.rs      | `pone.rs`    | `PoneVideo`     |
| kappa.lol    | `kappa.lol`  | `KappaVideo`    |
| segs.lol     | `segs.lol`   | `SegsVideo`     |

## Supported Download Sources

pyVHL delegates extraction to youtube-dl, with dedicated handling for:

- Twitch clips
- YouTube
- Facebook
- Generic video pages (any site supported by youtube-dl's generic extractor)

## Installation

pyVHL requires Python 3.10+.

```bash
pip install pyVHL
```

Or install from source with [uv](https://docs.astral.sh/uv/):

```bash
git clone https://github.com/problemxl/pyVHL.git
cd pyVHL
uv sync
```

## Quickstart

Download a clip and mirror it to a random host:

```python
from pyVHL.pyvhl import pyvhl

client = pyvhl()

# Download a clip and collect its metadata
video = client.get_video("https://clips.twitch.tv/FantasticTenaciousLlamaVoteYea-i8q-ElNjPqIFx7eA")

# Upload it to a randomly selected host
mirror = client.upload_video(video["title"], video["filename"])

print(f"Mirrored to {mirror.host}: {mirror.url}")
```

Skip the download and only inspect the metadata:

```python
video = client.get_video(video_url, download=False)
```

Upload to a specific host and delete the local file afterwards:

```python
mirror = client.upload_video(video["title"], video["filename"], host="streamable", delete_file=True)
```

## API Reference

### `pyvhl()`

The main client. Initialize it once and reuse it.

### `get_video(video_url: str, download: bool = True) -> dict`

Extracts (and by default downloads) a clip via youtube-dl and returns its
metadata as a dict:

| Key         | Description                                |
| ----------- | ------------------------------------------ |
| `filename`  | Path to the downloaded file (when `download=True`) |
| `title`     | Clip title                                 |
| `url`       | Source URL of the clip                     |
| `id`        | Clip ID                                    |
| `streamer`  | Channel / uploader name                    |
| `date`      | Upload date                                |
| `extractor` | youtube-dl extractor used                  |
| `file_size` | Size of the downloaded file in bytes       |

If the URL cannot be processed, the values are `None`.

### `upload_video(clip_title: str, filename: str, host: str = "", delete_file: bool = False) -> VideoClient`

Uploads a local file to a mirror host.

- `clip_title` — title passed to the host (used by Streamable).
- `filename` — path to the local video file.
- `host` — one of the identifiers in [Supported Hosts](#supported-hosts).
  An empty string picks a random host.
- `delete_file` — remove the local file after a successful upload.

Returns a `VideoClient` subclass (`StreamableVideo`, `CatboxVideo`,
`QuaxVideo`, `PoneVideo`, `KappaVideo`, or `SegsVideo`) with:

- `host` — the host identifier,
- `url` — the public mirror URL,
- `shortcode` — the host-specific short ID.

Raises `ValueError` for an unsupported host and `FileNotFoundError` if the
file does not exist. Failed uploads are retried automatically.

## Development

The project uses [uv](https://docs.astral.sh/uv/) for dependency management,
[pytest](https://docs.pytest.org/) for tests, and [ruff](https://docs.astral.sh/ruff/)
for linting.

```bash
uv sync          # install dependencies
uv run pytest    # run the test suite
uv run ruff check pyvhl tests
```

Contributions follow [GitFlow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow):
branch off `develop` (`feature/...`, `fix/...`, `doc/...`) and open a pull
request back into `develop`. Releases are cut through `release/*` branches
into `main`.

## License

This project does not currently include a license file. Please contact the
maintainer if you intend to redistribute or reuse the code.
