pix
Batch AVIF shrink and image-host sync CLI.
pix incrementally compresses local image directories to AVIF, mirrors originals to cloud storage, and publishes compressed images to an image host. Compression uses pyvips; sync is delegated to rclone.
中文 · Architecture · Development · Decisions · Roadmap
Status: core commands are implemented and tested. Real R2 and Windows smoke tests are still pending; see the roadmap.
What pix does
| Command | Purpose |
|---|---|
pix shrink |
Recursively compress images to AVIF, re-encoding only changed files |
pix backup |
Bidirectionally mirror an original photo directory to a cloud drive |
pix publish |
Bidirectionally mirror AVIF output to an image host; optionally emit public URLs |
pix doctor |
Check Python, libvips, rclone and configuration |
pix config init |
Write a commented default configuration file |
pix is deliberately narrow:
- It is not an rclone replacement and does not manage credentials.
- It does not read repository config or care about version control.
- It has no one-shot
pix run; you chain the commands yourself.
How it works
photos/ ── shrink (pyvips) ──> out/*.avif + out/.pix/state.json
│
└── publish (rclone bisync) ──> r2:images
photos/ ── backup (rclone bisync) ────────────────────────────────> onedrive:photos
| Command | What actually runs | Who does the work |
|---|---|---|
shrink |
pyvips thumbnail + AVIF encode | pix |
backup |
rclone bisync SRC REMOTE |
pix composes arguments, rclone executes |
publish |
rclone bisync OUT REMOTE, excluding .pix/** |
pix + rclone |
doctor / config init |
Dependency checks / config scaffold | pix |
Sync is rclone. backup and publish only compose rclone bisync arguments; rclone handles authentication, transfers and conflict handling. Configure remotes with rclone config before using them. pix never reads or writes rclone.conf.
backup / publish are bidirectional mirrors, not one-way backups. Deletions propagate in both directions.
Requirements
- Python 3.11+
- uv
- rclone >= 1.66 for
backup/publish - At least one remote already configured in rclone
pyvips[binary] bundles libvips, so no separate vips installation is required.
Install
Not yet published on PyPI; install from source:
git clone https://github.com/uzvg/pix.git
cd pix
uv tool install --editable .
pix --version
Linux installs ~/.local/bin/pix; Windows installs %USERPROFILE%\.local\bin\pix.exe. An editable install picks up source changes immediately. Reinstall with uv tool install --force --editable ., or remove it with uv tool uninstall pix-cli.
Configure rclone
Only needed for backup / publish:
rclone config # create a remote, for example onedrive or r2
rclone lsd onedrive: # verify that it works
rclone stores credentials in ~/.config/rclone/rclone.conf (Windows: %APPDATA%\rclone\rclone.conf). pix passes the remote name through and does not store or validate credentials.
Configure pix (optional)
Default configuration paths:
- Linux/macOS:
$XDG_CONFIG_HOME/pix/config.toml, or~/.config/pix/config.toml - Windows:
%APPDATA%\pix\config.toml
If the file is missing, built-in defaults are used. Generate a commented file with:
pix config init
Existing files are not overwritten; pass --force to replace one. Use --config PATH before the subcommand to point at another file.
Common keys:
| Section | Key | Default | Meaning |
|---|---|---|---|
[shrink] |
width |
1920 |
Longest edge in pixels |
quality |
85 |
AVIF quality, 1–100 | |
effort |
5 |
libvips effort, 0–9 | |
min_size |
0 |
Skip sources smaller than this; accepts 500K, 1M, 2G |
|
include |
jpg, jpeg, png, webp |
Extensions to scan, without dots | |
[backup] |
exclude |
[] |
rclone exclude globs; command-line values are appended |
max_delete |
10 |
Delete limit as a percentage; -1 disables, 0 forbids |
|
[publish] |
base_url |
"" |
Public URL prefix; must start with http:// or https:// |
urls |
false |
Emit a JSON URL manifest | |
exclude |
[] |
Same as backup | |
max_delete |
10 |
Same as backup | |
[rclone] |
binary |
rclone |
rclone executable name or path |
Precedence is command line > pix config > built-in defaults. --exclude is appended to config excludes rather than replacing them. -q means quality; silence is --quiet (no short form).
Quick start
-
Check the environment:
pix doctor -
Compress images:
pix shrink -s ~/Pictures -d ~/Pictures-avif --dry-run pix shrink -s ~/Pictures -d ~/Pictures-avif -w 1920 -q 85
The first run encodes everything; later runs skip unchanged files. State lives in
OUT/.pix/state.json. Changingwidth,qualityorefforttriggers a full re-encode.--dry-runonly prints the plan. -
Back up originals:
pix backup -s ~/Pictures -d onedrive:photos --dry-run pix backup -s ~/Pictures -d onedrive:photos --resync
--resyncis required on the first run. Always preview with--dry-runfirst. -
Publish the AVIF output:
pix publish -s ~/Pictures-avif -d r2:images \ --base-url https://img.example.com --urls > urls.json
publishrequires at least one.avifin the source and always excludes.pix/**. The URL manifest is printed to stdout as JSON only after a successful, non-dry-run sync. -
Re-run later:
pix shrink -s ~/Pictures -d ~/Pictures-avif pix backup -s ~/Pictures -d onedrive:photos pix publish -s ~/Pictures-avif -d r2:images --urls
Only changes are encoded and transferred.
Command cheat sheet
| Task | Command |
|---|---|
| Preview compression | pix shrink -s SRC -d OUT --dry-run |
| Re-encode everything | pix shrink -s SRC -d OUT --force |
| Remove outputs whose source is gone | pix shrink -s SRC -d OUT --prune |
| Preview a sync | pix backup -s SRC -d REMOTE --dry-run |
| First run, or after changing excludes | pix backup -s SRC -d REMOTE --resync |
| Emit a URL manifest | pix publish -s OUT -d REMOTE --base-url https://img.example.com --urls |
| Diagnose the environment | pix doctor |
| Generate a config file | pix config init |
Safety rules
| Situation | What happens | Protection |
|---|---|---|
| Deletions on either side | Propagated in both directions; accidental deletion is irreversible | Preview with --dry-run; default --max-delete 10 (percentage) |
| First sync | rclone has no baseline and errors | Add --resync explicitly |
| Excludes changed | The old baseline may treat excluded files as deletions | Run with --resync again after changing excludes |
| Empty source | backup / publish refuse; shrink --prune also refuses |
Use --allow-empty (plus --resync for sync) only when intentional |
a.jpg and a.jpeg both exist |
Both map to the same output | Detected before encoding; exits 2 |
| Two concurrent shrinks | They would write the same state and outputs | OUT/.pix/lock blocks the second process |
| State lost or corrupt | Warning, then a full rebuild | Only causes re-encoding; outputs stay valid |
Output and exit codes
- Logs and progress go to stderr.
- stdout carries
doctorreports,--dry-runplans and--urlsJSON. - Exit codes:
0success,1runtime failure,2argument or configuration error. backup/publishreturn rclone's exit code unchanged.
URL manifest example:
{
"2024/a.avif": "https://img.example.com/2024/a.avif"
}
Troubleshooting
| Symptom | Fix |
|---|---|
pyvips/libvips unavailable |
Reinstall to bring libvips: uv tool install --force --editable /path/to/pix; or install libvips manually (Linux: apt install libvips42, Windows: libvips release zip on PATH) |
| rclone is older than 1.66 | Upgrade rclone |
| bisync reports a missing baseline | Add --resync on the first run |
OUT/.pix/lock already exists |
Check for another shrink, then remove a stale lock |
| Configuration fails to parse | Run pix doctor to locate the file; fix the TOML or remove the file to fall back to defaults |
publish says the source has no .avif |
Run pix shrink first |
| URL-related error | base_url must start with http:// or https://, and --urls requires it |
Development
uv sync
uv run pytest
uv run pix --version
uv build
The code must stay compatible with Python 3.11. See development.md for tests, packaging and release details. Behavior is specified in architecture.md; rationale lives in decisions.md.
The detailed docs are currently written in Chinese.
Metadata
Release files for pix-cli 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pix_cli-0.1.1.tar.gz | 52.5 kB | With provenance | Details for pix_cli-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pix_cli-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for pix_cli-0.1.1-py3-none-any.whl |
Total release size: 70.5 kB
Release files / pix_cli-0.1.1.tar.gz
| Download URL | pix_cli-0.1.1.tar.gz |
|---|---|
| Size | 52.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
412e689fdaf47baa4be21b271ac27431e5d8700f410c473bd3fb44ea52b79b0a
|
|
BLAKE2b-256 checksum How to use checksums |
6904c3507b549c04aedd3e2b89d6139e55fd42fa087c9e12e188739d3dc81d06
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logRelease files / pix_cli-0.1.1-py3-none-any.whl
| Download URL | pix_cli-0.1.1-py3-none-any.whl |
|---|---|
| Size | 18.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
154eb3ceea1bd2dbd75e8455b181300c24da686d7aa1fdb0e215096dda52ba5a
|
|
BLAKE2b-256 checksum How to use checksums |
cda031124ca7368c52d9ef48e853ddb12b653a5fd0c0d37066acb246d193792d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logSecurity for pix-cli 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for pix_cli-0.1.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.