wp-media / imagify-plugin

Speed up your website with lighter images without losing quality.
https://imagify.io
69 stars 24 forks source link

Add compatibility for Responsive Pics #787

Open tombroucke opened 4 months ago

tombroucke commented 4 months ago

Responsive pics compatibility

I am using https://github.com/clarifynl/responsive-pics in a lot of projects, but imagify is not optimizing the images generated by Responsive Pics. I did some digging, and I assume the best way to add compatibility, is to add the generated images in the imagify_media_files filter.

How Responsive Pics works

In my template, I fetch an image from the media library (let’s say with an ID of 5, 2024/01/filename.png). We can define the width and aspect ratio we want to generate:

ResponsivePics::get_image(5, 'md-6, xl-4', 1)

When the image is requested for the first time (first page view), an action is scheduled to generate the requested image sizes. When action scheduler has run the queue, the images are placed in the same upload directory (2024/01/filename-330x330-crop-50-50.png, 2024/01/filename-330x330-crop-50-50@2x.png etc.)

Working solution

Add generated images to imagify_media_files

add_filter('imagify_media_files', function ($files, $mediaObject) {
    $fileName = pathinfo($mediaObject->get_raw_original_path(), PATHINFO_FILENAME);
    $fileExtension = pathinfo($mediaObject->get_raw_original_path(), PATHINFO_EXTENSION);
    $filePath = pathinfo($mediaObject->get_raw_original_path(), PATHINFO_DIRNAME);

    $pattern = "{$filePath}/{$fileName}-*x*-crop-*-*.{$fileExtension}";
    $responsivePicsFiles = glob($pattern);

    foreach ($responsivePicsFiles as $file) {
        preg_match('/(\d+x\d+)(?=-crop)/', $file, $matches);
        $size = $matches[0];

        if ($size) {
            $key = $size . '_crop';
            if (strpos($file, '@2x') !== false) {
                $key .= '@2x';
            }
            $files[$key] = [
                'size' => $size,
                'path' => $file,
                'width' => (int)explode('x', $size)[0],
                'height' => (int)explode('x', $size)[1],
                'mime-type' => $mediaObject->get_mime_type(),
                'disabled' => false,
            ];
        }
    }
    return $files;
}, 10, 2);

Schedule optimize images

We need to make sure Responsive Pics has finished generating the images, so we add a 60 second delay

add_action('responsive_pics_request_processed', function ($postId) {
    $action = 'imagify_responsive_pics_optimize_image';
    $args = ['post_id' => $postId];

    if (!as_next_scheduled_action($action, $args)) {
        as_schedule_single_action(
            time() + 60,
            $action,
            $args
        );
    }
}, 10);

Optimize the images

As the image was already optimized, we need to change the status to make sure the optimization process runs

add_action('imagify_responsive_pics_optimize_image', function ($postId) {
    // Force Imagify to optimize the image
    $originalStatus = get_post_meta($postId, '_imagify_status', true);
    update_post_meta($postId, '_imagify_status', 'already_optimized');

    $optimized = imagify_get_optimization_process($postId, 'WP')->optimize();

    if ($optimized) {
        update_post_meta($postId, '_imagify_status', 'success');
    } else {
        update_post_meta($postId, '_imagify_status', $originalStatus);
    }
});

I’m sure this is not the best way to fix my problem, but it works. Could this be an integration that you would consider building in to the plugin itself? If not, could you give me some pointers on how to optimize the process? Can you find any potential problems in this code?