pixopt 1.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pixopt-1.0.5/.github/workflows/bump-version.yml +143 -0
- pixopt-1.0.5/.github/workflows/ci.yml +58 -0
- pixopt-1.0.5/.github/workflows/docs.yml +51 -0
- pixopt-1.0.5/.gitignore +48 -0
- pixopt-1.0.5/.readthedocs.yaml +16 -0
- pixopt-1.0.5/CHANGELOG.md +23 -0
- pixopt-1.0.5/CONTRIBUTING.md +38 -0
- pixopt-1.0.5/LICENSE +21 -0
- pixopt-1.0.5/MANIFEST.in +7 -0
- pixopt-1.0.5/PKG-INFO +304 -0
- pixopt-1.0.5/README.md +265 -0
- pixopt-1.0.5/docs/api.md +45 -0
- pixopt-1.0.5/docs/changelog.md +19 -0
- pixopt-1.0.5/docs/cli.md +144 -0
- pixopt-1.0.5/docs/contributing.md +27 -0
- pixopt-1.0.5/docs/index.md +59 -0
- pixopt-1.0.5/docs/installation.md +35 -0
- pixopt-1.0.5/docs/library.md +156 -0
- pixopt-1.0.5/mkdocs.yml +75 -0
- pixopt-1.0.5/pyproject.toml +94 -0
- pixopt-1.0.5/requirements-dev.txt +5 -0
- pixopt-1.0.5/requirements.txt +5 -0
- pixopt-1.0.5/setup.py +61 -0
- pixopt-1.0.5/src/pixopt/__init__.py +19 -0
- pixopt-1.0.5/src/pixopt/adaptive_quality.py +103 -0
- pixopt-1.0.5/src/pixopt/cli/__init__.py +17 -0
- pixopt-1.0.5/src/pixopt/cli/app.py +14 -0
- pixopt-1.0.5/src/pixopt/cli/commands/__init__.py +14 -0
- pixopt-1.0.5/src/pixopt/cli/commands/batch.py +90 -0
- pixopt-1.0.5/src/pixopt/cli/commands/compare.py +94 -0
- pixopt-1.0.5/src/pixopt/cli/commands/convert.py +110 -0
- pixopt-1.0.5/src/pixopt/cli/commands/favicon.py +52 -0
- pixopt-1.0.5/src/pixopt/cli/commands/info.py +44 -0
- pixopt-1.0.5/src/pixopt/cli/commands/optimize.py +159 -0
- pixopt-1.0.5/src/pixopt/cli/commands/placeholder.py +56 -0
- pixopt-1.0.5/src/pixopt/cli/commands/srcset.py +132 -0
- pixopt-1.0.5/src/pixopt/cli/options.py +123 -0
- pixopt-1.0.5/src/pixopt/cli/output.py +73 -0
- pixopt-1.0.5/src/pixopt/constants.py +45 -0
- pixopt-1.0.5/src/pixopt/format_resolver.py +35 -0
- pixopt-1.0.5/src/pixopt/html_comparison.py +200 -0
- pixopt-1.0.5/src/pixopt/image_ops.py +133 -0
- pixopt-1.0.5/src/pixopt/models.py +57 -0
- pixopt-1.0.5/src/pixopt/optimizer.py +474 -0
- pixopt-1.0.5/src/pixopt/placeholder.py +132 -0
- pixopt-1.0.5/src/pixopt/smart_format.py +101 -0
- pixopt-1.0.5/src/pixopt/srcset_generator.py +102 -0
- pixopt-1.0.5/src/pixopt/svg_optimizer.py +81 -0
- pixopt-1.0.5/src/pixopt/utils.py +22 -0
- pixopt-1.0.5/tests/__init__.py +0 -0
- pixopt-1.0.5/tests/test_core.py +358 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
name: Bump Version and Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
token: ${{ secrets.PAT }}
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
pip install -e ".[dev]"
|
|
30
|
+
|
|
31
|
+
- name: Run tests
|
|
32
|
+
run: pytest tests/ -v --tb=short
|
|
33
|
+
|
|
34
|
+
- name: Run ruff
|
|
35
|
+
run: ruff check src tests
|
|
36
|
+
|
|
37
|
+
- name: Run mypy
|
|
38
|
+
run: mypy src
|
|
39
|
+
|
|
40
|
+
bump:
|
|
41
|
+
needs: test
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
outputs:
|
|
44
|
+
tag: ${{ steps.version.outputs.tag }}
|
|
45
|
+
steps:
|
|
46
|
+
- name: Checkout
|
|
47
|
+
uses: actions/checkout@v4
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0
|
|
50
|
+
token: ${{ secrets.PAT }}
|
|
51
|
+
|
|
52
|
+
- name: Set up Python
|
|
53
|
+
uses: actions/setup-python@v5
|
|
54
|
+
with:
|
|
55
|
+
python-version: "3.12"
|
|
56
|
+
|
|
57
|
+
- name: Install dependencies
|
|
58
|
+
run: |
|
|
59
|
+
python -m pip install --upgrade pip
|
|
60
|
+
pip install python-semantic-release build
|
|
61
|
+
|
|
62
|
+
- name: Configure Git
|
|
63
|
+
run: |
|
|
64
|
+
git config --global user.name "github-actions"
|
|
65
|
+
git config --global user.email "actions@github.com"
|
|
66
|
+
|
|
67
|
+
- name: Bump version and create tag
|
|
68
|
+
id: version
|
|
69
|
+
env:
|
|
70
|
+
GH_TOKEN: ${{ secrets.PAT }}
|
|
71
|
+
run: |
|
|
72
|
+
set -x
|
|
73
|
+
echo "Running semantic-release version..."
|
|
74
|
+
if semantic-release version; then
|
|
75
|
+
echo "Version bump succeeded"
|
|
76
|
+
git log --oneline -5
|
|
77
|
+
git tag -l
|
|
78
|
+
git remote -v
|
|
79
|
+
git push origin main --tags
|
|
80
|
+
echo "tag=$(git describe --tags --abbrev=0)" >> "$GITHUB_OUTPUT"
|
|
81
|
+
else
|
|
82
|
+
echo "No version bump needed or failed"
|
|
83
|
+
git log --oneline -5
|
|
84
|
+
git tag -l
|
|
85
|
+
echo "tag=" >> "$GITHUB_OUTPUT"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
build:
|
|
89
|
+
needs: bump
|
|
90
|
+
if: needs.bump.outputs.tag != ''
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
steps:
|
|
93
|
+
- name: Checkout
|
|
94
|
+
uses: actions/checkout@v4
|
|
95
|
+
with:
|
|
96
|
+
ref: ${{ needs.bump.outputs.tag }}
|
|
97
|
+
fetch-depth: 0
|
|
98
|
+
|
|
99
|
+
- name: Set up Python
|
|
100
|
+
uses: actions/setup-python@v5
|
|
101
|
+
with:
|
|
102
|
+
python-version: "3.12"
|
|
103
|
+
|
|
104
|
+
- name: Install build dependencies
|
|
105
|
+
run: |
|
|
106
|
+
python -m pip install --upgrade pip
|
|
107
|
+
pip install build
|
|
108
|
+
|
|
109
|
+
- name: Build distribution
|
|
110
|
+
run: python -m build
|
|
111
|
+
|
|
112
|
+
- name: Upload artifacts
|
|
113
|
+
uses: actions/upload-artifact@v4
|
|
114
|
+
with:
|
|
115
|
+
name: dist
|
|
116
|
+
path: dist/
|
|
117
|
+
|
|
118
|
+
publish-pypi:
|
|
119
|
+
needs: build
|
|
120
|
+
if: needs.bump.outputs.tag != ''
|
|
121
|
+
runs-on: ubuntu-latest
|
|
122
|
+
environment:
|
|
123
|
+
name: pypi
|
|
124
|
+
url: https://pypi.org/p/pixopt
|
|
125
|
+
permissions:
|
|
126
|
+
id-token: write
|
|
127
|
+
steps:
|
|
128
|
+
- name: Download artifacts
|
|
129
|
+
uses: actions/download-artifact@v4
|
|
130
|
+
with:
|
|
131
|
+
name: dist
|
|
132
|
+
path: dist/
|
|
133
|
+
|
|
134
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
135
|
+
id: trusted-publish
|
|
136
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
137
|
+
continue-on-error: true
|
|
138
|
+
|
|
139
|
+
- name: Publish to PyPI (Token fallback)
|
|
140
|
+
if: steps.trusted-publish.outcome == 'failure'
|
|
141
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
142
|
+
with:
|
|
143
|
+
password: ${{ secrets.PYPI_TOKEN }}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, windows-latest]
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: pytest tests/ -v --tb=short
|
|
34
|
+
|
|
35
|
+
- name: Run ruff
|
|
36
|
+
run: ruff check src tests
|
|
37
|
+
|
|
38
|
+
- name: Run mypy
|
|
39
|
+
run: mypy src
|
|
40
|
+
|
|
41
|
+
build-docs:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
steps:
|
|
44
|
+
- name: Checkout
|
|
45
|
+
uses: actions/checkout@v4
|
|
46
|
+
|
|
47
|
+
- name: Set up Python
|
|
48
|
+
uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: "3.12"
|
|
51
|
+
|
|
52
|
+
- name: Install docs dependencies
|
|
53
|
+
run: |
|
|
54
|
+
python -m pip install --upgrade pip
|
|
55
|
+
pip install -e ".[docs]"
|
|
56
|
+
|
|
57
|
+
- name: Build docs
|
|
58
|
+
run: mkdocs build --strict
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pages
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
build:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
pip install -e ".[docs]"
|
|
33
|
+
|
|
34
|
+
- name: Build docs
|
|
35
|
+
run: mkdocs build --strict
|
|
36
|
+
|
|
37
|
+
- name: Upload artifact
|
|
38
|
+
uses: actions/upload-pages-artifact@v3
|
|
39
|
+
with:
|
|
40
|
+
path: site/
|
|
41
|
+
|
|
42
|
+
deploy:
|
|
43
|
+
needs: build
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
environment:
|
|
46
|
+
name: github-pages
|
|
47
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
48
|
+
steps:
|
|
49
|
+
- name: Deploy to GitHub Pages
|
|
50
|
+
id: deployment
|
|
51
|
+
uses: actions/deploy-pages@v4
|
pixopt-1.0.5/.gitignore
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
develop-eggs/
|
|
8
|
+
dist/
|
|
9
|
+
downloads/
|
|
10
|
+
eggs/
|
|
11
|
+
.eggs/
|
|
12
|
+
lib/
|
|
13
|
+
lib64/
|
|
14
|
+
parts/
|
|
15
|
+
sdist/
|
|
16
|
+
var/
|
|
17
|
+
wheels/
|
|
18
|
+
pip-wheel-metadata/
|
|
19
|
+
share/python-wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
MANIFEST
|
|
24
|
+
.env
|
|
25
|
+
.venv
|
|
26
|
+
env/
|
|
27
|
+
venv/
|
|
28
|
+
ENV/
|
|
29
|
+
env.bak/
|
|
30
|
+
venv.bak/
|
|
31
|
+
.mypy_cache/
|
|
32
|
+
.dmypy.json
|
|
33
|
+
dmypy.json
|
|
34
|
+
.pytest_cache/
|
|
35
|
+
.coverage
|
|
36
|
+
htmlcov/
|
|
37
|
+
*.log
|
|
38
|
+
.DS_Store
|
|
39
|
+
Thumbs.db
|
|
40
|
+
*.jpg
|
|
41
|
+
*.jpeg
|
|
42
|
+
*.png
|
|
43
|
+
*.webp
|
|
44
|
+
*.avif
|
|
45
|
+
*.bmp
|
|
46
|
+
*.gif
|
|
47
|
+
*.tiff
|
|
48
|
+
!tests/fixtures/
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Lossless compression mode for PNG/WEBP (`--lossless`).
|
|
13
|
+
- Interactive HTML before/after comparison slider (`pixopt compare`).
|
|
14
|
+
- Adaptive quality via binary search for target file size (`--target-size`).
|
|
15
|
+
- Responsive srcset image generation with HTML snippet output (`pixopt srcset`).
|
|
16
|
+
- Lazy-loading placeholders: dominant color, LQIP data URI, and blurhash (`pixopt placeholder`).
|
|
17
|
+
- Smart format detection: auto-select WEBP/JPEG/PNG based on image content (`--smart-format`).
|
|
18
|
+
- Backup originals before processing (`--backup`).
|
|
19
|
+
- Skip files below a minimum size threshold (`--min-size`).
|
|
20
|
+
- Animated GIF to animated WEBP conversion.
|
|
21
|
+
- Pure-Python SVG minification.
|
|
22
|
+
- HEIC/HEIF support via `pillow-heif`.
|
|
23
|
+
- GitHub Actions workflows for CI, release (PyPI), and documentation.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing!
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
1. Clone the repository:
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/MathiasPaulenko/pixopt.git
|
|
10
|
+
cd pixopt
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Create a virtual environment and install dependencies:
|
|
14
|
+
```bash
|
|
15
|
+
python -m venv .venv
|
|
16
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
17
|
+
pip install -e ".[dev]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Running tests
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pytest tests/ -v
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Code style
|
|
27
|
+
|
|
28
|
+
- Follow PEP 8.
|
|
29
|
+
- Use type hints where possible.
|
|
30
|
+
- Keep docstrings concise and descriptive.
|
|
31
|
+
|
|
32
|
+
## Pull requests
|
|
33
|
+
|
|
34
|
+
1. Fork the repository.
|
|
35
|
+
2. Create a feature branch.
|
|
36
|
+
3. Make your changes with tests.
|
|
37
|
+
4. Ensure `pytest` passes.
|
|
38
|
+
5. Submit a pull request with a clear description.
|
pixopt-1.0.5/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ImgOptimizer contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pixopt-1.0.5/MANIFEST.in
ADDED
pixopt-1.0.5/PKG-INFO
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pixopt
|
|
3
|
+
Version: 1.0.5
|
|
4
|
+
Summary: Fast Python image optimizer. Resize, compress, convert, and generate responsive assets.
|
|
5
|
+
Project-URL: Homepage, https://github.com/MathiasPaulenko/pixopt
|
|
6
|
+
Project-URL: Repository, https://github.com/MathiasPaulenko/pixopt
|
|
7
|
+
Project-URL: Issues, https://github.com/MathiasPaulenko/pixopt/issues
|
|
8
|
+
Author-email: Mathias Paulenko Echeverz <mathias.paulenko@outlook.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,compression,image,jpeg,optimization,png,webp
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: piexif>=1.1.3
|
|
24
|
+
Requires-Dist: pillow-heif>=1.0.0
|
|
25
|
+
Requires-Dist: pillow>=10.0.0
|
|
26
|
+
Requires-Dist: rich>=13.0.0
|
|
27
|
+
Requires-Dist: typer>=0.12.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: python-semantic-release>=9.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
34
|
+
Provides-Extra: docs
|
|
35
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
|
|
36
|
+
Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
|
|
37
|
+
Requires-Dist: mkdocstrings[python]>=0.25.0; extra == 'docs'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# pixopt
|
|
41
|
+
|
|
42
|
+
A powerful, easy-to-use Python library and CLI tool for optimizing images for web and storage.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Size reduction** – resize images to maximum width/height while keeping aspect ratio
|
|
47
|
+
- **Format conversion** – convert between JPEG, PNG, WEBP, AVIF, GIF, HEIC/HEIF and SVG
|
|
48
|
+
- **Animated GIF → WEBP** – convert animated GIFs to much lighter animated WEBP
|
|
49
|
+
- **SVG minification** – pure-Python SVG cleanup (no Node.js tools needed)
|
|
50
|
+
- **HEIC/HEIF import** – open iPhone photos directly thanks to `pillow-heif`
|
|
51
|
+
- **Lossless mode** – lossless PNG/WEBP compression for UI assets that need pixel-perfect fidelity
|
|
52
|
+
- **Adaptive quality** – binary-search quality to hit a target file size automatically
|
|
53
|
+
- **Visual comparison** – generate interactive HTML before/after sliders
|
|
54
|
+
- **Responsive srcset** – generate multiple width variants and HTML `<img srcset="...">` snippets
|
|
55
|
+
- **Lazy-loading placeholders** – extract dominant color, generate LQIP data URIs, or blurhash strings
|
|
56
|
+
- **Smart format detection** – auto-detect the most efficient format (photo → WEBP, graphic → PNG, transparent → WEBP)
|
|
57
|
+
- **Backup originals** – copy originals to a backup directory before processing
|
|
58
|
+
- **Min-size filter** – skip files already below a size threshold
|
|
59
|
+
- **Metadata stripping** – remove EXIF and other metadata to save space
|
|
60
|
+
- **Quality tuning** – adjustable compression quality (1-100)
|
|
61
|
+
- **Progressive JPEG** – enable progressive encoding for better web delivery
|
|
62
|
+
- **Batch processing** – optimize single files, directories, or multiple files at once
|
|
63
|
+
- **CLI with Typer** – intuitive command-line interface with beautiful output
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install pixopt
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## CLI Usage
|
|
72
|
+
|
|
73
|
+
### Optimize a single image
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pixopt optimize photo.jpg photo_optimized.jpg --quality 80 --width 1200
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Optimize all images in a directory
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pixopt optimize ./images ./optimized --recursive --quality 85 --format webp
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Batch optimize specific files
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pixopt batch photo1.jpg photo2.png photo3.bmp -o ./optimized --width 800
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Convert image format / extension
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pixopt convert photo.png photo.webp -f webp
|
|
95
|
+
pixopt convert ./images ./webp_images -r -f webp
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Generate favicon (.ico)
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pixopt favicon logo.png favicon.ico
|
|
102
|
+
pixopt favicon logo.png --size 16 --size 32 --size 48
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Convert animated GIF to animated WEBP
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
pixopt convert animation.gif animation.webp -f webp
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Optimize SVG
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pixopt convert icon.svg icon.min.svg
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Convert HEIC (iPhone) to JPEG
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pixopt convert photo.heic photo.jpg
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Lossless PNG/WEBP for UI assets
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pixopt convert icon.png icon.webp --lossless -f webp
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Adaptive quality (target file size)
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pixopt optimize photo.jpg --target-size 50
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Generate visual comparison HTML
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
pixopt compare photo.jpg comparison.html --open
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Generate responsive srcset images
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pixopt srcset hero.jpg --sizes 320,640,1024,1920 --output-dir ./responsive/
|
|
145
|
+
pixopt srcset hero.jpg --sizes 320,640,1024,1920 -f webp --html snippet.html
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Generate lazy-loading placeholders
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
pixopt placeholder photo.jpg --type color
|
|
152
|
+
pixopt placeholder photo.jpg --type lqip
|
|
153
|
+
pixopt placeholder photo.jpg --type blurhash -o blurhash.txt
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Smart format detection
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
pixopt optimize photo.jpg --smart-format
|
|
160
|
+
pixopt convert graphic.png output --smart-format
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Backup originals before processing
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
pixopt optimize ./images --backup ./originals --recursive
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Skip already-optimized files
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
pixopt optimize ./images --min-size 10 --recursive
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Inspect image info without optimizing
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pixopt info photo.jpg
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Available options
|
|
182
|
+
|
|
183
|
+
| Option | Description |
|
|
184
|
+
|--------|-------------|
|
|
185
|
+
| `-q, --quality` | JPEG/WEBP quality (1-100, default: 85) |
|
|
186
|
+
| `-w, --width` | Maximum width in pixels |
|
|
187
|
+
| `-h, --height` | Maximum height in pixels |
|
|
188
|
+
| `-f, --format` | Output format: auto, jpeg, png, webp, avif, original |
|
|
189
|
+
| `-s, --strip` | Remove metadata (default: True) |
|
|
190
|
+
| `--progressive` | Progressive JPEG encoding (default: True) |
|
|
191
|
+
| `-r, --recursive` | Process directories recursively |
|
|
192
|
+
| `--overwrite` | Overwrite source files |
|
|
193
|
+
| `--lossless` | Lossless PNG/WEBP compression |
|
|
194
|
+
| `--target-size` | Target file size in KB (adaptive quality) |
|
|
195
|
+
| `--smart-format` | Auto-detect the most efficient output format |
|
|
196
|
+
| `--backup` | Backup originals to this directory |
|
|
197
|
+
| `--min-size` | Skip files already smaller than this threshold (KB) |
|
|
198
|
+
|
|
199
|
+
## Library Usage
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from pixopt import optimize_image
|
|
203
|
+
from pixopt.models import OutputFormat
|
|
204
|
+
|
|
205
|
+
# Optimize a single image
|
|
206
|
+
result = optimize_image(
|
|
207
|
+
"photo.jpg",
|
|
208
|
+
"photo_optimized.webp",
|
|
209
|
+
max_width=1200,
|
|
210
|
+
quality=80,
|
|
211
|
+
strip_metadata=True,
|
|
212
|
+
output_format=OutputFormat.WEBP,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
print(f"Saved {result.savings_percent:.1f}% ({result.human_savings})")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Placeholders for lazy loading
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from pixopt.placeholder import generate_placeholder
|
|
222
|
+
|
|
223
|
+
color = generate_placeholder("photo.jpg", placeholder_type="color")
|
|
224
|
+
lqip = generate_placeholder("photo.jpg", placeholder_type="lqip")
|
|
225
|
+
blurhash = generate_placeholder("photo.jpg", placeholder_type="blurhash")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Smart format detection
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from pixopt.smart_format import detect_optimal_format
|
|
232
|
+
|
|
233
|
+
fmt = detect_optimal_format("photo.jpg")
|
|
234
|
+
# Returns OutputFormat.WEBP for photos, PNG for graphics, WEBP for transparent images
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Responsive srcset generation
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
from pixopt.srcset_generator import generate_srcset_images
|
|
241
|
+
|
|
242
|
+
variants = generate_srcset_images(
|
|
243
|
+
"hero.jpg",
|
|
244
|
+
"./responsive",
|
|
245
|
+
widths=[320, 640, 1024, 1920],
|
|
246
|
+
output_format="WEBP",
|
|
247
|
+
quality=80,
|
|
248
|
+
)
|
|
249
|
+
for v in variants:
|
|
250
|
+
print(f"{v.width}px -> {v.size_bytes} bytes")
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Batch / directory processing
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from pixopt import optimize_directory
|
|
257
|
+
|
|
258
|
+
results = optimize_directory(
|
|
259
|
+
"./images",
|
|
260
|
+
"./optimized",
|
|
261
|
+
recursive=True,
|
|
262
|
+
max_width=800,
|
|
263
|
+
quality=75,
|
|
264
|
+
output_format=OutputFormat.WEBP,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
for r in results:
|
|
268
|
+
if r.success:
|
|
269
|
+
print(f"{r.source_path.name}: {r.savings_percent:.1f}% saved")
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Development
|
|
273
|
+
|
|
274
|
+
Install in development mode:
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
git clone https://github.com/MathiasPaulenko/pixopt.git
|
|
278
|
+
cd pixopt
|
|
279
|
+
pip install -e ".[dev]"
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Run tests:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
pytest
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Run linter:
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
ruff check src tests
|
|
292
|
+
mypy src
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Build documentation locally:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
pip install -e ".[docs]"
|
|
299
|
+
mkdocs serve
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT
|