Metadata-Version: 2.4
Name: django-pixbin
Version: 0.1.0
Summary: Django integration for the Pixbin Image API
Author: Deni Bertovic
License: MIT
Project-URL: Documentation, https://github.com/denibertovic/django-pixbin#readme
Project-URL: Source, https://github.com/denibertovic/django-pixbin
Project-URL: Tracker, https://github.com/denibertovic/django-pixbin/issues
Keywords: django,pixbin,images,upload,transformation,thumbnail
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Framework :: Django :: 3.2
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.0
Classifier: Framework :: Django :: 5.1
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Django>=3.2
Requires-Dist: pixbin[full]>=0.1.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-django>=4.5; extra == "dev"
Requires-Dist: ruff>=0.16; extra == "dev"
Dynamic: license-file

# Django Pixbin SDK

Django integration for the [Pixbin Image API](https://pixbin.net). Provides template tags, model fields, forms, and management commands for working with Pixbin images in Django projects.

## Features

- 🎨 **Template Tags** - Easy image transformations in templates
- 📦 **Model Fields** - Custom field for storing Pixbin image IDs
- 📝 **Forms** - Form fields and widgets for image upload
- 🔧 **Management Commands** - CLI tools for image management

## Installation

```bash
uv add django-pixbin
```

Or with pip:

```bash
pip install django-pixbin
```

## Quick Start

### 1. Add to Installed Apps

```python
# settings.py
INSTALLED_APPS = [
    ...
    'django_pixbin',
]

# Configure your Pixbin API credentials
PIXBIN_API_TOKEN = 'your_api_token_here'  # Required
PIXBIN_BASE_URL = 'https://pixbin.net'     # Optional, defaults to https://pixbin.net
```

Get your API token from your [Pixbin account settings](https://pixbin.net/account/settings).

### 2. Use in Models

```python
from django.db import models
from django_pixbin.fields import PixbinImageField

class Product(models.Model):
    name = models.CharField(max_length=200)
    image = PixbinImageField(blank=True, null=True)

    def __str__(self):
        return self.name
```

### 3. Use in Templates

```django
{% load pixbin %}

<!-- Simple transformation -->
<img src="{% pixbin_transform product.image 'resize:300x300:fit' %}" alt="{{ product.name }}">

<!-- Thumbnail helper -->
<img src="{% pixbin_thumbnail product.image 300 300 %}" alt="Thumbnail">

<!-- Responsive images with srcset -->
<img
    src="{% pixbin_transform product.image 'resize:800x600:fit' %}"
    srcset="{% pixbin_srcset product.image '400x300:fit@400w,800x600:fit@800w,1200x900:fit@1200w' %}"
    sizes="(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px"
    alt="{{ product.name }}"
>

<!-- Template filter -->
<img src="{{ product.image|pixbin_url:'resize:300x300:fit' }}" alt="{{ product.name }}">
```

### 4. Upload Images

```python
from django_pixbin import get_pixbin_client

# Get configured client
client = get_pixbin_client()

# Upload image
image_id = client.upload_file('path/to/photo.jpg')

# Save to model
product = Product.objects.create(
    name="Widget",
    image=image_id
)
```

## Template Tags Reference

### `pixbin_transform`

Generate a transformation URL for an image.

```django
{% pixbin_transform image_id 'resize:300x300:fit,quality:85' %}
```

**Parameters:**

- `image_id`: Pixbin image UUID (string or UUID object)
- `params`: Transformation parameters string

### `pixbin_thumbnail`

Generate a thumbnail URL with common defaults.

```django
{% pixbin_thumbnail image_id 300 300 %}
{% pixbin_thumbnail image_id 300 300 90 %}  {# Custom quality #}
```

**Parameters:**

- `image_id`: Pixbin image UUID
- `width`: Thumbnail width in pixels
- `height`: Thumbnail height in pixels
- `quality`: JPEG quality 1-100 (default: 85)

### `pixbin_srcset`

Generate srcset attribute for responsive images.

```django
{% pixbin_srcset image_id '300x300:fit@400w,600x600:fit@800w,1200x1200:fit@1600w' %}
```

**Parameters:**

- `image_id`: Pixbin image UUID
- `sizes`: Comma-separated list of transformations with width descriptors

**Output:**

```
https://pixbin.net/api/v1/image/sig/resize:300x300:fit/id 400w,
https://pixbin.net/api/v1/image/sig/resize:600x600:fit/id 800w,
https://pixbin.net/api/v1/image/sig/resize:1200x1200:fit/id 1600w
```

### `pixbin_url` (Filter)

Template filter for generating Pixbin URLs.

```django
<img src="{{ product.image|pixbin_url:'resize:300x300:fit' }}" alt="Product">
<img src="{{ product.image|pixbin_url }}" alt="Original">
```

**Parameters:**

- `image_id`: Pixbin image UUID
- `params`: Optional transformation parameters

### `pixbin_js`

Include JavaScript for automatic loading states with shimmer animation. Handles polling for image variants that are still being generated.

```django
{% load pixbin %}

<!-- In base template head or before </body> -->
{% pixbin_js %}

<!-- With options -->
{% pixbin_js debug=True poll_interval=1000 max_retries=30 %}
```

**Parameters:**

- `debug`: Enable console logging (default: `False`)
- `poll_interval`: Milliseconds between polling attempts (default: `2000`)
- `max_retries`: Maximum poll attempts before giving up (default: `30`)

**Features:**

- Auto-detects Pixbin transformation URLs
- Shows blurred placeholder with shimmer animation while processing
- Polls for completion and transitions to sharp image
- Fires custom events: `pixbin:processing`, `pixbin:ready`, `pixbin:timeout`

**Usage:**

Images are auto-detected by URL pattern, or you can explicitly mark them:

```django
<img src="{% pixbin_transform product.image 'resize:300x300' %}" data-pixbin="true" width="300" height="300">
```

**Tip:** Always include `width` and `height` attributes so the placeholder area is correctly sized.

## Model Field Reference

### `PixbinImageField`

Custom model field for storing Pixbin image IDs.

```python
from django_pixbin.fields import PixbinImageField

class Product(models.Model):
    image = PixbinImageField(blank=True, null=True)
```

**Helper Methods:**

The field automatically adds helper methods to your model:

```python
# Get image URL (original or transformed)
product.get_image_url()  # Original
product.get_image_url('resize:300x300:fit')
# → 'https://pixbin.net/api/v1/image/sig/resize:300x300:fit/uuid'

# Get path only (no host)
product.get_image_url(include_host=False)
# → '/api/v1/image/uuid'
```

## Form Fields & Widgets

### `PixbinImageField`

Form field for Pixbin image IDs. Uses `PixbinImageWidget` by default.

```python
from django_pixbin.forms import PixbinImageField

class ProductForm(forms.Form):
    image = PixbinImageField(
        required=False,
        verify_exists=True,  # Optional: calls the API to check the image exists
        help_text="Enter Pixbin image ID"
    )
```

### `PixbinImageWidget`

Text input with a live thumbnail preview. ModelForms and the admin get it
automatically for the model `PixbinImageField`. The preview calls the
`check_image_exists` view, so include `django_pixbin.urls` (see below).

```python
from django_pixbin.forms import PixbinImageWidget

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ['name', 'image']
        widgets = {
            'image': PixbinImageWidget(thumbnail_size='200x200')  # default 300x300
        }
```

### `PixbinImageUploadWidget`

Drag-and-drop upload straight from the browser to Pixbin storage. The widget
asks your Django backend for a presigned URL, uploads the file, then stores the
resulting image UUID in a hidden input. The API token never reaches the browser.

```python
from django_pixbin.forms import PixbinImageField, PixbinImageUploadWidget

class ProductForm(forms.Form):
    image = PixbinImageField(
        verify_exists=True,
        widget=PixbinImageUploadWidget(),
    )
```

Render `{{ form.media }}` in the page head to load the widget's JS and CSS.

### URLs

The upload widget and the preview widget need these views. All three require a
logged-in user.

```python
# urls.py
from django.urls import path, include

urlpatterns = [
    ...
    path('pixbin/', include('django_pixbin.urls')),
]
```

- `presigned-url/`: starts an upload and returns the presigned S3 POST
- `complete-upload/`: finalizes the upload and returns a preview URL
- `image/<uuid>/exists/`: checks an image and returns a thumbnail URL

## Management Commands

### Check Configuration

```bash
python manage.py pixbin_status
```

Checks:

- ✓ PIXBIN_API_TOKEN is set
- ✓ PIXBIN_BASE_URL configuration
- ✓ Client initialization
- ✓ API connectivity

### Upload Images

```bash
# Upload single image
python manage.py pixbin_upload photo.jpg

# Upload multiple images
python manage.py pixbin_upload photo1.jpg photo2.jpg photo3.jpg

# With options
python manage.py pixbin_upload photo.jpg --caption "Product photo" --private

# Temporary images (24 hours)
python manage.py pixbin_upload photo.jpg --retention-hours 24

# Permanent images (default)
python manage.py pixbin_upload photo.jpg --retention-hours -1
```

## Transformation Parameters

### Resize

```
resize:WIDTHxHEIGHT:MODE
```

**Modes:**

- `fit` - Fit within dimensions (maintains aspect ratio)
- `fill` - Fill dimensions (crops if needed)
- `exact` - Exact dimensions (may distort)

**Examples:**

```django
{% pixbin_transform image 'resize:300x300:fit' %}
{% pixbin_transform image 'resize:800x600:fill' %}
```

### Crop

```
crop:MODE
```

**Modes:**

- `smart` - Smart crop (face detection)
- `center` - Center crop

### Quality

```
quality:N
```

JPEG quality 1-100 (default: 85)

```django
{% pixbin_transform image 'resize:800x600:fit,quality:90' %}
```

### Format

```
format:TYPE
```

**Types:** `jpeg`, `png`, `webp`, `avif`

```django
{% pixbin_transform image 'resize:800x600:fit,format:webp' %}
```

### Filters

```
blur:RADIUS          # 0-50
sharpen:FACTOR       # 0-10
grayscale           # Convert to grayscale
```

**Examples:**

```django
{% pixbin_transform image 'resize:500x500:fit,blur:5' %}
{% pixbin_transform image 'resize:800x600:fit,sharpen:2' %}
{% pixbin_transform image 'resize:400x400:fit,grayscale' %}
```

### Combining Transformations

Combine multiple transformations with commas:

```django
{% pixbin_transform image 'resize:800x600:fill,crop:smart,quality:85,format:webp' %}
{% pixbin_transform image 'crop:center,resize:500x500:exact,grayscale,sharpen:3' %}
```

## Usage in Python

### Get Client

```python
from django_pixbin import get_pixbin_client

client = get_pixbin_client()
```

### Upload Images

```python
# Upload file
image_id = client.upload_file('photo.jpg')

# Upload with options
image_id = client.upload_file(
    'photo.jpg',
    retention_hours=-1,  # Permanent
    caption='Product photo',
    private=True
)

# Upload file object
with open('photo.jpg', 'rb') as f:
    image_id = client.upload_file(f)
```

### Generate URLs

```python
# Original image
url = client.transform_url(image_id, '')

# Transformed image
url = client.transform_url(image_id, 'resize:300x300:fit')

# Multiple transformations
url = client.transform_url(
    image_id,
    'resize:800x600:fill,quality:85,format:webp'
)
```

## Complete Example

```python
# models.py
from django.db import models
from django_pixbin.fields import PixbinImageField

class Product(models.Model):
    name = models.CharField(max_length=200)
    description = models.TextField()
    image = PixbinImageField(blank=True, null=True)

    def __str__(self):
        return self.name
```

```python
# views.py
from django.shortcuts import render, redirect
from django_pixbin import get_pixbin_client
from .models import Product

def upload_product(request):
    if request.method == 'POST':
        # Upload image to Pixbin
        client = get_pixbin_client()
        image_id = client.upload_file(request.FILES['image'])

        # Create product
        product = Product.objects.create(
            name=request.POST['name'],
            description=request.POST['description'],
            image=image_id
        )

        return redirect('product_detail', pk=product.pk)

    return render(request, 'upload.html')
```

```django
{# templates/product_detail.html #}
{% load pixbin %}

<div class="product">
    <h1>{{ product.name }}</h1>

    <!-- Responsive image with srcset -->
    <img
        src="{% pixbin_transform product.image 'resize:800x600:fit' %}"
        srcset="{% pixbin_srcset product.image '400x300:fit@400w,800x600:fit@800w,1200x900:fit@1200w' %}"
        sizes="(max-width: 600px) 400px, (max-width: 900px) 800px, 1200px"
        alt="{{ product.name }}"
        class="product-image"
        loading="lazy"
    >

    <p>{{ product.description }}</p>
</div>
```

## Configuration

### Settings

```python
# settings.py

# Required
PIXBIN_API_TOKEN = 'your_api_token_here'

# Optional
PIXBIN_BASE_URL = 'https://pixbin.net'  # Defaults to https://pixbin.net
```

### Get API Token

1. Sign up for a [Pixbin Pro account](https://pixbin.net/pricing)
2. Go to [Account Settings](https://pixbin.net/account/settings)
3. Generate an API token
4. Add to your Django settings

## Requirements

- Django >= 3.2
- Python >= 3.8
- `pixbin[full]` (Python SDK, pulls in Pillow so uploads include image dimensions)

## Contributing

The repo ships a [devenv](https://devenv.sh) configuration that provides
Python, a virtualenv with the package installed in editable mode, and
pre-commit hooks for ruff, prettier and djlint.

```bash
# One time: install Nix and devenv, see https://devenv.sh/getting-started/
devenv shell        # or `direnv allow` to enter the shell automatically

make help           # list all targets
make lint           # ruff lint and format check
make format         # auto-fix and reformat
make test           # run the test suite, e.g. make test ARGS="-k widget"
make check          # lint + test, the same gates CI runs on pull requests
make build          # build sdist and wheel into dist/
```

Without devenv, any Python 3.8 or newer works. With uv:

```bash
uv sync             # installs the package plus the dev tooling into .venv
uv run make check
```

Or with plain pip:

```bash
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
make check
```

Releases are published to PyPI by the `cicd.yaml` workflow when a `v*` tag is
pushed. The tag must match the version in `pyproject.toml`. To cut one:

```bash
make release VERSION=0.2.0   # bumps pyproject.toml, commits, tags v0.2.0
git push origin main v0.2.0  # triggers the publish job
```

## License

MIT License - see LICENSE file for details

## Links

- [Pixbin Website](https://pixbin.net)
- [API Documentation](https://pixbin.net/api/v1/docs)
- [Python SDK](https://github.com/denibertovic/pixbin-python)
- [Account Settings](https://pixbin.net/account/settings)

## Support

For issues and questions:

- GitHub Issues: [github.com/denibertovic/django-pixbin/issues](https://github.com/denibertovic/django-pixbin/issues)
- Contact: https://pixbin.net/contact/
