Skip to content

Metadata Presets

simonefil edited this page Jul 30, 2026 · 1 revision

Metadata Presets

A preset is an ordered list of IF/THEN rules. Each rule says "for every container / video track / audio track / subtitle track that satisfies these conditions, do these things". Analysis runs the whole list against every file and shows the result; nothing is written until you apply.

This page is about writing rules. Running them is Metadata Mode. The complete field, token, operator and function catalogue is Metadata Reference. Ready-made recipes are in Examples: Metadata.

Where presets live

Presets are JSON files in:

<data dir>/.remux-forge/presets/metadata/

The dropdown in Metadata mode lists what is in that folder, and the editor writes there. You never browse to a preset path in the WebUI: the name you give a preset becomes its filename. The CLI, by contrast, takes an explicit path: --preset /path/to/preset.json.

Presets are portable: copy the JSON file to another machine's preset folder and it appears in the dropdown.

The editor

Press F3 (or File ▸ Load preset…) in Metadata mode.

Preset editor

Three columns:

  • Left: the rule list, in execution order. Buttons per rule: ^ move up, v move down, + duplicate, x delete. Add rule at the bottom.
  • Centre: the selected rule, with its header fields, then the IF section, then the THEN section.
  • Right: the token browser and function browser, both filterable, which insert into whichever value field you last focused.

At the top: the preset selector, the name field, and New / Save / Delete. A name is required before you can save. It becomes the JSON filename and has nothing to do with what the rules do.

Hovering any control shows its explanation in the help area, the same way the Remux configuration dialog does.

Rule order

Rules run top to bottom, and each rule sees the file as modified by the rules before it.

This is the source of both the power and the confusion:

Rule 1: IF audio title is not empty        THEN Clear field audio_title
Rule 2: IF audio language equals ita      THEN Set audio_title = "Italian [audio_format]"

Rule 2's condition is evaluated against the state Rule 1 left behind. If Rule 2 had tested audio_title is not empty, it would never match, because Rule 1 just emptied it.

Two consequences:

  • Order determines the result. Use ^/v to put the rules in the sequence they must run.
  • When you need the value a field had before any rule touched it, use the original. prefix, [original.audio_title]. See Reading values.

The snapshot model behind this, and why re-running analysis is deterministic, is in Internals.

Rule header

Field Notes
Description* Required. It is the readable identifier for the rule everywhere else, in the change list, and in the hover tooltip that tells you which rule changed a field. Describe what the rule does.
Enabled Untick to disable a rule without deleting it. Handy for bisecting a preset that is doing something unexpected.
Advanced fields Reveals less common but still editable fields. Genuinely dangerous fields stay excluded regardless. If a preset already uses advanced fields you cannot untick this until you remove them.
Apply to The rule's scope: Container, Video, Audio or Subtitles.

Apply to determines everything else. It decides what the rule iterates over, which condition fields exist, and which fields you can edit:

Scope Runs Typical fields
Container once per file container_title, container_date, file tags
Video once per video track video_title, video_language, flags, video properties
Audio once per audio track audio_title, audio_language, flags, audio properties
Subtitles once per subtitle track subtitle_title, subtitle_language, flags, subtitle properties

Picking a mismatched field for the scope is a validation error: field is not compatible with scope X.

The IF section

Every condition in the IF list must be true; the list is an AND. An empty IF list means the rule always applies (Always true), which is the correct form for a rule that must reach every file or every track.

Four things you can add:

Field condition

The most common condition type. Four parts:

Part Notes
Field Read from the current pipeline snapshot: i.e. as modified by earlier rules.
Operator See the table below.
Value A literal, or a template with tokens and functions.
Unit For numeric, size and duration comparisons. Normalises the value before comparing. Size: KB, MB, GB, KiB, MiB, GiB. Duration: ms, s, min, h.

Operators:

Group Operators
Equality Equals, Not equals
Text Contains, Does not contain, Starts with, Ends with
Regex Regex, Not regex
Presence Empty, Not empty
Lists In list, Not in list
Numeric Greater than, Greater or equal, Less than, Less or equal
Ranges Between, Not between
Boolean True, False

In list needs at least one value; Between needs both bounds; a bad regex is rejected at save time with the position.

The Unit field is what makes size and duration comparisons readable. audio_stream_size Greater than 200 MB is a condition you can maintain; the same thing in raw bytes is not.

OR alternatives

A nested group where at least one inner condition must be true. This is how you express OR inside an otherwise-AND list:

IF  subtitle_language equals ita
AND OR alternatives:
      subtitle_title contains Signs
      subtitle_title contains Songs
      subtitle_title contains Forced

Matches Italian subtitles whose title mentions any of the three.

Track comparison

Compares the current track's field against the other tracks in a group. This is how you express "the biggest one" without knowing any absolute sizes.

Comparison True when the current track's value is…
Equals at least one equal to at least one other track's
Different from all different from every other track's
Greater than all greater than every other track's
Greater or equal to all ≥ every other track's
Less than all less than every other track's
Less or equal to all ≤ every other track's
Largest in group the maximum in the group
Smallest in group the minimum in the group
Rank at position N when the group is ordered (rank must be > 0)

The group is chosen separately:

Track group Members
Same language tracks in scope sharing the current track's language
Same format tracks in scope sharing the current track's format
Same language and format both
All in scope every track of this type in the file

subtitle_stream_size + Largest in group + Same language is the canonical "this is the full subtitle track, the small one is signs and songs" test.

Not valid in Container scope. There are no other tracks to compare with.

Track count

Counts the tracks in the selected group and compares that number with a value. Track count, Same language, Equals, 1 means "this is the only track in this language", which is a useful guard before setting a default flag.

Also not valid in Container scope.

The THEN section

Add one or more operations. They run in listed order, and can be reordered with ^/v.

Operation Effect
Set field Set an editable field to a value (literal or template)
Clear field Empty a clearable field
Set exclusive flag Set a boolean flag on this track and clear it on the rest of a group
Remove track Delete the track from the file. requires a remux, so this is the operation that turns a fast job into a slow one. Track scopes only.
Add/update statistics Add or refresh the track statistics tags
Delete statistics Remove the track statistics tags
Set tag Set a managed Matroska tag
Clear tag Clear one managed Matroska tag
Clear managed tags Clear every managed tag on the target. requires explicit confirmation via a toggle in the operation, otherwise validation refuses it

Set field and Clear field are not one action per field: the field is chosen from a dropdown filtered to what is valid for this rule's scope, and to non-advanced fields unless Advanced fields is enabled.

Set exclusive flag

This operation solves a problem that plain Set field cannot.

Matroska's default flag is supposed to be unique. Setting it on one track with Set field leaves any other default track set too, and players then pick arbitrarily. Set exclusive flag sets it on the current track and clears it on every other track in the chosen group:

Exclusive group Cleared on
Same language other tracks with the same language
Same format other tracks with the same format
Same language and format both
All in scope every other track of this type

"Exactly one default audio per language" is Set exclusive flag on the default flag with group Same language. Trying to use it on a non-boolean field is a validation error.

Tags

Tag target decides where a tag lands:

Target Writes to
File the container's tags
Current track the current track's tags
All tracks every track's tags

Tag keys are chosen from a list; free-form tag XML is not available. Which keys are offered depends on the scope:

Available on Keys
Container and tracks TITLE, SUBTITLE, DESCRIPTION, COMMENT, LANGUAGE
Container only SUMMARY, SYNOPSIS, DATE_RELEASED, GENRE, PART_NUMBER, PART_TOTAL, DIRECTOR, PRODUCER, WRITTEN_BY, COMPOSER, ENCODER, SOURCE, ORIGINAL_MEDIA_TYPE

For a note on an individual track use DESCRIPTION, COMMENT or SUBTITLE. Attempting a container-only key on a track fails validation with metadata is not managed by the UI: X.

PART_NUMBER, PART_TOTAL, DIRECTOR, PRODUCER, WRITTEN_BY, COMPOSER, ENCODER, SOURCE, LANGUAGE and ORIGINAL_MEDIA_TYPE are advanced: turn on Advanced fields to see them.

LANGUAGE tag values must be ISO 639-2 (eng, ita, jpn, und). MediaInfo often reports track languages as two-letter codes (en, it, ja), so converting is on you:

{[audio_language]:ToLower():Replace("en","eng"):Replace("it","ita"):Replace("ja","jpn")}

Reading values: tokens and functions

Both condition values and operation values are templates: they accept tokens and function expressions as well as literal text.

Tokens

Square brackets, substituted with the field's current value:

[audio_language] - [audio_format] [audio_channels_label]

ita - DTS 5.1

The token browser on the right lists every token available in the current scope, filterable, and inserts at the cursor. It needs a focused value field first, otherwise you get Select a value field in IF or THEN first.

Three prefixes change which snapshot a token reads:

Prefix Reads
(none) the current pipeline value, as modified by earlier rules
current. the same thing, written explicitly
original. the value before any rule ran

original. is how a rule reads a value that an earlier rule has overwritten. To clear a title and then rebuild it from the previous value:

Rule 1: THEN Clear field audio_title
Rule 2: THEN Set audio_title = "{[original.audio_title]:NormalizeSpaces()} - [audio_format]"

Five further prefixes read MediaInfo's raw field names directly for fields the catalogue does not model: mi.current.*, mi.video.*, mi.audio.*, mi.text.* and mi.general.*. Use them only when no catalogued field covers the value required; they bypass the type normalisation that catalogued fields receive.

An unknown token is a validation error naming the token and its position.

Functions

Functions wrap a value in braces and can be chained with : or with dots:

{[file_name]:TrimEnd(4)}
{[audio_title].Trim().NormalizeSpaces()}
{[audio_sampling_rate]:Div(1000):Format(0.#)} kHz
Function Example Input → Output
Trim() {[audio_title]:Trim()} ITA Full ITA Full
TrimEnd(4) {[file_name]:TrimEnd(4)} Bleach - 001.mkvBleach - 001
ToUpper() {[audio_language]:ToUpper()} itaITA
ToLower() {[audio_language]:ToLower()} ITAita
Replace(old,new) {[audio_title]:Replace(FLAC,LPCM)} FLAC 2.0LPCM 2.0
RegexReplace(pat,repl) {[audio_title]:RegexReplace(\d+,02)} Audio 01Audio 02
Substring(0,3) {[audio_language]:Substring(0,3)} ItalianoIta
Left(3) {[audio_language]:Left(3)} ItalianoIta
Right(3) {[audio_language]:Right(3)} Italianoano
NormalizeSpaces() {[subtitle_title]:NormalizeSpaces()} ITA FullITA Full
Add(1000) {[audio_bitrate]:Add(1000)} 2400025000
Sub(1000) {[audio_bitrate]:Sub(1000)} 2500024000
Mul(2) {[video_frame_count]:Mul(2)} 2448
Div(1000) {[audio_bitrate]:Div(1000)} 15360001536
Round(1) {[video_fps]:Round(1)} 23.97624
Floor() {[video_fps]:Floor()} 23.97623
Ceil() {[video_fps]:Ceil()} 23.00124
Format(0.#) {[audio_sampling_rate]:Div(1000):Format(0.#)} kHz 4800048 kHz

Division by zero is reported at validation time.

The function browser on the right inserts a function around whatever is already in the focused field, so put the token in first, then apply the function. Otherwise you get Insert a token or expression in the value first. Each function has an Ex. button that opens a worked example showing input, expression, output and notes, including argument order.

For long expressions, both IF and THEN values have a text editor dialog, a larger multi-line box, easier than editing a chain of five functions in a narrow input.

Validation

Save-time validation is strict and its messages name the exact rule and operation:

Message Meaning
Duplicate Description: X two rules share a description. They must be unique
Metadata rule without Description at index N Description is required
Metadata rule without operations at index N a rule needs at least one THEN operation
field is not compatible with scope X wrong field for the rule's Apply to
SetExclusiveFlag requires a boolean field exclusive flag used on a non-boolean
field is not clearable Clear field on a field that cannot be emptied
metadata is not managed by the UI: X tag key not available for that target
RemoveTrack requires a track scope Remove track in Container scope
ClearTags requires explicit confirmation tick the confirmation toggle
unknown condition field X typo, or a field from the wrong scope
comparison with other tracks is not valid for Container track comparison in Container scope
rank must be greater than zero Rank needs a positive value
list requires at least one value In list with an empty list
range requires both minimum and maximum values Between with one bound
invalid regex: X the pattern does not compile
Unsupported metadata preset schema version: X the JSON is from a newer version

Validation runs before the preset is written, so a rejected preset has never been applied to a file.

Design guidance

Keep rules narrow, and use several. One rule per intention, with a description that says what it does. A rule doing four unrelated things is impossible to debug when one of them misfires.

Order the rules. Clear before you set. Set languages before rules that condition on language. Put Remove track rules last so earlier rules are not operating on tracks that are about to disappear.

Guard your conditions. IF nothing THEN Set audio_title = "Italian" also labels the Japanese track. Condition on language, format or size.

Always analyze, then read the Simulation detail. Open the dialog, hover the changed fields, confirm the right rule changed the right thing. Presets are cheap to iterate on because analysis is free.

Check the Execution column. MkvMerge means the preset contains a structural change. That may be intended, but it changes the cost of the job substantially on a large library.

Test on a copy first. Set the output policy to Output folder, apply to a handful of files, inspect them, and only switch to Overwrite once you trust the preset.

Disable rather than delete while iterating. Untick Enabled, re-analyze and compare the result to identify which rule produced a given change.

Preset JSON

The editor is the supported way to author presets. The JSON is readable, diffable and portable. A minimal example:

{
  "SchemaVersion": 1,
  "Name": "audio-titles",
  "Rules": [
    {
      "Description": "Normalize Italian audio titles",
      "Enabled": true,
      "Scope": "Audio",
      "When": {
        "Conditions": [
          { "Field": "audio_language", "Operator": "Equals", "Value": "ita" }
        ]
      },
      "Operations": [
        {
          "Type": "SetField",
          "Field": "audio_title",
          "Value": "Italiano - [audio_format] [audio_channels_label]"
        }
      ]
    }
  ]
}

The full schema is in Metadata Reference.

Next

Clone this wiki locally