Files
pastilhasandClaude Opus 5 7d57deeb3f three tasks on the right-click menu, and a skill for the agents
Convert Audio, Extract Audio, Convert Video. ported from the versions that ran on
alpha for months, which is where the rules come from — they are not invented:

  skip a file already in the target format (re-encoding mp3 to mp3 loses quality
    for nothing)
  never overwrite an existing output (the file there may be one somebody made)
  delete the source only AFTER the output exists, and only when asked

`inline: ask`, so the modal offers running it now or as a job — a few files inline,
a discography as a job. that was the affordance the old ones had and the reason the
choice exists.

THIN CLIENTS. every one calls /v1/ffmpeg; none of them runs ffmpeg. what to tell
ffmpeg lives once in av-club rather than three times in bash, which is the whole
point of the plugin existing. the three files nevertheless repeat their walk and
their reporting, because the task runner materialises ONLY index.ts and a script
task cannot import a sibling — that duplication is the platform's shape, not a
choice, and each file says so.

CONVERT VIDEO PROBES BEFORE IT DECIDES. if the codecs already fit the container it
asks for `c:v copy, c:a copy` — a remux, lossless, roughly a hundred times faster
than re-encoding. guessing from the extension is how a "fast remux" silently
re-encodes, since plenty of .avi holds h264. that needed a path probe, so
/v1/probe?path= is added beside it, same containment, read-only.

EXTRACT AUDIO HAS NO delete_source, deliberately. extracting is additive; offering
to delete would be offering to trade a video for its soundtrack. a video with no
audio track is reported as a skip rather than a failure — in a folder of screen
recordings that is most of them.

what is NOT here: track selection, subtitle handling, downmixing, per-file
percentage. those belong with jellyfin rather than with basic conversion and are
left where they are rather than half-ported. convert-video's TASK.md says so.

VERIFIED by running each task the way the runner does — temp dir, env vars, argv:
flac→mp3 on a folder; the re-run skipping both files for the two different reasons;
extraction skipping the silent clip and converting the other; mp4→mkv copying h264
and aac through untouched; mp4→webm re-encoding to vp9/opus; delete_source removing
a source after success and LEAVING it after a failure.

the skill tells an agent to use the endpoint and never to shell out to ffmpeg —
everything that makes this safe is on the far side of it — and to offer the
right-click tasks first when somebody is at a screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 02:17:57 +01:00

109 lines
5.1 KiB
Markdown

---
name: media-conversion
title: Converting media
description: Use when asked to convert, re-encode, remux or extract audio from a video or audio file, to make a web-friendly version of something, or to pull a still out of a video — "make this mp4", "get the audio off this", "these need to be smaller for the site". Carries the one endpoint, the copy-or-encode decision, and the rule that decides where the output goes.
version: 1
---
You convert media through **av-club**, a plugin on this machine. One endpoint does the work and you
already have the key.
```bash
AV=(-H "Authorization: Bearer $OFFICER_AGENT_KEY" -H "Host: $OFFICER_API_HOST"
-H 'content-type: application/json')
curl -s "${AV[@]}" "$OFFICER_API_URL/api/av-club/health"
```
## Never shell out to ffmpeg yourself
Not because you cannot, but because everything that makes this safe lives on the other side of that
endpoint: no shell, protocols disabled, filters allowlisted, work bounded, paths confined. A hand-built
`ffmpeg` command has none of it, and the first one somebody writes with a filename from a web page is
the incident. If av-club cannot express what is being asked, say so — that is a gap to report, not a
reason to go around it.
## Files stay where they are
```bash
curl -s "${AV[@]}" -X POST "$OFFICER_API_URL/api/av-club/v1/ffmpeg" \
-d '{"inputPath":"/home/you/Movies/film.mkv","outputPath":"/home/you/Movies/film.mp4",
"format":"mp4","outputOptions":{"c:v":"copy","c:a":"copy","movflags":"+faststart"}}'
# {"path":"/home/you/Movies/film.mp4","bytes":734003200}
```
Both paths must be **absolute** and inside the owner's home. A relative path, a `..`, a symlink
pointing out, or a write outside the root are each a 400 — do not try to work around one, it is
telling you the answer is no.
There is also an asset mode (`{"input":"as_…"}`) for files that are not on disk. Prefer paths: a
conversion of something already on the filesystem should not copy it twice.
## Look before you convert
```bash
curl -s "${AV[@]}" "$OFFICER_API_URL/api/av-club/v1/probe?path=/home/you/Movies/film.mkv"
```
**This decides the most important thing you will decide.** If the codecs already fit the target
container, ask for `"c:v":"copy","c:a":"copy"` — that is a *remux*: lossless, and about a hundred
times faster. Re-encoding a two-hour film that only needed rewrapping costs an hour of CPU and quality,
for nothing.
- **mp4** carries h264, hevc, mpeg4 · aac, mp3
- **mov** carries h264, hevc, prores · aac, pcm
- **mkv** carries almost anything, so converting *into* mkv is nearly always a remux
- **webm** carries only vp8, vp9, av1 · opus, vorbis — so mp4 → webm is always a re-encode
Guessing from the extension is how a "fast remux" silently re-encodes: plenty of `.avi` holds h264.
## Extracting audio
`vn: true` drops the video. It is a boolean — `"1"` is refused.
```bash
curl -s "${AV[@]}" -X POST "$OFFICER_API_URL/api/av-club/v1/ffmpeg" \
-d '{"inputPath":"/home/you/ep.mkv","outputPath":"/home/you/ep.mp3","format":"mp3",
"outputOptions":{"vn":true,"c:a":"libmp3lame","b:a":"320k","map_metadata":"0"}}'
```
**A video with no audio track fails**, and that is not a broken video — screen recordings usually have
none. Report it as "no audio track", not as an error.
## Quality settings worth reusing
| Want | Say |
| --- | --- |
| mp3 | `c:a libmp3lame`, `b:a 320k`, `id3v2_version 3` |
| m4a | `c:a aac`, `b:a 256k` |
| opus | `c:a libopus`, `b:a 192k` |
| ogg | `c:a libvorbis`, `q:a 6` |
| flac / wav | `c:a flac` / `c:a pcm_s16le` |
| h264 re-encode | `c:v libx264`, `crf 21`, `preset medium`, `pix_fmt yuv420p` |
| for the web | add `movflags +faststart`, or playback waits for the whole file |
**`map_metadata: "0"` or the tags are gone.** A converted album with no artist and no track numbers is
the kind of loss nobody notices until the library has been rebuilt.
CRF, not a bitrate: constant quality means a difficult scene gets the bits it needs and a static one
does not waste them.
## Three tasks already exist — offer them first
**Convert Audio**, **Extract Audio** and **Convert Video** are on the right-click menu of any media
file or folder in the file browser. They do everything above, with the skip rules and a delete-source
checkbox. If the owner is at a screen, pointing at the menu is better than running curl for them.
Reach for the API when it is not a right-click: a folder being watched, a batch inside a larger job,
or something the tasks do not offer.
## Rules that are not negotiable
- **The input is never modified.** av-club writes to a temp file and moves it into place, so a job
that dies leaves the original intact. Preserve that: never convert a file onto itself.
- **Never overwrite an existing output** without saying so first. The file that is there may be one
somebody made deliberately.
- **Deleting a source is the owner's decision**, and only ever after the output exists and is
non-empty. If you were not asked to delete, do not.
- **Say what you did in their words** — "made film.mp4 beside the original, 700 MB, copied both
streams so nothing was re-encoded" — not the JSON.