tekkenking/tinypeexi

A fluent Laravel package for integrating with the Lossless Media Service (aipeexi).

Maintainers

Package info

github.com/tekkenking/laravel_tiny_peexi

pkg:composer/tekkenking/tinypeexi

Statistics

Installs: 20

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-03-06 14:17 UTC

This package is auto-updated.

Last update: 2026-03-09 16:04:18 UTC


README

Upload, transform, and deliver images from your Laravel app in minutes.

TinyPeexi is a Laravel package that gives you a clean, fluent PHP API to interact with a self-hosted aipeexi / Lossless Media Service instance. Upload images, generate optimized variants, apply effects, create e-commerce product shots, and get CDN-ready delivery URLs โ€” all without leaving your Laravel codebase.

๐Ÿ“ฆ Installation

1. Require via Composer

composer require tekkenking/tinypeexi

2. Publish the Config File

php artisan vendor:publish --provider="Tekkenking\TinyPeexi\TinyPeexiServiceProvider"

This creates config/tinypeexi.php in your Laravel project.

3. Add Your Credentials to .env

TINYPEEXI_API_URL=http://localhost:8080
TINYPEEXI_TENANT_SLUG=dev
TINYPEEXI_API_KEY=sk_live_your_api_key_here

That's it. You're ready to use TinyPeexi.

๐Ÿš€ Quick Start

use Tekkenking\TinyPeexi\Facades\TinyPeexi;

// Upload an image
$asset = TinyPeexi::upload($request->file('photo'));

// Generate a 400px wide WebP variant
$variant = TinyPeexi::variant($asset->sha)
    ->resize(400)
    ->format('webp')
    ->quality(80)
    ->generate();

// Get the public URL
echo $variant->url();
// => http://localhost:8080/a/dev/abc123.../w400.webp

๐Ÿ“– Full API Reference

Uploading Assets

Upload from a Form Request

$asset = TinyPeexi::upload($request->file('image'));

echo $asset->sha; // "a1b2c3d4e5f6..."

Upload from a File Path

$asset = TinyPeexi::upload('/path/to/local/image.png');

echo $asset->sha;

The upload() method returns an AssetDto with the asset's sha (SHA-256 hash), which you'll use as the identifier for all future operations on this image.

Uploading Multiple Files at Once

TinyPeexi supports uploading multiple files in a single HTTP request (the backend defaults to a max of 10 files per request).

$files = $request->file('gallery_images'); // Array of UploadedFile

$assets = TinyPeexi::uploadMany($files);

foreach ($assets as $asset) {
    echo $asset->sha . "\n";
}

The uploadMany() method returns an array of AssetDto objects.

๐ŸŽจ Blade & View Helpers

TinyPeexi includes a global helper function tinypeexi() to natively integrate into your views. Furthermore, the Variant builder automatically converts directly into a URL string when echoed, keeping your Blade templates incredibly clean.

1. The Fluent Way (Cleanest)

You don't need to call ->url(). Just echo the builder directly!

<!-- Automatically outputs the URL string -->
<img src="{{ tinypeexi($product->image_sha)->resize(400)->format('webp') }}">

<img src="{{ tinypeexi($product->image_sha)->ecommerceVariant() }}">

2. The Array Shorthand (Shortest)

Pass parameters dynamically if you prefer:

<img src="{{ tinypeexi($product->image_sha, ['w' => 800, 'format' => 'webp']) }}">

3. Using Config Presets

Instantly apply predefined transformations from your tinypeexi.php config:

<img src="{{ tinypeexi($product->image_sha, 'thumbnail') }}">

Bulk Asset Migration

TinyPeexi includes a powerful Artisan command to migrate your existing local files into the Lossless Media Service. It is memory-safe, supports batching, queue-based variant generation, and uses a CSV file (storage/app/tinypeexi_migration_results.csv) for idempotency, meaning if the process dies, you can run the exact same command again and it will pick up right where it left off!

php artisan tinypeexi:migrate \
    --path="/var/www/uploads/2023|/var/www/uploads/2024" \
    --ext="jpg|png|webp" \
    --starts-with="product_|user_" \
    --variants="w800,h800|w200" \
    --batch=50
  • --path: Pipe-separated list of directories to scan.
  • --ext: Pipe-separated list of extensions to include.
  • --starts-with: Optional pipe-separated list of filename prefixes to include.
  • --variants: Optional pipe-separated list of variant dimensions to generate in the background (dispatches GenerateTinyPeexiVariantJob).
  • --batch: Number of originals to upload per HTTP request (default: 50).

Deleting Assets

TinyPeexi::delete($sha);

Returns true on success, throws TinyPeexiException on failure.

Generating Variants

The heart of TinyPeexi is the fluent Variant Builder. You start with TinyPeexi::variant($sha) and chain methods to describe the transformation you want.

Basic Resize

$variant = TinyPeexi::variant($sha)
    ->resize(800)
    ->format('jpeg')
    ->quality(85)
    ->generate();

echo $variant->url();

Resize with Exact Dimensions

$variant = TinyPeexi::variant($sha)
    ->resize(800, 600)    // width, height
    ->fit('cover')        // crop to fill
    ->generate();

Available Fit Modes

Mode Behavior
cover Crop to fill exact dimensions
contain Fit within dimensions, letterbox if needed
fill Stretch to exact dimensions (may distort)
inside Fit inside, never upscale (default)
outside Fit outside, may upscale to cover

Retina / HiDPI Support

$variant = TinyPeexi::variant($sha)
    ->resize(400)
    ->dpr(2.0)    // Outputs 800px wide for retina displays
    ->generate();

Cropping

Smart Crop

$variant = TinyPeexi::variant($sha)
    ->resize(400, 400)
    ->fit('cover')
    ->crop('face')        // AI-powered face detection crop
    ->generate();

Available Crop Positions

Position Description
center Center of image (default)
top Top edge
bottom Bottom edge
left Left edge
right Right edge
topleft Top-left corner
topright Top-right corner
bottomleft Bottom-left corner
bottomright Bottom-right corner
face AI face detection (Pro tier)
entropy Smart crop using entropy analysis
attention Smart crop using saliency/attention

Manual Crop Rectangle

$variant = TinyPeexi::variant($sha)
    ->cropRect(100, 50, 600, 400) // x, y, width, height
    ->generate();

Auto-Trim Whitespace

$variant = TinyPeexi::variant($sha)
    ->trim(25) // threshold 0-255
    ->generate();

Orientation

// Rotate 90 degrees clockwise
$variant = TinyPeexi::variant($sha)
    ->rotate(90)
    ->generate();

// Flip horizontally (mirror)
$variant = TinyPeexi::variant($sha)
    ->flip('h')
    ->generate();

// Auto-fix EXIF orientation
$variant = TinyPeexi::variant($sha)
    ->autoOrient()
    ->generate();
Rotate Values Flip Values
0, 90, 180, 270, -90 h (horizontal), v (vertical), both

Effects

$variant = TinyPeexi::variant($sha)
    ->resize(800)
    ->blur(5.0)           // Gaussian blur (0.3 - 1000.0)
    ->sharpen(1.5)        // Sharpen (0.5 - 10.0)
    ->brightness(1.2)     // Brightness (0.1 - 10.0)
    ->contrast(1.1)       // Contrast (0.1 - 10.0)
    ->saturation(1.5)     // Saturation (0.0 - 2.0)
    ->generate();

Grayscale

$variant = TinyPeexi::variant($sha)
    ->grayscale()
    ->format('jpeg')
    ->generate();

E-Commerce Product Images

This is where TinyPeexi really shines. Creating uniform, professional product images is a single method call.

Using the Fluent Builder

$variant = TinyPeexi::variant($sha)
    ->format('jpeg')
    ->ecommerce()          // Flag as e-commerce operation
    ->canvas(1024)         // 1024ร—1024 square canvas
    ->resize(800)          // Product fits within 800px
    ->pad(40)              // 40px breathing room
    ->background('white')  // Clean white background
    ->quality(90)
    ->generate();

echo $variant->url();
// => http://localhost:8080/a/dev/abc123.../w800.jpg

Using the Shorthand Helper

If you use the same e-commerce settings repeatedly, use the shorthand:

$variant = TinyPeexi::variant($sha)->ecommerceVariant();
// Uses defaults from config: canvas=1024, pad=40, bg=white, jpeg, q=85

Custom E-Commerce Settings

$variant = TinyPeexi::variant($sha)->ecommerceVariant(
    canvasSize: 800,
    padding: 20,
    background: 'transparent',
    format: 'png',
    quality: 100,
);

With AI Background Removal

$variant = TinyPeexi::variant($sha)
    ->removeBg()           // Remove background via AI
    ->canvas(1024)
    ->pad(40)
    ->background('white')
    ->format('jpeg')
    ->quality(90)
    ->generate();

Watermarks

Image Watermark

// First, upload your watermark image (do this once)
$watermark = TinyPeexi::upload('/path/to/watermark.png');

// Then apply it to any variant
$variant = TinyPeexi::variant($sha)
    ->resize(1200)
    ->watermark(
        assetSha: $watermark->sha,
        position: 'bottomright',
        opacity: 0.5,
        scale: 0.15,
    )
    ->generate();

Available Watermark Positions

Position Description
center Center of image
topleft Top-left corner
topright Top-right corner
bottomleft Bottom-left corner
bottomright Bottom-right corner (default)
tile Tile across entire image

Using Presets

Define reusable transformation presets in config/tinypeexi.php:

// In config/tinypeexi.php
'defaults' => [
    'thumbnail' => [
        'width' => 200,
        'height' => 200,
        'fit' => 'cover',
        'crop' => 'center',
        'format' => 'webp',
        'quality' => 80,
    ],
]

Then use them in your code:

$variant = TinyPeexi::variant($sha)
    ->preset('thumbnail')
    ->generate();

You can define as many presets as you need (e.g., hero_banner, avatar, og_image, etc.).

Batch Variant Generation

Generate multiple sizes at once:

TinyPeexi::variant($sha)->batch(
    sizes: [400, 800, 1200],
    format: 'webp',
);
// Queues: w400.webp, w800.webp, w1200.webp

Output Formats

Format Extension Notes
jpeg .jpg Best for photos
png .png Lossless, supports transparency
webp .webp Modern, smaller files (default)
avif .avif Newest, smallest files
gif .gif Animated images
tiff .tiff Print-quality
ico .ico Favicons
svg .svg Vector graphics

โš™๏ธ Configuration Reference

After publishing, edit config/tinypeexi.php:

return [
    // === REQUIRED ===
    'api_url'      => env('TINYPEEXI_API_URL', 'http://localhost:8080'),
    'delivery_url' => env('TINYPEEXI_DELIVERY_URL', env('TINYPEEXI_API_URL', 'http://localhost:8080')),
    'tenant_slug'  => env('TINYPEEXI_TENANT_SLUG', 'dev'),
    'api_key'      => env('TINYPEEXI_API_KEY', ''),

    // === ADVANCED (optional) ===
    'advanced' => [
        'timeout'     => env('TINYPEEXI_TIMEOUT', 10),      // seconds
        'retries'     => env('TINYPEEXI_RETRIES', 3),
        'retry_delay' => env('TINYPEEXI_RETRY_DELAY', 500), // ms

        'upload' => [
            'max_filesize_bytes' => env('TINYPEEXI_MAX_UPLOAD_SIZE', 26214400), // 25MB
            'allowed_mimes'      => ['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/gif'],
            'strip_metadata'     => env('TINYPEEXI_STRIP_METADATA', true),
            'auto_optimize'      => env('TINYPEEXI_AUTO_OPTIMIZE', false),
        ],
    ],

    // === PRESETS (optional) ===
    'defaults' => [
        'ecommerce' => [
            'canvas' => 1024, 'pad' => 40, 'background' => 'white',
            'format' => 'jpeg', 'quality' => 85,
        ],
        'thumbnail' => [
            'width' => 200, 'height' => 200, 'fit' => 'cover',
            'crop' => 'center', 'format' => 'webp', 'quality' => 80,
        ],
    ],
];

Environment Variables

Variable Default Description
TINYPEEXI_API_URL http://localhost:8080 Base URL of aipeexi instance
TINYPEEXI_DELIVERY_URL Same as API URL CDN/delivery URL
TINYPEEXI_TENANT_SLUG dev Your tenant slug
TINYPEEXI_API_KEY (empty) Your API key
TINYPEEXI_TIMEOUT 10 HTTP timeout (seconds)
TINYPEEXI_RETRIES 3 Retry count for failed requests
TINYPEEXI_RETRY_DELAY 500 Delay between retries (ms)
TINYPEEXI_MAX_UPLOAD_SIZE 26214400 Max upload size (bytes)
TINYPEEXI_STRIP_METADATA true Strip EXIF on upload
TINYPEEXI_AUTO_OPTIMIZE false Auto-optimize on upload

๐Ÿง‘โ€๐Ÿ’ป Real-World Examples

Profile Avatar Upload

// In a Controller
public function updateAvatar(Request $request)
{
    $request->validate(['avatar' => 'required|image|max:5120']);

    $asset = TinyPeexi::upload($request->file('avatar'));

    // Generate a small square avatar
    $variant = TinyPeexi::variant($asset->sha)
        ->resize(200, 200)
        ->fit('cover')
        ->crop('face')
        ->format('webp')
        ->quality(80)
        ->generate();

    auth()->user()->update(['avatar_url' => $variant->url()]);

    return back()->with('success', 'Avatar updated!');
}

Product Image Upload (E-Commerce)

public function storeProduct(Request $request)
{
    $request->validate(['photo' => 'required|image|max:25600']);

    $asset = TinyPeexi::upload($request->file('photo'));

    // Professional product shot with white background
    $main = TinyPeexi::variant($asset->sha)->ecommerceVariant();

    // Also create a thumbnail
    $thumb = TinyPeexi::variant($asset->sha)
        ->preset('thumbnail')
        ->generate();

    Product::create([
        'name'          => $request->name,
        'image_sha'     => $asset->sha,
        'image_url'     => $main->url(),
        'thumbnail_url' => $thumb->url(),
    ]);

    return redirect()->route('products.index');
}

Responsive Image Set

$asset = TinyPeexi::upload($request->file('hero'));

// Generate multiple sizes for responsive <picture> element
TinyPeexi::variant($asset->sha)->batch(
    sizes: [400, 800, 1200, 1600],
    format: 'webp',
);

// In your Blade template:
// <picture>
//   <source srcset="/a/dev/{sha}/w400.webp 400w,
//                   /a/dev/{sha}/w800.webp 800w,
//                   /a/dev/{sha}/w1200.webp 1200w,
//                   /a/dev/{sha}/w1600.webp 1600w"
//           type="image/webp">
// </picture>

Blog Post with Watermarked Images

$watermarkAsset = TinyPeexi::upload('/path/to/blog-watermark.png');

$asset = TinyPeexi::upload($request->file('blog_image'));

$variant = TinyPeexi::variant($asset->sha)
    ->resize(1200)
    ->format('jpeg')
    ->quality(85)
    ->watermark(
        assetSha: $watermarkAsset->sha,
        position: 'bottomright',
        opacity: 0.3,
        scale: 0.1,
    )
    ->generate();

๐Ÿ”ง Error Handling

All errors throw TinyPeexiException:

use Tekkenking\TinyPeexi\Exceptions\TinyPeexiException;

try {
    $asset = TinyPeexi::upload($request->file('image'));
} catch (TinyPeexiException $e) {
    Log::error('TinyPeexi upload failed', [
        'message' => $e->getMessage(),
        'code'    => $e->getCode(),
    ]);

    return back()->withErrors(['image' => 'Image upload failed. Please try again.']);
}

๐Ÿงช Testing

When writing tests, you can mock the TinyPeexi facade:

use Tekkenking\TinyPeexi\Facades\TinyPeexi;
use Tekkenking\TinyPeexi\DTOs\AssetDto;

TinyPeexi::shouldReceive('upload')
    ->once()
    ->andReturn(new AssetDto('fake_sha_256_hash'));

๐Ÿ“‹ Method Cheat Sheet

Method Description
TinyPeexi::upload($file) Upload an image, returns AssetDto
TinyPeexi::uploadMany(array $files) Upload multiple images, returns AssetDto[]
TinyPeexi::delete($sha) Delete an asset
TinyPeexi::variant($sha) Start building a variant
->resize($w, $h?) Set target dimensions
->fit($mode) Set fit mode
->dpr($ratio) Set device pixel ratio
->format($fmt) Set output format
->quality($q) Set compression quality
->progressive() Enable progressive JPEG
->stripMetadata() Strip EXIF metadata
->crop($position) Set crop anchor
->cropRect($x,$y,$w,$h) Manual crop rectangle
->trim($threshold?) Auto-trim whitespace
->rotate($degrees) Rotate image
->flip($direction) Flip image
->autoOrient() Auto-fix EXIF orientation
->blur($sigma) Apply Gaussian blur
->sharpen($amount) Apply sharpening
->brightness($value) Adjust brightness
->contrast($value) Adjust contrast
->saturation($value) Adjust saturation
->grayscale() Convert to grayscale
->canvas($size) Set square canvas size
->pad($px) Set canvas padding
->background($color) Set background color
->removeBg() AI background removal
->ecommerce() Flag as e-commerce operation
->ecommerceVariant(...) Shorthand for e-commerce variant
->watermark(...) Add image watermark
->preset($name) Apply a config preset
->batch($sizes, $fmt?) Generate multiple sizes
->generate() Execute and return VariantDto
->toArray() Debug: get params as array

๐Ÿ“„ License

MIT License. See LICENSE for details.

๐Ÿค Contributing

Pull requests are welcome! Please open an issue first to discuss what you'd like to change.

Built with โค๏ธ for Laravel developers by tekkenking.