~コピペで導入できる完成形~
できること
- アップロード時に JPEG/PNG → WebP/AVIF 変換
- 品質調整(JPEG/WebP/AVIF個別に設定可)
- 最大サイズ以上は縮小(例:2560px以内)
- EXIFメタ情報削除(Imagick有効時のみ)
- 管理画面(設定 → NekoPixel)からGUIで数値変更
導入手順
wp-content/plugins/にneko-pixel-optimizerフォルダを作成- 中に
neko-pixel-optimizer.phpを置く - 下のコードをコピペして保存
- 管理画面 → プラグイン一覧 → 「NekoPixel Optimizer」を有効化
- 設定 → NekoPixel から数値を調整
完成コード(コピペOK)
<?php
/**
* Plugin Name: NekoPixel Optimizer
* Description: アップロード画像を自動最適化(WebP/AVIF変換・品質調整・縮小・EXIF削除)。GUI設定付き完成版。
* Version: 1.0.0
* Author: Kagioneko + Chappie
*/
if (!defined('ABSPATH')) exit;
class NekoPixel_Optimizer {
const OPT_KEY = 'nekopixel_options';
public function __construct() {
add_action('admin_init', [$this, 'register_settings']);
add_action('admin_menu', [$this, 'add_settings_page']);
add_filter('image_editor_output_format', [$this, 'map_output_format']);
add_filter('wp_editor_set_quality', [$this, 'set_quality'], 10, 2);
add_filter('wp_handle_upload', [$this, 'maybe_downscale_original']);
add_filter('image_make_intermediate_size', [$this, 'strip_exif_after_resize']);
}
/* ===== デフォルト値 ===== */
public static function defaults() {
return [
'jpeg_quality' => 82,
'webp_quality' => 75,
'avif_quality' => 60,
'max_width' => 2560,
'max_height' => 2560,
'strip_exif' => 1,
'prefer_avif' => 1,
];
}
private function opts() {
return wp_parse_args(get_option(self::OPT_KEY, []), self::defaults());
}
/* ===== 設定ページ ===== */
public function register_settings() {
register_setting('nekopixel', self::OPT_KEY, [
'type' => 'array',
'sanitize_callback' => [$this, 'sanitize'],
'default' => self::defaults(),
]);
add_settings_section('nekopixel_main', 'NekoPixel Optimizer 設定', '__return_false', 'nekopixel');
$fields = [
['jpeg_quality','JPEG 品質(1-100)','number'],
['webp_quality','WebP 品質(1-100)','number'],
['avif_quality','AVIF 品質(1-100)','number'],
['max_width','オリジナル最大幅(px, 0で無効)','number'],
['max_height','オリジナル最大高さ(px, 0で無効)','number'],
['strip_exif','EXIF削除(Imagick使用時のみ)','checkbox'],
['prefer_avif','AVIFを優先(対応環境のみ)','checkbox'],
];
foreach ($fields as [$key,$label,$type]) {
add_settings_field($key,$label,[$this,'render_field'],'nekopixel','nekopixel_main',['key'=>$key,'type'=>$type]);
}
}
public function sanitize($in) {
$d=self::defaults(); $out=[];
$out['jpeg_quality']=max(1,min(100,intval($in['jpeg_quality']??$d['jpeg_quality'])));
$out['webp_quality']=max(1,min(100,intval($in['webp_quality']??$d['webp_quality'])));
$out['avif_quality']=max(1,min(100,intval($in['avif_quality']??$d['avif_quality'])));
$out['max_width']=max(0,intval($in['max_width']??$d['max_width']));
$out['max_height']=max(0,intval($in['max_height']??$d['max_height']));
$out['strip_exif']=empty($in['strip_exif'])?0:1;
$out['prefer_avif']=empty($in['prefer_avif'])?0:1;
return $out;
}
public function render_field($args) {
$o=$this->opts(); $key=$args['key']; $type=$args['type']; $val=$o[$key];
if ($type==='checkbox') {
printf("<input type='checkbox' name='%s[%s]' value='1' %s />",
esc_attr(self::OPT_KEY),esc_attr($key),checked($val,1,false));
} else {
printf("<input type='%s' name='%s[%s]' value='%s' style='width:120px' />",
esc_attr($type),esc_attr(self::OPT_KEY),esc_attr($key),esc_attr($val));
}
}
public function add_settings_page() {
add_options_page('NekoPixel Optimizer','NekoPixel','manage_options','nekopixel',[$this,'render_settings_page']);
}
public function render_settings_page() {
echo "<div class='wrap'><h1>NekoPixel Optimizer</h1><form method='post' action='options.php'>";
settings_fields('nekopixel'); do_settings_sections('nekopixel'); submit_button();
echo "</form></div>";
}
/* ===== 実処理 ===== */
public function map_output_format($formats) {
$o=$this->opts(); $target=null;
if ($o['prefer_avif'] && function_exists('imageavif')) $target='image/avif';
elseif (function_exists('imagewebp')) $target='image/webp';
elseif (class_exists('Imagick')) {
$f=@Imagick::queryFormats();
if (is_array($f)&&in_array('AVIF',$f,true)) $target='image/avif';
elseif (is_array($f)&&in_array('WEBP',$f,true)) $target='image/webp';
}
if ($target) {
$formats['image/jpeg']=$target;
$formats['image/png']=$target;
}
return $formats;
}
public function set_quality($q,$mime) {
$o=$this->opts();
if ($mime==='image/avif') return $o['avif_quality'];
if ($mime==='image/webp') return $o['webp_quality'];
if ($mime==='image/jpeg') return $o['jpeg_quality'];
return $q;
}
public function maybe_downscale_original($upload) {
$o=$this->opts();
if (empty($o['max_width'])&&empty($o['max_height'])) return $upload;
if (empty($upload['file'])||!file_exists($upload['file'])) return $upload;
$type=wp_check_filetype($upload['file']);
if (!in_array($type['type'],['image/jpeg','image/png','image/webp','image/avif'],true)) return $upload;
$editor=wp_get_image_editor($upload['file']);
if (is_wp_error($editor)) return $upload;
$size=$editor->get_size();
$w=intval($size['width']??0); $h=intval($size['height']??0);
$mw=intval($o['max_width']); $mh=intval($o['max_height']);
$need=($mw && $w>$mw)||($mh && $h>$mh);
if ($need) {
$editor->resize($mw?:null,$mh?:null,false);
$editor->set_quality($this->pick_quality($type['type'],$o));
$editor->save($upload['file']);
}
return $upload;
}
private function pick_quality($mime,$o) {
if ($mime==='image/jpeg') return $o['jpeg_quality'];
if ($mime==='image/webp') return $o['webp_quality'];
if ($mime==='image/avif') return $o['avif_quality'];
return 82;
}
public function strip_exif_after_resize($resized_path) {
$o=$this->opts();
if (!$resized_path||!file_exists($resized_path)) return $resized_path;
if (empty($o['strip_exif'])||!class_exists('Imagick')) return $resized_path;
try {
$img=new Imagick($resized_path);
$img->stripImage(); $img->writeImage($resized_path);
$img->clear(); $img->destroy();
} catch(\Throwable $e){ error_log('[NekoPixel] strip failed: '.$e->getMessage()); }
return $resized_path;
}
}
new NekoPixel_Optimizer();
動作確認
- 設定 → NekoPixel で品質や最大サイズを調整
- 3000×3000px以上の画像をアップ → 2560px以内に縮小される
- サムネイルが WebP/AVIF で生成される
- EXIF情報が削除される(Imagick有効な場合)
まとめ
- 新規アップロードの画像を自動で最適化
- ShortPixel風の動作を、完全自作&無料で実現
- 設定GUIで簡単にカスタマイズ可能
/

コメント