PHPackages                             rllngr/kirby-videozer - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. rllngr/kirby-videozer

ActiveKirby-plugin[File &amp; Storage](/categories/file-storage)

rllngr/kirby-videozer
=====================

Kirby CMS plugin — compresses uploaded videos with FFmpeg and generates poster frames

1.3.0(1mo ago)111MITPHPPHP &gt;=8.0

Since Mar 26Pushed 1mo agoCompare

[ Source](https://github.com/RLLNGR/kirby-videozer)[ Packagist](https://packagist.org/packages/rllngr/kirby-videozer)[ Docs](https://github.com/rllngr/kirby-videozer)[ RSS](/packages/rllngr-kirby-videozer/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (4)Dependencies (3)Versions (5)Used By (0)

Kirby Videozer
==============

[](#kirby-videozer)

A [Kirby CMS](https://getkirby.com) plugin that automatically compresses uploaded videos using FFmpeg and generates poster frames. All processed files are stored in a `video-cache/` directory outside Kirby's content folder and served directly by the web server.

**What it produces for each uploaded video:**

- `{name}-compressed.mp4` — H.264/AAC optimized MP4
- `{name}-opt.webm` — VP9/Opus WebM variant (optional)
- `{name}-poster.jpg` — Thumbnail extracted at 1 second (or 10% of duration)

The poster is also copied to the page's content directory so Kirby's thumb system can generate srcset variants and the Panel can show a video preview image. The video's orientation (`portrait`/`landscape`/`square`) is auto-detected from its dimensions and saved as a content field on upload.

---

Requirements
------------

[](#requirements)

- Kirby CMS ^4 || ^5
- PHP &gt;=8.0
- [FFmpeg](https://ffmpeg.org) installed on the server

---

Installation
------------

[](#installation)

### Via Composer (recommended)

[](#via-composer-recommended)

```
composer require rllngr/kirby-videozer
```

### Manual

[](#manual)

Download and extract the plugin into `site/plugins/videozer/`.

---

Configuration
-------------

[](#configuration)

Add options to your `site/config/config.php`:

```
return [
    // Path to the ffmpeg binary. Defaults to 'ffmpeg' (resolved via PATH).
    'rllngr.videozer.ffmpeg' => '/usr/local/bin/ffmpeg',

    // Named quality preset to use (web/high/low). Overrides crf/max_width when set.
    // Default: null (uses individual crf/max_width options below)
    'rllngr.videozer.preset' => 'web',

    // Built-in presets — override or extend as needed.
    'rllngr.videozer.presets' => [
        'web'  => ['crf' => 23, 'max_width' => 1280, 'ffpreset' => 'slow'],
        'high' => ['crf' => 18, 'max_width' => 1920, 'ffpreset' => 'slow'],
        'low'  => ['crf' => 28, 'max_width' => 720,  'ffpreset' => 'fast'],
    ],

    // H.264 CRF quality (0–51, lower = better quality). Used when no preset is set.
    'rllngr.videozer.crf' => 28,

    // Maximum output width in pixels (aspect ratio preserved). Default: 1920
    'rllngr.videozer.max_width' => 1920,

    // Strip audio from the output. Recommended for silent background/portfolio videos.
    // Default: false
    'rllngr.videozer.strip_audio' => false,

    // MP4 audio bitrate. Ignored when strip_audio is true. Default: '96k'
    'rllngr.videozer.audio_bitrate' => '96k',

    // Whether to generate a VP9/WebM variant. Default: true
    'rllngr.videozer.generate_webm' => true,

    // WebM CRF quality. Default: 33
    'rllngr.videozer.webm_crf' => 33,

    // WebM audio bitrate. Ignored when strip_audio is true. Default: '64k'
    'rllngr.videozer.webm_audio_bitrate' => '64k',

    // Restrict processing to specific page templates. null = all pages.
    'rllngr.videozer.templates' => ['project', 'article'],

    // File template to assign after processing. false = skip.
    'rllngr.videozer.change_template' => 'video',

    // Name of the gallery field to clean up on file deletion. null = skip.
    'rllngr.videozer.gallery_field' => 'gallery',

    // Custom base directory for the video cache. null = {webroot}/video-cache/.
    'rllngr.videozer.cache_dir' => null,
];
```

---

Usage
-----

[](#usage)

### Page method

[](#page-method)

Use `$page->videozFiles()` to retrieve files excluding any generated variants (useful for gallery fields):

```
// In a blueprint field:
query: page.videozFiles

// In a template:
foreach ($page->videozFiles() as $file) { ... }
```

### File methods

[](#file-methods)

All methods are available directly on video file objects:

```
// Best available MP4 URL (compressed if cached, original otherwise)
$file->videozUrl()

// WebM URL if generated, null otherwise
$file->videozWebmUrl()

// Poster URL — always returns the expected URL (browser handles 404 gracefully via @error)
$file->videozPosterUrl()

// Srcset for the poster frame via Kirby's thumb system (requires content-dir copy to exist)
$file->videozPosterSrcset()

// Whether a compressed MP4 exists in the cache
$file->hasVideoz()

// Whether a poster exists in the cache
$file->videozHasPoster()

// Orientation string: 'portrait', 'landscape', or 'square'
// Returns the saved panel value if set, otherwise auto-detects from ffprobe/image dimensions
$file->videozOrientation()

// Panel image for use with `image.query` — returns poster for videos, self for images, null otherwise
$file->videozPanelImage()

// FFprobe metadata: duration, size, bitrate, width, height, codec, fps, hasAudio
$file->videozInfo()

// Manually trigger processing (optional preset + force flag)
$file->videozerProcess('high', force: true)

// Manually regenerate poster (optional force + timestamp in seconds)
$file->videozGeneratePoster(force: true, timestamp: 3.5)
```

### Cache URL pattern

[](#cache-url-pattern)

Generated files are stored at:

```
/video-cache/{page-id}/{name}-compressed.mp4
/video-cache/{page-id}/{name}-opt.webm
/video-cache/{page-id}/{name}-poster.jpg

```

Make sure your web server serves this directory statically. Example nginx rule:

```
location /video-cache/ {
    root /path/to/public;
    expires 30d;
    add_header Cache-Control "public";
}
```

### Panel API routes

[](#panel-api-routes)

The plugin exposes three authenticated Panel API endpoints:

MethodEndpointDescription`GET``/api/videozer/status`FFmpeg availability + video stats`POST``/api/videozer/optimize`Process a single file (`page`, `filename`, `preset`, `force`)`POST``/api/videozer/optimize-all`Batch process all videos (`preset`, `force`)---

Troubleshooting
---------------

[](#troubleshooting)

- Check `site/plugins/videozer/videozer.log` for processing errors.
- If FFmpeg is not found, set `rllngr.videozer.ffmpeg` to the full binary path.
- Uploads trigger `processBackground()` — FFmpeg runs in a detached shell so the Panel request returns immediately. The poster and srcset will appear once the background job completes.
- The API routes (`/api/videozer/optimize`, `/api/videozer/optimize-all`) use the synchronous `process()` method — suited for scripts and manual re-processing.

---

License
-------

[](#license)

MIT — [Nicolas Rollinger](https://rollinger.design) 🫰

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance89

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~12 days

Total

4

Last Release

56d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a423a955706f2f0254ab40c1bc7a17f9a88f8134a65d51a0036cd0e9ce0d6382?d=identicon)[RLLNGR](/maintainers/RLLNGR)

---

Top Contributors

[![RLLNGR](https://avatars.githubusercontent.com/u/34002361?v=4)](https://github.com/RLLNGR "RLLNGR (10 commits)")

### Embed Badge

![Health badge](/badges/rllngr-kirby-videozer/health.svg)

```
[![Health](https://phpackages.com/badges/rllngr-kirby-videozer/health.svg)](https://phpackages.com/packages/rllngr-kirby-videozer)
```

###  Alternatives

[getkirby/cms

The Kirby core

1.5k567.4k434](/packages/getkirby-cms)[distantnative/retour-for-kirby

Manage redirects and track 404s right from the Kirby CMS Panel

14695.1k1](/packages/distantnative-retour-for-kirby)[getkirby/starterkit

Kirby Starterkit

20313.1k](/packages/getkirby-starterkit)[getkirby/staticache

Static site performance on demand

10017.2k](/packages/getkirby-staticache)[getkirby/plainkit

Kirby Plainkit

11911.4k1](/packages/getkirby-plainkit)[belugadigital/kirby-navigation

Kirby 5 field for hierarchical menus with drag &amp; drop level indentation.

8613.8k](/packages/belugadigital-kirby-navigation)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
