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?
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?
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 ...
}
Here are another two approaches that might or might not be suitable for your case:
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:
Strip out the -\d+x\d+
part from the filename so it becomes:
$url = 'https://example.tld/wp-content/2017/03/vatnajokull.jpg';
Use the core $attachment_id = attachment_url_to_postid( $url )
function to get the possible attachment ID.
$meta = wp_get_attachment_metadata( $attahcment_id )
that contains all generated image sizes. It fetches the _wp_attachment_metadata
meta value.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 ;-)
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
)
) ) {
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;
}
}
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++;
}
}
});