Bug description
Generating a Control Panel video thumbnail writes a Log::error() entry on every successful run, even though the thumbnail is produced correctly. Any app routing error-level logs to an aggregator (Sentry, Bugsnag, Papertrail, …) gets a spurious error for each first-time video thumbnail.
Two things combine:
-
Console\Processes\Process::logErrorOutput() logs at error level whenever the process wrote anything to stderr, regardless of exit code. ffmpeg (like git, composer, …) writes its banner and per-frame progress to stderr on success, so hasErrorOutput() is always true after a successful Ffmpeg::extractThumbnail(). Note Ffmpeg never calls throwOnFailure() — it treats a non-zero exit as "no thumbnail" via file_exists() — yet a zero-exit run is still logged as an error.
-
Console\Processes\Ffmpeg::buildCommand() builds … -vframes 1 <output>.jpg with no -loglevel/-hide_banner and no -update. On ffmpeg 8 the image2 muxer additionally emits:
[image2 @ …] The specified filename '…thumb_….jpg' does not contain an image sequence pattern or a pattern is invalid.
[image2 @ …] Use a pattern such as %03d for an image sequence or use the -update option (with -frames:v 1 if needed) to write a single image.
This is non-fatal (exit 0, file written), but it adds to the stderr that (1) then logs at error level.
How to reproduce
- Install ffmpeg (any version reproduces the stderr-on-success logging; ffmpeg 8 adds the extra image2 warning).
- Upload a video asset to a container with
video_thumbnails enabled.
- View it in the CP so the thumbnail is generated (
/cp/thumbnails/…).
- Observe an
error-level log entry Process Class: Ffmpeg / Command: … / Error: … containing the ffmpeg banner (and, on ffmpeg 8, the image2 warning) — despite the thumbnail rendering fine.
Command-level repro on ffmpeg 8.x:
$ ffmpeg -y -ss 00:00:00 -i input.mov -vframes 1 out.jpg; echo "exit=$?"
… <banner + progress on stderr> …
[image2 @ …] The specified filename 'out.jpg' does not contain an image sequence pattern…
[image2 @ …] Use … the -update option …
exit=0 # succeeds; out.jpg is written
$ ffmpeg -hide_banner -loglevel error -y -ss 00:00:00 -i input.mov -frames:v 1 -update 1 out.jpg; echo "exit=$?"
exit=0 # succeeds; out.jpg written; stderr EMPTY
Environment
- Statamic 6.23.0 (unchanged on
6.x HEAD)
- PHP 8.4
- ffmpeg 8.0.1 (Alpine) for the image2 warning; the stderr-on-success logging occurs on any ffmpeg version
Suggested fix
Primary (targeted, low risk) — quiet the command and make single-image output explicit in Ffmpeg::buildCommand():
return collect([
escapeshellarg($ffmpegBinary),
+ '-hide_banner',
+ '-loglevel error',
'-y',
'-ss',
escapeshellarg($this->startTimestamp),
'-i',
escapeshellarg($path),
- '-vframes 1',
+ '-frames:v 1',
+ '-update 1',
escapeshellarg($output),
])->join(' ');
With -loglevel error, a successful run produces no stderr → no error log, while a genuine failure still prints at error level and is still logged. -update 1 is ffmpeg's documented opt-in for writing a single image to a fixed filename — it silences the image2 warning and keeps the command correct if a future ffmpeg promotes that warning to a hard error.
Secondary (deeper root cause, optional) — Process::logErrorOutput() arguably shouldn't log at error when the process exited 0; gating on $process->getExitCode() > 0 (or logging stderr-on-success at debug/warning) would prevent this class of false error for every process wrapper. Flagging separately since it also affects Composer/Please/etc. and may warrant its own discussion.
Happy to open a PR for the Ffmpeg::buildCommand change.
Bug description
Generating a Control Panel video thumbnail writes a
Log::error()entry on every successful run, even though the thumbnail is produced correctly. Any app routingerror-level logs to an aggregator (Sentry, Bugsnag, Papertrail, …) gets a spurious error for each first-time video thumbnail.Two things combine:
Console\Processes\Process::logErrorOutput()logs aterrorlevel whenever the process wrote anything to stderr, regardless of exit code. ffmpeg (like git, composer, …) writes its banner and per-frame progress to stderr on success, sohasErrorOutput()is always true after a successfulFfmpeg::extractThumbnail(). NoteFfmpegnever callsthrowOnFailure()— it treats a non-zero exit as "no thumbnail" viafile_exists()— yet a zero-exit run is still logged as an error.Console\Processes\Ffmpeg::buildCommand()builds… -vframes 1 <output>.jpgwith no-loglevel/-hide_bannerand no-update. On ffmpeg 8 the image2 muxer additionally emits:This is non-fatal (exit 0, file written), but it adds to the stderr that (1) then logs at error level.
How to reproduce
video_thumbnailsenabled./cp/thumbnails/…).error-level log entryProcess Class: Ffmpeg / Command: … / Error: …containing the ffmpeg banner (and, on ffmpeg 8, the image2 warning) — despite the thumbnail rendering fine.Command-level repro on ffmpeg 8.x:
Environment
6.xHEAD)Suggested fix
Primary (targeted, low risk) — quiet the command and make single-image output explicit in
Ffmpeg::buildCommand():return collect([ escapeshellarg($ffmpegBinary), + '-hide_banner', + '-loglevel error', '-y', '-ss', escapeshellarg($this->startTimestamp), '-i', escapeshellarg($path), - '-vframes 1', + '-frames:v 1', + '-update 1', escapeshellarg($output), ])->join(' ');With
-loglevel error, a successful run produces no stderr → no error log, while a genuine failure still prints aterrorlevel and is still logged.-update 1is ffmpeg's documented opt-in for writing a single image to a fixed filename — it silences the image2 warning and keeps the command correct if a future ffmpeg promotes that warning to a hard error.Secondary (deeper root cause, optional) —
Process::logErrorOutput()arguably shouldn't log aterrorwhen the process exited0; gating on$process->getExitCode() > 0(or logging stderr-on-success atdebug/warning) would prevent this class of false error for every process wrapper. Flagging separately since it also affects Composer/Please/etc. and may warrant its own discussion.Happy to open a PR for the
Ffmpeg::buildCommandchange.