ImageMagick & FFMPEG 缩略图生成

ImageMagick 在生成缩略图的过程中遇到的问题。

不推荐 NPM 上的 GM 这个库,一个原因是更新时间年代久远,第二个是最新的 ImageMagick 的 语法推荐使用 magick 而非 convert GM 库底层使用的是 convert 命令因而不能使用最新的语法。

图片加水印

先生成文字图片,然后合成两张图片实现水印效果😊。

#!/bin/bash
# usage: ./watermark.sh "your text" input_image_path.png
convert -background none -fill "rgba(128,128,128,0.25)" -font Arial -rotate -30 -pointsize 14 label:"$1" ./watermark.png
convert $2 -alpha on \( +clone -tile ./watermark.png -draw "color 0,0 reset" \) -composite result.png
rm ./watermark.png

GIF PDF PSD 转换

命令:

const cmd = `magick "${input}" -resize ${scale.width}x${scale.height}  -quality 90 ${output}`;
execSync(cmd, { encoding: 'utf8' }).toString();

psd 和 pdf 在文件名上需要特殊处理,xx.psd[0] 代表着合并所有图层,xx.pdf[0] 代表获取 pdf 文件中的第一个图像,而如果你需要截取 GIF 的某一帧的图片的话,也需要在文件名后缀添加 xxx.gif[2]

文件尺寸

identify -ping -format "%wx%h" 'xxx.psd[0]'

报错

identify: improper image header `xxx.psd' @ error/psd.c/ReadPSDLayersInternal/1984

如果是 7.0+ 的版本,可以使用上面提到的文件名后缀添加 [0]的方式来尝试解决

300x30038x22871x243104x253170x271202x281241x291223x280223x281220x281220x282219x282218x284218x285223x285223x270238x287236x284238x282240x282243x282243x280278x283300x288300x292300x281300x281300x197300x185 

GIF 和 webp 格式的图片在尺寸获取上如果后缀没有加 [0] 的话,同样会出现上面的宽高结果。

FFMPEG 缩略图生成

视频信息获取

/**
 * 获取视频信息
 * @param path 视频路径
 * @returns 视频信息
 */
export const getVideoInfo = async (path: string) => {
  let info = null;
  const videoInfo: any = execSync(
    `ffprobe -v quiet -print_format json -show_format -show_streams -print_format json "${path}"`,
    { encoding: 'utf8' },
  ).toString();
  info = JSON.parse(videoInfo);
  const res = [
    {
      width: info.streams[0].width,
      height: info.streams[0].height,
    },
    info.format.size,
  ];
  return res;
};

视频时长获取

如果直接截取视频的某一帧的话会出现截取不到或者首帧为空白,后者这种粗暴的做法会导致页面上的所有视频的缩略图都可能是【广电龙头】🥸,因此我们需要判断视频的长度。

// 时长 单位秒
const duration = Number(
  execSync(
    `ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ${from}`,
    { encoding: 'utf8' },
  ).toString(),
);

转码截取缩略图

execSync(
  `ffmpeg -hide_banner -loglevel error -i ${input}  -y -f image2 -ss 00:00:${seconds} -vframes 1 -s ${width}:${height} ${output}`,
  { encoding: 'utf8' },
).toString();