npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zapo-js/media-utils

v1.0.0

Published

Media processing utilities for zapo-js (thumbnails, waveforms, probing)

Readme

@zapo-js/media-utils

WaMediaProcessor implementation for zapo-js. Hooks sharp and ffmpeg/ffprobe into the client so outgoing media gets the right thumbnails, waveforms, and voice-note normalization without you wiring any of that yourself.

What it does

| Capability | Backend | Used by | | -------------------------------------- | --------- | ------------------------------------------------------------ | | Image thumbnails | sharp | image / document previews | | Sticker thumbnails | sharp | sticker previews + animated sticker first-frame | | Video thumbnails | ffmpeg | video / ptv first-frame previews | | Media probe (duration, mime, w/h) | ffprobe | every media kind that needs dimensions | | Voice-note waveform | ffmpeg | audio ptt waveform bar | | Voice-note normalization (Opus encode) | ffmpeg | recording-pipeline outputs that aren't already Opus 16k mono |

Install

npm install @zapo-js/media-utils sharp

sharp is a peer dependency. ffmpeg and ffprobe are external binaries the package shells out to - install them once on the system (or vendor a static build into the deploy).

Installing ffmpeg + ffprobe

Most distributions ship ffprobe together with ffmpeg (same upstream).

macOS (Homebrew):

brew install ffmpeg
ffmpeg -version && ffprobe -version

Debian / Ubuntu:

sudo apt update && sudo apt install -y ffmpeg

Fedora / RHEL (RPM Fusion):

sudo dnf install -y https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
sudo dnf install -y ffmpeg

Alpine (Docker base images):

apk add --no-cache ffmpeg

Windows (Chocolatey / winget / Scoop):

choco install ffmpeg-full -y          # OR
winget install Gyan.FFmpeg            # OR
scoop install ffmpeg

Vendored static build (any OS, no system package manager):

# pulls a self-contained ffmpeg + ffprobe pair into ./node_modules/.bin
npm install --save-dev @ffmpeg-installer/ffmpeg @ffprobe-installer/ffprobe
import { path as ffmpegPath } from '@ffmpeg-installer/ffmpeg'
import { path as ffprobePath } from '@ffprobe-installer/ffprobe'
import { createMediaProcessor } from '@zapo-js/media-utils'

const processor = createMediaProcessor({ ffmpegPath, ffprobePath })

Dockerfile snippet (Alpine base, no system PM allowed at runtime):

FROM node:22-alpine
RUN apk add --no-cache ffmpeg

Confirm ffmpeg -version and ffprobe -version resolve before starting your app. The processor is non-fatal on missing binaries (emits a warn through the per-call logger context instead of throwing), so a misconfigured deploy degrades to "no thumbnails / no waveform" instead of crashing.

Quick start

import { WaClient } from 'zapo-js'
import { createMediaProcessor } from '@zapo-js/media-utils'

// One processor can be shared across many WaClient sessions. The runtime
// passes a per-call `ctx.logger` to each method so warnings always land
// in the right session log without any per-session setup.
const processor = createMediaProcessor()

const client = new WaClient({
    store,
    sessionId: 'default',
    media: { processor }
})

// Now sending a video produces a real thumbnail + correct duration/dimensions:
await client.message.send(jid, {
    type: 'video',
    media: '/tmp/clip.mp4',
    mimetype: 'video/mp4',
    caption: 'demo'
})

Config

createMediaProcessor(options) accepts:

| Field | Description | | ------------------------------------------------------------------- | ----------------------------------------------------------- | | ffmpegPath / ffprobePath | Override binary paths (default: resolved from PATH). | | imageThumbMaxEdge | Override the per-call maxEdge for image/video thumbnails. | | imageThumbQuality | JPEG quality (1-100) for image thumbnails. | | waveformPoints | Number of waveform points for voice notes (WA default: 64). | | voiceNoteBitRate / voiceNoteSampleRate / voiceNoteApplication | Opus encoding params for voice-note normalization. |

Logging is wired automatically when the processor is invoked from a WaClient: every method receives a per-call ctx: { logger?: Logger } the runtime fills in with that session's logger (carrying its { session, scope: 'media-utils' } bindings). No callback to plumb, no mutable state on the processor - so a single createMediaProcessor() result is safe to share across multiple WaClient instances. When used standalone (no ctx), the processor stays silent.

Notes

  • Missing binaries are non-fatal. If ffmpeg/ffprobe isn't installed, the affected operation is skipped and a warn is emitted through the per-call logger context - the rest of the media path still works. This means you can ship the processor as a soft dependency.
  • The thumbnail / waveform code paths follow what the official client produces - sticker first-frame extraction matches WhatsApp's expected layout, waveforms render natively on iOS/Android/desktop.
  • Voice-note normalization is needed because WhatsApp only renders Opus 16k mono in the chat UI - other formats play but lose the waveform.

See the main zapo-js docs for the media option and WaMediaProcessor contract.