宝塔面板下让 video-thumb.php 跑通 ffmpeg
把 maer.win 的随拍页面搬到 ai.seerking.com 时,视频帖子封面显示不出来。
查下来一环套一环,记一下。
现象
maer.win 的随拍分类里 49 张图 + 3 个视频。视频帖正文里只有 ,没有 poster 属性。
maer.win 上能正常显示视频封面(ffmpeg 提真实帧)。ai.seerking.com 上同样的三个视频帖,封面是空的。
第一处:变量没定义
最开始以为问题在视频封面生成。看了 page-gallery.php:
if ($isVideoOnly && !empty($firstVideo)) {
$thumbSrc = '/video-thumb.php?src=' . urlencode($firstVideo) . '&w=400';
}
$firstVideo 这个变量从来没被定义过,前面视频数组存的是 $vm[1]。
所以 $thumbSrc 永远是空, 不输出,播放按钮孤零零飘在卡片里。
补上:
preg_match_all('/第二处:exec 被禁
修了第一处,刷新还是没图。点开浏览器 devtools 看 video-thumb.php 的响应:
Fatal error: Uncaught Error: Call to undefined function exec()
宝塔默认的 php.ini 把 exec / shell_exec / passthru / popen / proc_open / system 全禁了。
这个安全配置在 PHP-FPM 进程下生效,命令行 php 不受影响。
我写了个 video-thumb.php 三层降级,这样即使没解封也能跑:
// ── 模式A: ffmpeg (需 exec + ffmpeg 都可用) ──
$hasExec = function_exists('exec') && function_exists('shell_exec')
&& function_exists('escapeshellarg') && function_exists('escapeshellcmd');
$hasFfmpeg = false;
if ($hasExec) {
$which = @shell_exec('command -v ffmpeg 2>/dev/null');
$hasFfmpeg = !empty(trim((string)$which));
}
if ($hasExec && $hasFfmpeg) {
$tmpFile = $cacheDir . $hash . '_tmp.jpg';
$cmd = sprintf(
'%s -ss 0.5 -i %s -vframes 1 -q:v 2 -vf scale=%d:-1 -y %s 2>&1',
escapeshellcmd('ffmpeg'), escapeshellarg($src), $width, escapeshellarg($tmpFile)
);
@exec($cmd, $output, $rc);
if ($rc === 0 && file_exists($tmpFile) && filesize($tmpFile) > 500) {
@rename($tmpFile, $cacheFile);
header('Content-Type: image/jpeg');
readfile($cacheFile);
exit;
}
@unlink($tmpFile);
}
// ── 模式B: GD 渐变封面 ──
if (function_exists('imagecreatetruecolor') && function_exists('imagejpeg')) {
$height = max(80, round($width * 9 / 16));
$img = @imagecreatetruecolor($width, $height);
if ($img) {
for ($y = 0; $y < $height; $y++) {
$ratio = $y / max(1, $height);
$r = round(20 + $ratio * 10);
$g = round(25 + $ratio * 5);
$b = round(50 + $ratio * 30);
$color = imagecolorallocate($img, $r, $g, $b);
imageline($img, 0, $y, $width, $y, $color);
}
// 中心白色三角 + 圆形光晕 + 底栏暗条
// (省略... 跟 maer.win 一样)
imagejpeg($img, $cacheFile, 85);
imagedestroy($img);
header('Content-Type: image/jpeg');
readfile($cacheFile);
exit;
}
}
// ── 模式C: 兜底 SVG ──
header('Content-Type: image/svg+xml');
echo '';
降到 GD 模式以后,封面确实出来了——但是一眼假。深蓝渐变 + 白色三角,明显是占位图。
跟 maer.win 的真实视频帧对比差太多,还是得解封 exec 装 ffmpeg。
第三处:装 ffmpeg
apt-get install -y ffmpeg
这一步炸出第二个坑:
dpkg: error processing package nginx-common (--configure):
*** nginx (Y/I/N/O/D/Z) [default=N] ?
宝塔的 nginx 用的是 /www/server/nginx/sbin/nginx,但 Ubuntu 的 apt-get install ffmpeg
把 nginx-common 也列为依赖(间接依赖链:ffmpeg → libavcodec-extra → libnginx-mod-*)。
nginx-common 配置时会弹 conffile 提示保持还是替换当前版本,默认 N 然后 dpkg 报错。
加上 --force-confdef --force-confnew 让 apt 自动选:
DEBIAN_FRONTEND=noninteractive \
apt-get -o Dpkg::Options::="--force-confdef" \
-o Dpkg::Options::="--force-confnew" \
install -y ffmpeg
装完 Ubuntu 自带的 nginx.service 会注册但不会启动(80 端口被宝塔占着)。
为了以后不被 apt upgrade 拉起来抢端口,hold 住:
apt-mark hold nginx nginx-common nginx-core第四处:PHP 版本
解封 exec 后第一波没生效:
$ grep '^disable_functions' /www/server/php/80/etc/php.ini
disable_functions = system,putenv,chroot,...
改了,但刷新页面 video-thumb.php 还在走 GD 模式(5KB 渐变,不是 ffmpeg 出的 38KB 真实帧)。
宝塔默认装了两套 PHP(80 和 82),页面跑的是 8.2 不是 8.0:
$ pgrep -a php-fpm
179957 php-fpm: master process (/www/server/php/82/etc/php-fpm.conf)
重新改 82,重启:
cp /www/server/php/82/etc/php.ini /www/server/php/82/etc/php.ini.bak.$(date +%Y%m%d_%H%M%S)
sed -i 's/^disable_functions = .*/disable_functions = system,putenv,chroot,chgrp,chown,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv/' /www/server/php/82/etc/php.ini
/etc/init.d/php-fpm-82 restart
然后清掉旧的 GD 缓存(不删的话,video-thumb.php 会命中 7 天缓存继续返回渐变图):
rm -f /www/wwwroot/ai.seerking.com/usr/uploads/video-thumbs/*.jpg验证
三个视频帖都拿到了 ffmpeg 真实帧:
| 视频 | 大小 | 尺寸 |
|---|---|---|
| 与好友骑行 | 38.6 KB | 400×844 |
| 厂房花飞 | 19.8 KB | 400×225 |
| 青海湖视频 | 39.3 KB | 400×844 |
跟 maer.win 的 Lavc58.134.100 mjpeg 输出是同一个编码器,视觉一致。
几个值得记一下的点:
page-gallery.php里用了$firstVideo但没定义——这种 bug PHP 不报错,$thumbSrc静默为空,是从 HTML 输出里看不见了才反应过来- 宝塔默认装了两套 PHP(80 和 82),改 ini 前必须先
pgrep -a php-fpm看清楚跑的是哪套,不然改了 80 没动 82 整个下午都在 debug apt install ffmpeg会顺带拉 nginx-common,装完一定apt-mark hold三件套(nginx nginx-common nginx-core),否则下次apt upgrade会把宝塔 nginx 挤掉- video-thumb.php 顶上的 7 天缓存判断会先于 ffmpeg 逻辑生效,GD 占位缓存如果不删,新逻辑永远跑不到