File method not work in Panel

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:

<?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?

I checked this same code in Kirby 4, and it still doesn’t work

This is how it worked:

image:
    src: "{{ file.videoposter.url }}"

But why did the query property stop working in versions 4 and 5?

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.

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