Stephan
November 14, 2025, 1:50pm
1
Hello, dear Kirby developers. I’ve encountered a problem where the file method in the control panel isn’t working, as it used to in Kirby 3.
This method worked perfectly for Kirby 3:
If you auto-generate such a poster image from the video, you can give it the same name as the video file. Then you can solve this with a file method
Example files section:
test:
type: files
layout: cards
template: video
image:
query: file.videoposter
cover: true
File method defined in plugin:
<?php
Kirby::plugin('texnixe/methods', [
'fileMethods' => [
'videoposter' => function() {
$filename = $this->name() . '.jpg';
if($poster = $this->par…
<?php
Kirby::plugin('texnixe/methods', [
'fileMethods' => [
'videoposter' => function() {
$filename = $this->name() . '.jpg';
if($poster = $this->parent()->image($filename)) {
return $poster;
} else {
return null;
}
}
]
]);
Unfortunately, in version 5, I encountered a problem where the video preview image isn’t displaying. To check what the method returns, I tried displaying the data in the info property like this:
sections:
files:
headline: Video
type: files
layout: cards
size: medium
template: video
info: "{{ file.videoposter }}<b>{{ file.language.upper }}</b>…"
image:
query: file.videoposter
As a result, I see (for some reason) the img tag in the info property, but the image isn’t displaying as expected.
On the frontend in template, I use the following code:
$f->videoposter()->resize(800, 800)->url()
and everything works fine.
Can you tell me what I’m doing wrong? Am I missing something?
Stephan
November 15, 2025, 1:15pm
2
I checked this same code in Kirby 4, and it still doesn’t work
Stephan
November 16, 2025, 12:40pm
3
This is how it worked:
image:
src: "{{ file.videoposter.url }}"
But why did the query property stop working in versions 4 and 5?
texnixe
November 16, 2025, 1:55pm
4
This is weird, I did a quick test and cannot reproduce your issue. However, I’m missing your video blueprint. And where do you upload your poster files? Please post all relevant blueprints.
Stephan
November 18, 2025, 11:24am
5
Hello texnixe!
Hi texnixe
Here’s a plugin with a video poster method that also generates a preview for a video file. The file isn’t downloaded, but rather generated when a video is uploaded with the name videofilename.jpg:
<?php
Kirby::plugin('test/file-methods', [
'fileMethods' => [
'videoposter' => function() {
$filename = $this->name() . '.jpg';
if($poster = $this->parent()->image($filename)) {
return $poster;
} else {
return null;
}
}
],
'hooks' => [
'file.create:after' => function ($file) {
if($file->type() == 'video') {
try {
$out = str_replace($file->extension(), 'jpg', $file->root());
$duration = get_diveo_duration($file->root());
create_preview($file->root(), $out, intdiv($duration, 2));
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}
],
]);
function create_preview($video_file, $output_filename, $second) {
$return = null;
$retval = null;
$time = gmdate("H:i:s", $second);
$isWindows = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN';
$video_file = str_replace('\\', '/', $video_file);
$output_filename = str_replace('\\', '/', $output_filename);
$video_file_esc = escapeshellarg($video_file);
$output_esc = escapeshellarg($output_filename);
$nullOutput = $isWindows ? ' > NUL 2>&1' : ' > /dev/null 2>&1';
$cmd = "ffmpeg -i $video_file_esc -ss {$time}.000 -vframes 1 $output_esc$nullOutput";
exec($cmd, $return, $retval);
return $retval === 0;
}
function get_diveo_duration($video_file){
$return = null;
$retval = null;
exec("ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 \"{$video_file}\"", $return, $retval);
return (int)$return[0];
}
And this is a Blueprint file for the parent page of the video files:
title: Folder (video)
icon: video
status:
draft: true
listed: true
tabs:
main:
label: Files and folders
icon: file-video
columns:
- width: 1/1
sections:
folders:
headline: Folders
type: pages
layout: cards
size: medium
empty: Empty
template:
- videoalbum
- album
- files
image:
ratio: 16/9
- width: 1/1
sections:
files:
headline: Video files
type: files
layout: cards
size: medium
limit: 100
sortBy: modified DESC
template: video
info: "{{ file.modified('Y-m-d H:i:s') }}<br/>{{ file.niceSize }}"
image:
query: file.videoposter # It doesn't work.
src: "{{ file.videoposter.url }}" # It works
ratio: 16/9
cover: true
settings:
label: Settings
icon: settings
columns:
- width: 3/4
fields:
fields:
meta: fields/folder.meta
mixed_content:
label: Mixed content
type: toggle
text:
- "No"
- "Yes"
- width: 1/4
sections:
covers:
headline: Folder covers
type: files
layout: list
template: cover
info: "{{ file.modified('Y-m-d H:i:s') }} / {{ file.niceSize }}"
image:
ratio: 16/9
cover: true
size: small
translate: false