görselleri webp. çeviren bir kaç eklenti denedim ama çoğunu çevirmedi yada yarım kaldı. ücretsiz olarak kullanabileceğim eklenti varmı
<?php
/**
* Plugin Name: WD WebP Dönüştürücü
* Plugin URI: https://webdanismani.com
* Description: WordPress & WooCommerce'e yüklenen tüm JPEG/PNG görselleri otomatik olarak WebP formatına dönüştürür. Mevcut medya kütüphanesi için toplu dönüştürme aracı içerir.
* Version: 1.0.0
* Author: Web Danışmanı
* Author URI: https://webdanismani.com
* License: GPL-2.0-or-later
* Text Domain: wd-webp
* Requires at least: 5.8
* Requires PHP: 7.4
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class WD_WebP_Converter {
const OPTION = 'wd_webp_settings';
private static $instance = null;
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Yükleme anında dönüştürme (medya kütüphanesi, WooCommerce, REST, sideload — hepsi bu iki filtreden geçer)
add_filter( 'wp_handle_upload', array( $this, 'convert_on_upload' ) );
add_filter( 'wp_handle_sideload', array( $this, 'convert_on_upload' ) );
// Yönetim
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'wp_ajax_wd_webp_bulk', array( $this, 'ajax_bulk' ) );
add_filter( 'plugin_action_links_' . plugin_basename( __FILE__ ), array( $this, 'action_links' ) );
register_uninstall_hook( __FILE__, array( 'WD_WebP_Converter', 'uninstall' ) );
}
public static function uninstall() {
delete_option( self::OPTION );
delete_post_meta_by_key( '_wd_webp_failed' );
}
/* ------------------------------------------------------------------
* Ayarlar
* ---------------------------------------------------------------- */
public function settings() {
$defaults = array(
'enabled' => 1,
'quality' => 82,
'convert_jpeg' => 1,
'convert_png' => 1,
'keep_original' => 0,
);
return wp_parse_args( (array) get_option( self::OPTION, array() ), $defaults );
}
public function register_settings() {
register_setting(
'wd_webp',
self::OPTION,
array( 'sanitize_callback' => array( $this, 'sanitize_settings' ) )
);
}
public function sanitize_settings( $in ) {
$in = (array) $in;
return array(
'enabled' => empty( $in['enabled'] ) ? 0 : 1,
'quality' => max( 1, min( 100, (int) ( isset( $in['quality'] ) ? $in['quality'] : 82 ) ) ),
'convert_jpeg' => empty( $in['convert_jpeg'] ) ? 0 : 1,
'convert_png' => empty( $in['convert_png'] ) ? 0 : 1,
'keep_original' => empty( $in['keep_original'] ) ? 0 : 1,
);
}
public function action_links( $links ) {
array_unshift(
$links,
'<a href="' . esc_url( admin_url( 'options-general.php?page=wd-webp' ) ) . '">Ayarlar</a>'
);
return $links;
}
/* ------------------------------------------------------------------
* Sunucu yetenekleri
* ---------------------------------------------------------------- */
private function imagick_webp() {
if ( ! class_exists( 'Imagick' ) ) {
return false;
}
try {
return count( Imagick::queryFormats( 'WEBP' ) ) > 0;
} catch ( Exception $e ) {
return false;
}
}
private function gd_webp() {
if ( ! function_exists( 'imagewebp' ) || ! function_exists( 'gd_info' ) ) {
return false;
}
$gd = gd_info();
return ! empty( $gd['WebP Support'] );
}
private function webp_supported() {
return $this->imagick_webp() || $this->gd_webp();
}
/* ------------------------------------------------------------------
* Yükleme anında dönüştürme
* ---------------------------------------------------------------- */
public function convert_on_upload( $upload ) {
$s = $this->settings();
if ( empty( $s['enabled'] ) ) {
return $upload;
}
if ( ! is_array( $upload ) || empty( $upload['file'] ) || empty( $upload['type'] ) || ! empty( $upload['error'] ) ) {
return $upload;
}
$allowed = array();
if ( ! empty( $s['convert_jpeg'] ) ) {
$allowed[] = 'image/jpeg';
}
if ( ! empty( $s['convert_png'] ) ) {
$allowed[] = 'image/png';
}
if ( ! in_array( $upload['type'], $allowed, true ) ) {
return $upload;
}
// Sunucu WebP üretemiyorsa veya WP küçük boyutları WebP'den oluşturamayacaksa dokunma
if ( ! $this->webp_supported() || ! wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
return $upload;
}
$file = wp_normalize_path( $upload['file'] );
$webp = $this->unique_webp_path( $file );
if ( ! $this->convert_image( $file, $webp, (int) $s['quality'] ) ) {
return $upload; // dönüştürülemezse orijinal akış devam eder
}
// Nadiren WebP daha büyük çıkabilir; o zaman vazgeç
if ( filesize( $webp ) >= filesize( $file ) ) {
@unlink( $webp );
return $upload;
}
if ( empty( $s['keep_original'] ) ) {
@unlink( $file );
}
$upload['url'] = str_replace( wp_basename( $upload['file'] ), wp_basename( $webp ), $upload['url'] );
$upload['file'] = $webp;
$upload['type'] = 'image/webp';
return $upload;
}
/* ------------------------------------------------------------------
* Çekirdek dönüştürücü (Imagick öncelikli, GD yedek)
* ---------------------------------------------------------------- */
private function convert_image( $source, $dest, $quality ) {
$quality = max( 1, min( 100, (int) $quality ) );
$info = @getimagesize( $source );
if ( ! $info || empty( $info['mime'] ) ) {
return false;
}
$mime = $info['mime'];
if ( ! in_array( $mime, array( 'image/jpeg', 'image/png' ), true ) ) {
return false;
}
// 1) Imagick
if ( $this->imagick_webp() ) {
try {
$im = new Imagick( $source );
// EXIF yönünü piksellere uygula (telefon fotoğrafları yan dönmesin)
switch ( $im->getImageOrientation() ) {
case Imagick::ORIENTATION_BOTTOMRIGHT:
$im->rotateImage( '#000', 180 );
break;
case Imagick::ORIENTATION_RIGHTTOP:
$im->rotateImage( '#000', 90 );
break;
case Imagick::ORIENTATION_LEFTBOTTOM:
$im->rotateImage( '#000', -90 );
break;
}
$im->setImageOrientation( Imagick::ORIENTATION_TOPLEFT );
// Renk profili varsa sRGB'ye çevirip metadata'yı temizle (dosya küçülür, renk kaymaz)
if ( method_exists( $im, 'transformImageColorspace' ) && Imagick::COLORSPACE_SRGB !== $im->getImageColorspace() ) {
$im->transformImageColorspace( Imagick::COLORSPACE_SRGB );
}
$im->stripImage();
$im->setImageFormat( 'webp' );
$im->setImageCompressionQuality( $quality );
$ok = $im->writeImage( $dest );
$im->clear();
$im->destroy();
if ( $ok && file_exists( $dest ) && filesize( $dest ) > 0 ) {
return true;
}
} catch ( Exception $e ) {
// GD'ye düş
}
}
// 2) GD
if ( $this->gd_webp() ) {
$img = false;
if ( 'image/jpeg' === $mime ) {
$img = @imagecreatefromjpeg( $source );
if ( $img && function_exists( 'exif_read_data' ) ) {
$exif = @exif_read_data( $source );
if ( ! empty( $exif['Orientation'] ) ) {
$deg = 0;
switch ( (int) $exif['Orientation'] ) {
case 3:
$deg = 180;
break;
case 6:
$deg = -90;
break;
case 8:
$deg = 90;
break;
}
if ( $deg ) {
$rot = imagerotate( $img, $deg, 0 );
if ( $rot ) {
imagedestroy( $img );
$img = $rot;
}
}
}
}
} elseif ( 'image/png' === $mime ) {
$img = @imagecreatefrompng( $source );
if ( $img ) {
imagepalettetotruecolor( $img ); // palet PNG'ler imagewebp'te patlar
imagealphablending( $img, false );
imagesavealpha( $img, true ); // şeffaflık korunur
}
}
if ( ! $img ) {
return false;
}
$ok = imagewebp( $img, $dest, $quality );
imagedestroy( $img );
return $ok && file_exists( $dest ) && filesize( $dest ) > 0;
}
return false;
}
private function unique_webp_path( $file ) {
$dir = dirname( $file );
$name = preg_replace( '/\.[^.\/\\\\]+$/', '', wp_basename( $file ) ) . '.webp';
if ( file_exists( trailingslashit( $dir ) . $name ) ) {
$name = wp_unique_filename( $dir, $name );
}
return trailingslashit( $dir ) . $name;
}
/* ------------------------------------------------------------------
* Toplu dönüştürme (mevcut medya kütüphanesi)
* ---------------------------------------------------------------- */
private function count_remaining() {
$q = new WP_Query(
array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_mime_type' => array( 'image/jpeg', 'image/png' ),
'posts_per_page' => 1,
'fields' => 'ids',
'meta_query' => array(
array(
'key' => '_wd_webp_failed',
'compare' => 'NOT EXISTS',
),
),
)
);
return (int) $q->found_posts;
}
public function ajax_bulk() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( array( 'message' => 'Yetkiniz yok.' ), 403 );
}
check_ajax_referer( 'wd_webp_bulk' );
$mode = isset( $_POST['mode'] ) ? sanitize_key( $_POST['mode'] ) : 'status';
if ( 'reset_failed' === $mode ) {
delete_post_meta_by_key( '_wd_webp_failed' );
wp_send_json_success( array( 'message' => 'Başarısız işaretleri temizlendi, tekrar denenebilir.' ) );
}
if ( 'status' === $mode ) {
wp_send_json_success(
array(
'remaining' => $this->count_remaining(),
'webp' => $this->webp_supported(),
)
);
}
// mode === run
if ( ! $this->webp_supported() ) {
wp_send_json_error( array( 'message' => 'Sunucuda WebP desteği yok (GD webp veya Imagick gerekli).' ) );
}
@set_time_limit( 120 );
if ( function_exists( 'wp_raise_memory_limit' ) ) {
wp_raise_memory_limit( 'image' );
}
$s = $this->settings();
$ids = get_posts(
array(
'post_type' => 'attachment',
'post_status' => 'inherit',
'post_mime_type' => array( 'image/jpeg', 'image/png' ),
'posts_per_page' => 4,
'orderby' => 'ID',
'order' => 'ASC',
'fields' => 'ids',
'meta_query' => array(
array(
'key' => '_wd_webp_failed',
'compare' => 'NOT EXISTS',
),
),
)
);
$log = array();
$done = 0;
$failed = 0;
foreach ( $ids as $id ) {
$result = $this->convert_attachment( (int) $id, (int) $s['quality'], ! empty( $s['keep_original'] ) );
if ( is_wp_error( $result ) ) {
update_post_meta( $id, '_wd_webp_failed', $result->get_error_message() );
$failed++;
$log[] = sprintf( '#%d HATA: %s', $id, $result->get_error_message() );
} else {
$done++;
$log[] = sprintf( '#%d dönüştürüldü (%d dosya).', $id, $result );
}
}
wp_send_json_success(
array(
'processed' => count( $ids ),
'done' => $done,
'failed' => $failed,
'remaining' => $this->count_remaining(),
'log' => $log,
)
);
}
/**
* Bir eki tüm boyutlarıyla WebP'ye çevirir, metadata + içerik URL'lerini günceller.
*
* @return int|WP_Error Dönüştürülen dosya sayısı ya da hata.
*/
private function convert_attachment( $id, $quality, $keep_original ) {
$file = get_attached_file( $id );
if ( ! $file || ! file_exists( $file ) ) {
return new WP_Error( 'missing_file', 'Dosya diskte bulunamadı' );
}
$file = wp_normalize_path( $file );
$mime = get_post_mime_type( $id );
if ( ! in_array( $mime, array( 'image/jpeg', 'image/png' ), true ) ) {
return new WP_Error( 'bad_mime', 'Desteklenmeyen tür: ' . $mime );
}
$meta = wp_get_attachment_metadata( $id );
if ( ! is_array( $meta ) ) {
$meta = array();
}
$dir = trailingslashit( dirname( $file ) );
$uploads = wp_get_upload_dir();
$basedir = wp_normalize_path( $uploads['basedir'] );
$baseurl = $uploads['baseurl'];
$map = array(); // eski disk yolu => yeni disk yolu
// 1) Ana dosya (büyük görsel ölçeklendiyse bu "-scaled" dosyasıdır)
$new_main = $this->unique_webp_path( $file );
if ( ! $this->convert_image( $file, $new_main, $quality ) ) {
return new WP_Error( 'convert_failed', 'Ana dosya dönüştürülemedi' );
}
$map[ $file ] = $new_main;
// 2) Ölçeklenmemiş orijinal (varsa)
if ( ! empty( $meta['original_image'] ) ) {
$orig = $dir . $meta['original_image'];
if ( file_exists( $orig ) && ! isset( $map[ $orig ] ) ) {
$new_orig = $this->unique_webp_path( $orig );
if ( $this->convert_image( $orig, $new_orig, $quality ) ) {
$map[ $orig ] = $new_orig;
$meta['original_image'] = wp_basename( $new_orig );
}
}
}
// 3) Ara boyutlar (thumbnail, medium, woocommerce_thumbnail vb.)
if ( ! empty( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
foreach ( $meta['sizes'] as $size => $data ) {
if ( empty( $data['file'] ) ) {
continue;
}
$p = $dir . $data['file'];
// Aynı dosyayı paylaşan boyutlar tekrar dönüştürülmesin
if ( isset( $map[ $p ] ) ) {
$meta['sizes'][ $size ]['file'] = wp_basename( $map[ $p ] );
$meta['sizes'][ $size ]['mime-type'] = 'image/webp';
continue;
}
if ( ! file_exists( $p ) ) {
continue;
}
$np = $this->unique_webp_path( $p );
if ( $this->convert_image( $p, $np, $quality ) ) {
$map[ $p ] = $np;
$meta['sizes'][ $size ]['file'] = wp_basename( $np );
$meta['sizes'][ $size ]['mime-type'] = 'image/webp';
if ( isset( $meta['sizes'][ $size ]['filesize'] ) ) {
$meta['sizes'][ $size ]['filesize'] = (int) filesize( $np );
}
}
}
}
// 4) Ek kaydını güncelle
update_attached_file( $id, $new_main );
$meta['file'] = ltrim( str_replace( $basedir, '', $new_main ), '/' );
if ( isset( $meta['filesize'] ) ) {
$meta['filesize'] = (int) filesize( $new_main );
}
wp_update_attachment_metadata( $id, $meta );
$new_main_url = str_replace( $basedir, $baseurl, $new_main );
wp_update_post(
array(
'ID' => $id,
'post_mime_type' => 'image/webp',
'guid' => $new_main_url,
)
);
// 5) Yazı/ürün içeriklerindeki eski URL'leri değiştir + eski dosyaları sil
global $wpdb;
foreach ( $map as $old_path => $new_path ) {
$old_url = str_replace( $basedir, $baseurl, $old_path );
$new_url = str_replace( $basedir, $baseurl, $new_path );
if ( $old_url !== $new_url ) {
$wpdb->query(
$wpdb->prepare(
"UPDATE {$wpdb->posts} SET post_content = REPLACE(post_content, %s, %s)",
$old_url,
$new_url
)
);
}
if ( ! $keep_original ) {
@unlink( $old_path );
}
}
clean_post_cache( $id );
return count( $map );
}
/* ------------------------------------------------------------------
* Yönetim sayfası
* ---------------------------------------------------------------- */
public function admin_menu() {
add_options_page(
'WebP Dönüştürücü',
'WebP Dönüştürücü',
'manage_options',
'wd-webp',
array( $this, 'render_page' )
);
}
public function render_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$s = $this->settings();
$imagick_ok = $this->imagick_webp();
$gd_ok = $this->gd_webp();
$remaining = $this->count_remaining();
?>
<div class="wrap">
<h1>WD WebP Dönüştürücü</h1>
<p>
Sunucu desteği:
<strong style="color:<?php echo $imagick_ok ? '#00a32a' : '#d63638'; ?>">Imagick <?php echo $imagick_ok ? '✓' : '✗'; ?></strong> |
<strong style="color:<?php echo $gd_ok ? '#00a32a' : '#d63638'; ?>">GD WebP <?php echo $gd_ok ? '✓' : '✗'; ?></strong>
</p>
<?php if ( ! $imagick_ok && ! $gd_ok ) : ?>
<div class="notice notice-error"><p><strong>Sunucunuzda WebP desteği yok.</strong> Hosting sağlayıcınızdan GD'nin WebP desteğiyle derlenmesini veya Imagick kurulmasını isteyin. Eklenti bu durumda hiçbir şey yapmaz.</p></div>
<?php endif; ?>
<form method="post" action="options.php">
<?php settings_fields( 'wd_webp' ); ?>
<table class="form-table" role="presentation">
<tr>
<th scope="row">Otomatik dönüştürme</th>
<td>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[enabled]" value="1" <?php checked( $s['enabled'], 1 ); ?>>
Yüklenen görselleri otomatik olarak WebP'ye dönüştür
</label>
</td>
</tr>
<tr>
<th scope="row">Dönüştürülecek türler</th>
<td>
<label><input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[convert_jpeg]" value="1" <?php checked( $s['convert_jpeg'], 1 ); ?>> JPEG</label><br>
<label><input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[convert_png]" value="1" <?php checked( $s['convert_png'], 1 ); ?>> PNG (şeffaflık korunur)</label>
<p class="description">GIF ve SVG dosyalarına dokunulmaz.</p>
</td>
</tr>
<tr>
<th scope="row"><label for="wd-webp-quality">Kalite</label></th>
<td>
<input type="number" id="wd-webp-quality" name="<?php echo esc_attr( self::OPTION ); ?>[quality]" value="<?php echo esc_attr( $s['quality'] ); ?>" min="1" max="100" step="1" class="small-text"> / 100
<p class="description">Önerilen: 75–85. Değer düştükçe dosya küçülür, kalite azalır.</p>
</td>
</tr>
<tr>
<th scope="row">Orijinal dosya</th>
<td>
<label>
<input type="checkbox" name="<?php echo esc_attr( self::OPTION ); ?>[keep_original]" value="1" <?php checked( $s['keep_original'], 1 ); ?>>
Orijinal JPEG/PNG dosyasını diskte tut (yedek olarak; medya kütüphanesinde görünmez)
</label>
</td>
</tr>
</table>
<?php submit_button( 'Ayarları Kaydet' ); ?>
</form>
<hr>
<h2>Mevcut Görselleri Toplu Dönüştür</h2>
<p>Medya kütüphanesindeki dönüştürülebilir görsel sayısı: <strong id="wd-webp-remaining"><?php echo esc_html( $remaining ); ?></strong></p>
<p class="description"><strong>Uyarı:</strong> Başlamadan önce mutlaka dosya + veritabanı yedeği alın. Tüm boyutlar (küçük görseller dahil) dönüştürülür, ek kayıtları ve yazı/ürün içeriklerindeki görsel adresleri güncellenir, eski dosyalar (ayara göre) silinir.</p>
<p>
<button type="button" class="button button-primary" id="wd-webp-start">Toplu Dönüştürmeyi Başlat</button>
<button type="button" class="button" id="wd-webp-stop" disabled>Durdur</button>
<button type="button" class="button" id="wd-webp-reset">Başarısızları Sıfırla</button>
</p>
<div style="max-width:640px;background:#dcdcde;border-radius:3px;height:22px;overflow:hidden;margin-bottom:6px;">
<div id="wd-webp-bar" style="height:100%;width:0;background:#2271b1;transition:width .3s;"></div>
</div>
<p id="wd-webp-pct" style="margin-top:0;"></p>
<pre id="wd-webp-log" style="max-width:640px;height:220px;overflow:auto;background:#1d2327;color:#9ec2e6;padding:10px;font-size:12px;"></pre>
</div>
<script>
(function () {
var nonce = '<?php echo esc_js( wp_create_nonce( 'wd_webp_bulk' ) ); ?>';
var running = false;
var total = 0;
var done = 0;
function el( id ) { return document.getElementById( id ); }
function post( mode ) {
var body = new URLSearchParams( { action: 'wd_webp_bulk', mode: mode, _wpnonce: nonce } );
return fetch( ajaxurl, { method: 'POST', credentials: 'same-origin', body: body } )
.then( function ( r ) { return r.json(); } );
}
function log( lines ) {
var box = el( 'wd-webp-log' );
( lines || [] ).forEach( function ( l ) { box.textContent += l + '\n'; } );
box.scrollTop = box.scrollHeight;
}
function setProgress() {
var pct = total > 0 ? Math.round( ( done / total ) * 100 ) : 100;
el( 'wd-webp-bar' ).style.width = pct + '%';
el( 'wd-webp-pct' ).textContent = done + ' / ' + total + ' (%' + pct + ')';
}
function toggle() {
el( 'wd-webp-start' ).disabled = running;
el( 'wd-webp-stop' ).disabled = ! running;
}
function step() {
if ( ! running ) { return; }
post( 'run' ).then( function ( res ) {
if ( ! res || ! res.success ) {
log( [ 'HATA: ' + ( res && res.data && res.data.message ? res.data.message : 'İstek başarısız oldu.' ) ] );
running = false; toggle(); return;
}
done += res.data.processed;
log( res.data.log );
setProgress();
el( 'wd-webp-remaining' ).textContent = res.data.remaining;
if ( res.data.remaining > 0 && res.data.processed > 0 ) {
step();
} else {
log( [ '--- Tamamlandı ---' ] );
running = false; toggle();
}
} ).catch( function ( e ) {
log( [ 'HATA: ' + e ] );
running = false; toggle();
} );
}
el( 'wd-webp-start' ).addEventListener( 'click', function () {
post( 'status' ).then( function ( res ) {
if ( ! res || ! res.success ) { return; }
if ( ! res.data.webp ) { log( [ 'Sunucuda WebP desteği yok.' ] ); return; }
total = res.data.remaining;
done = 0;
el( 'wd-webp-log' ).textContent = '';
if ( total < 1 ) { log( [ 'Dönüştürülecek görsel yok.' ] ); return; }
running = true;
toggle();
setProgress();
step();
} );
} );
el( 'wd-webp-stop' ).addEventListener( 'click', function () {
running = false;
toggle();
log( [ 'Durduruldu. Kaldığı yerden tekrar başlatabilirsiniz.' ] );
} );
el( 'wd-webp-reset' ).addEventListener( 'click', function () {
post( 'reset_failed' ).then( function ( res ) {
log( [ res && res.success ? res.data.message : 'Sıfırlama başarısız.' ] );
post( 'status' ).then( function ( r2 ) {
if ( r2 && r2.success ) { el( 'wd-webp-remaining' ).textContent = r2.data.remaining; }
} );
} );
} );
})();
</script>
<?php
}
}
WD_WebP_Converter::instance();Ücretsiz kullanabilirsiniz.