1

I want to be able to detect if a file at a certain path is a WordPress generated thumbnail. The only distinguishing feature of the WordPress thumbnail, as far as individual files are concerned, is that it would end with two or three numbers, 'x', another two or three numbers and then the file extension. The only way I can think of doing this would be to use a regex to literally detect that pattern in the file name. Is there a better way that I'm not thinking of? If not can someone help me with the regex?

2

You need to know your site's current thumbnail dimension settings in order for you to detect if the $url is of thumbnail size.

$thumbnail_width = get_option( 'thumbnail_size_w' );
$thumbnail_height = get_option( 'thumbnail_size_h' );

// The do detection
// Assuming you have the image $url

$pattern = '%' . $thumbnail_width . 'x' . $thumbnail_height . '%';

if ( preg_match( $pattern, $url ) ) {
    // do your stuff here ...
}
  • What if the image is uploaded at exactly the thumbnails width and height? Pretty sure it won't generate a URL like the pattern you're looking for. – Howdy_McGee Mar 18 '17 at 21:03
  • Ah yes, I think would be a step in the right direction for me. – heller_benjamin Mar 19 '17 at 0:35
  • Actually this won't work because if you have thumbnail of 300x300 and an image of 350 X 450 it will crop height to 300, but width will be smaller to maintain aspect ratio. – heller_benjamin Mar 23 '17 at 5:10
1

Here are another two approaches that might or might not be suitable for your case:

Approach #1

Let's take the 300x200 size for example:

https://example.tld/wp-content/2017/03/vatnajokull-300x200.jpg

then here's one way how we can check if it's really part of the media library:

  1. Strip out the -\d+x\d+ part from the filename so it becomes:

    $url = 'https://example.tld/wp-content/2017/03/vatnajokull.jpg';
    
  2. Use the core $attachment_id = attachment_url_to_postid( $url ) function to get the possible attachment ID.

  3. Use the attachment ID from part 2. to fetch the metadata with $meta = wp_get_attachment_metadata( $attahcment_id ) that contains all generated image sizes. It fetches the _wp_attachment_metadata meta value.
  4. Check if the 300x200 size has been generated by peeking into the sizes metadata array ( i.e. $meta['sizes'] ) and e.g. file_exists( $path ) to be sure it really exists.

Hope you can fill in the missing parts ;-)

Approach #2

The core uses a wildcard LIKE search on the _wp_attachment_metadata meta values, within wp_delete_attachment() (source), to see if other attachments use it as a thumb, before deleting it. This could work here but would be less accurate as it searches through serialized arrays. It looks like this in core:

if ( ! $wpdb->get_row( 
    $wpdb->prepare( 
        "SELECT meta_id FROM $wpdb->postmeta 
         WHERE meta_key = '_wp_attachment_metadata' 
             AND meta_value LIKE %s 
             AND post_id <> %d", 
        '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', 
        $post_id
    )
) ) {
0

Ok here's how I did it. First I checked if it had supported image file extensions because we can save a DB query if not. Then I simply checked the database for an attachment id. If there is no attachment ID then I'm assuming this is a thumbnail. Maybe not perfect for every solution but will work for my application. Thanks for the suggestions they got my mind flowing in the right direction.

function is_not_thumbnail( $url ) {
    $result = true;
    $ext = substr( $url, -4 );

    if ( 
        $ext === '.jpg' || 
        $ext ===  '.JPG' ||
        $ext === 'jpeg' ||
        $ext === 'JPEG' ||
        $ext === '.png' ||
        $ext === '.PNG' ||
        $ext === '.gif' ||
        $ext === '.GIF' ||
        $ext === '.ico' ||
        $ext === '.ICO' 
     ) {
        if ( get_attachment_id_from_url( $url ) === 'null' ) {
            $result = false;
        }
    }

    return $result;

}

function get_attachment_id_from_url( $url ) {
    global $wpdb;

    $result = $wpdb->get_results( 'SELECT ID FROM ' . $wpdb->prefix . 'posts WHERE guid = "' . $url . '"' );


    if ( !empty( $result[0] ) ) {
        return $result[0]->ID;  
    } else {
        return false;
    }

}
  • Prior to this a converted the Path to URL btw. – heller_benjamin Mar 23 '17 at 5:47
0

I cloned my media library and ran this node script against my nested Wordpress Media Files and looped through the files / folders. Put some logic inside the loop to rest the file name against a regular expression for the thumbnail naming convention filename-SIZExSIZE.ext. I used node file system and path to move the files around appropriately.

const glob = require("glob");
const fs = require('fs');
const { resolve, basename } = require('path');

glob("./../media/**/*.png", {}, function (er, files) {

  var count = 0;
  for (const file of files) {
    if(/\b\d{2,4}[x.]?\d{2,4}[..]([a-z\.]{3})\b/.test(file) === false){

      let oldFileNamePath = resolve(file);
      let newFileNamePath = resolve('./../media-new/'+ basename(file));

       fs.rename(oldFileNamePath, newFileNamePath, function (err) {
         if (err) throw err;
         console.log(count, 'renamed complete', newFileNamePath);
       });

       count++;
    }

  }

});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.