mekiki

command module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2026 License: MIT Imports: 30 Imported by: 0

README

mekiki

Terminal media toolkit powered by FFmpeg.

  • Inspect codecs, stream sizes, frame rate, HDR metadata, audio and subtitle tracks, language tags and image EXIF data.
  • Convert files with built-in or custom presets, including GPU encoders, in a parallel queue.
  • Edit metadata of containers, tracks and photos with mkvpropedit, exiftool or an ffmpeg remux.

Every command has --json output with a versioned schema and documented exit codes, so scripts and agents can drive it.

Install

Requirements: Go 1.26+ and FFmpeg (ffprobe, and ffmpeg for convert and meta). Optional: MKVToolNix (mkvpropedit) for in-place Matroska edits and ExifTool for image metadata. mekiki doctor lists what is installed.

go install github.com/Mimic890/mekiki@latest

Or build the repository locally:

go build -o mekiki .

Usage

mekiki <command> [flags] [args]
Command Purpose
info [path...] Show media information (default command)
convert <input...> -o <output> Convert files with presets and a parallel queue
preset <command> List, show, save, remove or edit presets
meta get|set|rm <file...> Read or edit container, track and image tags
doctor Check external tools, their versions and GPU presets
tui [dir] Open the terminal interface
version Print the version (also -v, --version)

mekiki <path...> is shorthand for mekiki info <path...>. To inspect a file literally named like a command, use mekiki info ./info.

Flags may appear before or after positional arguments; -- ends flag parsing. -h prints a command's flags. The CLI never asks questions interactively.

info
mekiki info [--json] [file|directory...]
Invocation Behavior
mekiki Inspect media files in the current directory
mekiki ./Movies Inspect media files in one directory
mekiki file.mkv Show a detailed view of one file
mekiki a.mkv b.mp4 ./Music Inspect several files and directories

Directory scans are non-recursive, skip hidden entries and subdirectories, and discover media by file extension. Every file is attempted; failures are reported after the rest are shown (see exit codes).

Each ffprobe process has a 30-second timeout. Paths are passed as explicit local inputs, and filenames and metadata are sanitized before terminal output.

Output covers:

  • File size, duration, overall bitrate, chapters, and common metadata
  • Video codec, resolution, average frame rate, frame count, bit depth, pixel format, color range, bitrate, and stream size
  • HDR10, HDR10+, HLG, and Dolby Vision data when reported by ffprobe
  • Audio codec, channels, sample rate, bitrate, stream size, and detectable Atmos or DTS:X profiles
  • Subtitle format, language, forced flag, title, and stream size
  • Still-image dimensions, pixel format, bit depth, and camera, lens, exposure and GPS tags

For images, tags come from exiftool when it is installed (EXIF, IPTC, XMP, GPS and maker notes; one exiftool process serves the whole run) and from ffprobe otherwise.

convert
mekiki convert [flags] <input...> -o <output>

Inputs are files or directories (their media files, non-recursive). With one input file, -o is the output file. With several inputs, a directory input, an existing directory, or a path ending in /, -o is an output directory: each output is named after its input with the preset's container as extension. The directory is created if needed.

Flag Meaning
-o PATH Output file or directory (required)
--preset NAME Start from a preset; the flags below override its fields
--vcodec, --acodec, --scodec Encoder per stream type, e.g. libx265, hevc_nvenc, copy
--crf N Constant quality for the main stream (video, or audio for audio presets), mapped per encoder
--vbitrate, --abitrate Bitrates, e.g. 6M, 192k
--encoder-preset Video encoder speed preset, e.g. slow, p5
--pix-fmt Video pixel format, e.g. yuv420p10le
--track SEL Track to keep (repeatable): all, stream index N, v, a, s, t, TYPE:N, TYPE:all. Default: all tracks
--container EXT Output extension for directory output
--load CLASS Load class: cpu-heavy, light, gpu, io
--ffmpeg-arg ARG Extra raw ffmpeg output argument (repeatable)
-j N, --jobs N Parallel jobs: a number or auto (default)
--fail-fast Cancel the remaining jobs after the first failure
--overwrite Replace existing outputs, including the input itself
--dry-run Print the ffmpeg commands and exit

Without --preset, every track is copied (-c copy) and only the given flags change that. --crf maps to -crf (x264, x265, SVT-AV1, libaom, VP9), -cq (NVENC), -global_quality (QSV), -qp (VAAPI), -qp_i/-qp_p (AMF), -quality (WebP) or -q (JPEG, LAME); other encoders reject it.

Video encoders apply to video tracks only; attached pictures (cover art), attachments and other tracks are copied. Global metadata and chapters are kept by ffmpeg's defaults.

Safety. ffmpeg writes to a hidden temp file next to the output (.name.*.part.ext), which is renamed into place only after success. An existing output, including the input itself, is replaced only with --overwrite. ffmpeg runs without a shell and without stdin. Ctrl+C stops every running job, removes their temp files and exits with 130. Conversions have no timeout.

Parallel queue. Every preset has a load class. With --jobs auto, each class has its own limit and jobs of different classes run side by side; a light job may start while a cpu-heavy job waits. With --jobs N, at most N jobs of any class run, in queue order. Each job gets cores / limit CPU threads (-threads, plus pools for x265 and lp for SVT-AV1). A failed job does not stop the others unless --fail-fast is set.

Load class Auto limit
cpu-heavy 1
light number of CPU cores
gpu 2
io 2

Progress. Text mode writes progress and per-file results to stderr: a live block on a terminal, finished files only otherwise. With --json, stdout carries JSON Lines (see below).

Image inputs: ffmpeg reads JPEG, PNG, WebP, TIFF, BMP, GIF, AVIF and HEIC/HEIF. DNG is decoded only partially, and camera RAW formats (CR2, CR3, NEF, ARW, RAF, ORF) are not supported. Image conversion does not carry over EXIF metadata.

preset
mekiki preset list [--json]
mekiki preset show [--json] <name>
mekiki preset save [convert flags] [--preset BASE] [--description TEXT] [--media TYPE] [--yes] <name>
mekiki preset rm --yes <name>
mekiki preset edit

list hides GPU presets that fail a short test encode on this machine. save stores the given convert flags as a user preset; --preset becomes its extends. Replacing an existing user preset needs --yes, as does rm. edit opens the presets file in $VISUAL or $EDITOR and validates it afterwards.

Built-in presets (read-only):

Media Presets
Video h264 (x264 CRF 20, AAC), hevc (x265 CRF 22), av1 (SVT-AV1 CRF 30), remux (copy)
Video, GPU hevc-nvenc, av1-nvenc, hevc-qsv, av1-qsv, hevc-amf, av1-amf, hevc-vaapi, av1-vaapi
Audio opus (160k), aac (256k, M4A), mp3 (VBR V2), flac
Image jpeg, webp, avif

Video presets write MKV and copy audio unless noted. Audio presets keep audio tracks only. Image presets keep the first image. VAAPI presets use /dev/dri/renderD128.

Configuration files

Both files live in the user config directory ($XDG_CONFIG_HOME/mekiki/ or ~/.config/mekiki/ on Linux, ~/Library/Application Support/mekiki/ on macOS).

presets.yml holds a list of user presets. A user preset with a built-in's name overrides it for the CLI; built-in presets never see user overrides.

- name: hevc-hq                  # lowercase letters, digits, '.', '_', '-'
  description: HEVC for archiving
  extends: hevc                  # inherit, then override the fields below
  media: video                   # video | audio | image
  container: mkv                 # extension for directory output
  load: cpu-heavy                # cpu-heavy | light | gpu | io
  video: {codec: libx265, crf: 18, preset: slow, pixFmt: yuv420p10le, args: [-tune, grain]}
  audio: {codec: libopus, bitrate: 192k}
  subtitle: {codec: copy}
  tracks: [v, a, "s:all"]        # default: all tracks
  inputArgs: [-hwaccel, cuda]    # raw args before -i
  args: [-movflags, +faststart]  # raw output args

config.yml changes the automatic job limits:

jobs:
  cpu-heavy: 2
  gpu: 3
meta
mekiki meta get [--json] <file|directory...>
mekiki meta set [flags] <file...> KEY=VALUE...
mekiki meta rm  [flags] <file...> KEY...

get shows container tags, every track with its selector (v:0, a:1, s:0), default/forced flags and tags, and, for images, MP4 and MOV, all exiftool tags by group (IFD0, ExifIFD, GPS, XMP-dc, IPTC, maker notes, ...).

set and rm take the files first (arguments that exist on disk), then the tags. They never change an input unless --in-place is given; otherwise -o names the output file, or a directory for several inputs.

Flag Meaning
-o PATH Write the result here; the input stays untouched
--in-place Change the input files themselves
--overwrite Replace an existing -o output
--track SEL Edit a track (v:N, a:N, s:N or stream index) instead of the container
--from FILE set only: copy all tags from FILE first (images, MP4, MOV)
--group NAME rm only: clear an exiftool group such as GPS or XMP (repeatable; images, MP4, MOV)
--dry-run Print the backend command without changing anything

The keys default and forced set track flags and take 1/0, yes/no or true/false. Other keys are tag names: title and language for tracks, any container tag such as title, artist or comment, and exiftool tag names for images (Artist, XMP-dc:Title, GPSLatitude).

mekiki meta set movie.mkv --in-place title="My Movie"
mekiki meta set movie.mkv --in-place --track s:0 language=rus forced=1 default=0
mekiki meta rm ./Photos/*.jpg --group GPS -o ./clean/
mekiki meta set edited.jpg --from original.jpg --in-place

Backends:

File Backend
Images (JPEG, PNG, WebP, TIFF, HEIC, AVIF, GIF, DNG and camera RAW) exiftool
MP4, MOV, M4V, 3GP with group keys (Keys:Make), --group or --from exiftool
Matroska (mkv, mka, webm) with --in-place, editing the container title or a track's title, language, default, forced mkvpropedit, edits in place without rewriting the file
Everything else, including MP3 and FLAC ffmpeg remux with -c copy

ffmpeg and exiftool write a temp file next to the target and rename it only on success, so --in-place never leaves a half-written file and exiftool never leaves *_original backups. mkvpropedit edits the Matroska header in place by design. Batches use one exiftool process (-stay_open). A track that does not exist is an error. exiftool writes fail when a tag is unknown, not writable, or has an invalid value.

doctor
mekiki doctor [--json]

Checks ffprobe, ffmpeg, mkvpropedit and exiftool: whether each is in PATH and which version it reports. It also test-encodes a short clip with every GPU preset, since an encoder listed by ffmpeg may still lack the hardware or driver. Exits with code 4 if ffprobe or ffmpeg is missing; the optional mkvpropedit and exiftool and the GPU results do not affect the exit code. Commands that do not need a missing tool keep working.

tui
mekiki tui [dir]

Opens a full-screen interface in dir (default: the current directory): a file browser on the left and the Info, Tracks, Metadata and Convert tabs on the right. It starts only with this command and needs stdin and stdout to be a terminal; it exits with code 2 otherwise. --json is not supported.

Key Action
1–4 Switch tabs
tab Move focus between the file browser and the tab
↑/↓ or k/j Move the cursor
enter, → / ⌫, ← Open a directory / go to the parent (browser)
space Mark a file for conversion (browser)
q, ctrl+c Quit; a running queue is stopped first and its temp files removed

Metadata lists container tags (or image tags by group) and every track with its tags. enter edits a value, a adds KEY=VALUE to the selected container or track, d deletes a tag, t and f toggle a track's default and forced flags, u drops all pending changes. Changes stay pending until w, which shows them with the exact commands; then y writes in place, c writes a name.edited.ext copy (an existing copy is never overwritten) and esc goes back. Moving to another file drops pending changes.

Convert picks a preset with ←/→ (only presets usable on this machine) and overrides its fields with enter; an empty field keeps the preset value. r converts the marked files, or the selected one, into the output directory (default converted/ under the starting directory) with the same queue as convert; existing outputs are never overwritten. Each job shows a progress bar, plus a total. + and - change the number of parallel jobs, 0 returns to automatic limits; a new count applies to jobs started afterwards. x stops the queue. s saves the current preset and overrides as a user preset that extends it.

Terminal output

Colors and box frames are drawn only when stdout is a terminal. Piped output is plain text. Set NO_COLOR to any non-empty value to disable colors in a terminal.

Data goes to stdout; errors and logs go to stderr.

JSON output

Every command accepts --json. Documents are printed to stdout and always carry schemaVersion. The version changes only on breaking changes; new fields may be added without a bump.

info --json:

{
  "schemaVersion": 1,
  "files": [
    {
      "filePath": "/media/demo.mkv",
      "fileName": "demo.mkv",
      "fileSize": 1048576,
      "durationMs": 10250,
      "format": "matroska,webm",
      "formatLong": "Matroska / WebM",
      "bitrateBps": 818400,
      "chapters": 2,
      "tags": { "title": "Demo" },
      "video": [ { "index": 0, "codec": "hevc", "width": 3840, "height": 2160, "fps": 23.976, "bitDepth": 10, "hdr": { "isHdr10": true } } ],
      "audio": [ { "index": 1, "codec": "eac3", "channels": 6, "language": "eng", "isAtmos": true } ],
      "subtitle": [ { "index": 2, "codec": "subrip", "language": "rus", "forced": true } ]
    }
  ],
  "errors": [ { "path": "/media/broken.mp4", "error": "ffprobe failed on \"broken.mp4\": ..." } ]
}

The stream objects are abbreviated above; the full field list is in testdata/info.golden. files, errors, video, audio and subtitle are always arrays, never null.

doctor --json:

{
  "schemaVersion": 1,
  "tools": [
    { "name": "ffprobe", "found": true, "path": "/usr/bin/ffprobe", "version": "7.1" },
    { "name": "mkvpropedit", "found": false, "error": "mkvpropedit not found in PATH; install MKVToolNix" }
  ],
  "gpuPresets": [
    { "name": "hevc-nvenc", "encoder": "hevc_nvenc", "ok": true },
    { "name": "hevc-amf", "encoder": "hevc_amf", "ok": false, "error": "encoder hevc_amf is not included in this ffmpeg build" }
  ]
}

convert --json prints one event per line. Every line has schemaVersion, jobId, type, input and output. type is start (with threads), progress (with progress), done, failed or canceled (with error). percent and durationMs are 0 when the input duration is unknown.

{"schemaVersion":1,"jobId":1,"type":"start","input":"a.mkv","output":"out/a.mkv","threads":4}
{"schemaVersion":1,"jobId":1,"type":"progress","input":"a.mkv","output":"out/a.mkv","progress":{"outTimeMs":1500,"durationMs":3000,"percent":50,"fps":120,"speed":4.8,"sizeBytes":65536}}
{"schemaVersion":1,"jobId":1,"type":"done","input":"a.mkv","output":"out/a.mkv"}
{"schemaVersion":1,"jobId":2,"type":"failed","input":"b.mkv","output":"out/b.mkv","error":"ffmpeg failed on \"b.mkv\": ..."}

meta get --json prints {"schemaVersion":1,"files":[{"path","tags","tracks":[{"index","selector","type","codec","default","forced","tags"}],"groups":{...}}],"errors":[...]}; groups appears only when exiftool read the file. meta set|rm --json prints {"schemaVersion":1,"results":[{"path","output","backend","command"}],"errors":[...]}, where command is set with --dry-run. See testdata/meta-get.golden and testdata/meta-edit.golden.

In info --json, image files carry an exif object with the same groups, and every track has default and tags.

convert --dry-run --json prints {"schemaVersion":1,"jobs":[{"jobId","input","output","preset","load","threads","command":["ffmpeg",...]}]}; see testdata/convert-dry-run.golden. The command shows the final output path; the real run writes to a temp file first.

preset list --json prints {"schemaVersion":1,"presets":[...],"unavailable":[{"name","error"}]}, and preset show --json prints {"schemaVersion":1,"preset":{...}} with the fields of presets.yml plus source (builtin or user); see testdata/preset-show.golden.

On failure with --json, stderr receives one JSON line:

{"schemaVersion":1,"error":{"code":"tool_missing","message":"ffprobe not found in PATH; install FFmpeg"}}

Exit codes

Code error.code Meaning
0 Success
1 error The operation failed (for batches: every item failed)
2 usage Invalid arguments or flags
3 partial Some items of a batch failed, others succeeded
4 tool_missing A required external tool is not installed
130 interrupted Stopped by Ctrl+C or SIGTERM

Development

gofmt -w .
go vet ./...
go test ./...
go build ./...

Integration tests generate tiny media files with ffmpeg -f lavfi and are skipped when the needed tool is missing. Regenerate golden files with go test . -update. Tests point XDG_CONFIG_HOME and HOME at temp dirs, so they never touch your presets.

License

MIT

Documentation

Overview

mekiki inspects, converts and retags media files using ffmpeg tools.

Terminal rendering for mekiki.

Directories

Path Synopsis
internal
fsutil
Package fsutil writes files safely: into a hidden temp file next to the target first, then renamed over it in one step.
Package fsutil writes files safely: into a hidden temp file next to the target first, then renamed over it in one step.
testmedia
Package testmedia generates tiny media files for integration tests.
Package testmedia generates tiny media files for integration tests.
pkg
convert
Package convert builds and runs ffmpeg conversions from presets, with a parallel job queue and live progress.
Package convert builds and runs ffmpeg conversions from presets, with a parallel job queue and live progress.
meta
Package meta reads and writes container, track and image metadata using ffprobe/ffmpeg, mkvpropedit and exiftool.
Package meta reads and writes container, track and image metadata using ffprobe/ffmpeg, mkvpropedit and exiftool.
probe
Package probe provides media file analysis using ffprobe.
Package probe provides media file analysis using ffprobe.
tools
Package tools locates the external programs mekiki depends on and reports their versions.
Package tools locates the external programs mekiki depends on and reports their versions.
Package tui is the terminal interface: a file browser beside Info, Tracks, Metadata and Convert panels, built on the same core packages as the CLI.
Package tui is the terminal interface: a file browser beside Info, Tracks, Metadata and Convert panels, built on the same core packages as the CLI.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL