Index
If you use FFmpeg to do a lot of re-encoding/remuxing of your media files and have wanted a way to automatically insert the movie/show name as clean metadata, keep reading..
In order to make this work, the prerequisite is to ensure the name of the movie/show is in the file name itself. If you can't guarantee that, you should probably look for a different solution such as extracting the name from the container metadata.
After lots of trial & error, I have found what I believe to be the best method requiring the fewest number of modifications of the sed search script.
To reiterate: this extracts the movie/show name from the file name and inserts it into the title metadata field, which is a compatible and standard MKV & MP4 container metadata field.
gsed for GNU sed instead of sed on BSD platforms. Read about the differences between BSD sed and GNU sed.- Extract the movie/show name from the file name:
sed -E "s/\./\ /g;s/\w+/\U&/g;s/^(.*(S[[:digit:]]+E[[:digit:]]+|\(?[[:digit:]]{4}\)?[^P])).*(MKV|MP4)$/\1/" <INPUT>
Command Breakdown
sed -E
sed: The stream editor, a command-line tool for parsing and transforming text.-E: This flag enables extended regular expressions (EREs). This simplifies the syntax by removing the need to escape special characters like+,?,|,(, and).
The entire command is composed of three substitution expressions, each separated by a semicolon ;.
1. s/\./\ /g
s: The substitute command./\./: This is the pattern to be matched.\.is an escaped period, which matches a literal period character.. Without the backslash, a period is a wildcard that matches any single character.
/\ /: This is the replacement string. It replaces the matched period with a single space.g: The global flag, which means the substitution should be applied to all matches on the line, not just the first one.
Effect: This step replaces all periods in the input string with spaces. For example, My.Show.S01E01.mkv becomes My Show S01E01 mkv.
2. s/\w+/\U&/g
s: The substitute command./\w+/: The pattern to be matched.\wmatches any "word" character (letters, numbers, and underscore).+matches one or more occurrences of the preceding character, in this case, a word character.
/\U&/: The replacement string.\Uis a special escape sequence that converts all subsequent characters in the replacement string to uppercase.&is a back-reference that represents the entire matched pattern (\w+).
g: The global flag, which ensures that every "word" on the line is converted.
Effect: This step capitalizes every word in the input string. For example, My Show s01e01 mkv becomes MY SHOW S01E01 MKV.
3. s/^(.*(S[[:digit:]]+E[[:digit:]]+|\(?[[:digit:]]{4}\)?[^P])).*(MKV|MP4)$/\1/This is the most complex step, and it performs the final cleanup.
s: The substitute command.^...$: The pattern matches the entire line, from the beginning (^) to the end ($).^(.*(...|...)): The core of the regex is a capturing group ((...)) that holds the intended filename..*: Greedily matches any character (.) any number of times (*) until it finds the next part of the pattern.(S[[:digit:]]+E[[:digit:]]+| \(?[[:digit:]]{4}\)?[^P]): This is an alternation (|) matching one of two possible patterns commonly found in filenames.S[[:digit:]]+E[[:digit:]]+: Matches a show's season and episode format.S: The literal characterS.[[:digit:]]+: One or more digit characters.E: The literal characterE.[[:digit:]]+: One or more digit characters.
\(?[[:digit:]]{4}\)?[^P]: Matches a four-digit year, possibly in parentheses.\(?: An optional opening parenthesis.[[:digit:]]{4}: Exactly four digits.\)?: An optional closing parenthesis.[^P]: Any character that is not the letterP(to avoid matching the resolution such as1080P, which is normally after the year).
.*: Matches all remaining characters until the end of the line.(MKV|MP4): This is another capturing group, though it is not referenced in the replacement. It matches the common file extensionsMKVorMP4.$: Matches the end of the line./\1/: The replacement string uses the back-reference\1, which refers to the content captured by the first set of parentheses(...)described in the 3rd bullet-point under step 3.
Effect: This step finds the video file extension and any junk (like resolution, release group, or quality tags) and removes it, leaving only the series/episode or year information. For example, MY SHOW S01E01 1080P HDTV X264 MKV becomes MY SHOW S01E01 and MY MOVIE (2025) 1080P WEB-DL X265 MP4 becomes MY MOVIE (2025)
Shell Script Syntax using a For Loop
This shows the shell syntax involving the variable f in the for loop:
for f in <INPUT>; do ffmpeg -i "$f" -map 0 -metadata title="$(sed -E "s/\./\ /g;s/\w+/\U&/g;s/^(.*(S[[:digit:]]+E[[:digit:]]+|\(?[[:digit:]]{4}\)?[^P])).*(MKV|M4V|WEBM|MP4|MOV|MPG|AVI)$/\1/" <<<"${f}")" -codec copy <OUTPUT>; done
Simplified & Full Working Shell Script Examples
# SIMPLIFIED WORKING EXAMPLE ONE-LINER:
for f in *.mkv; do ffmpeg -i "$f" -map 0 -metadata title="$(sed -E 's/\./\ /g;s/\w+/\U&/g;s/^(.*(S[[:digit:]]+E[[:digit:]]+|\(?[[:digit:]]{4}\)?[^P])).*(MKV|MP4)$/\1/' <<<"${f}")" -codec copy "${f/.mkv/-modified.mkv}"; done
# SIMPLIFIED WORKING EXAMPLE MULTI-LINER:
for f in *.mkv; do ffmpeg -i "$f" -map 0 -metadata title="$(sed -E 's/\./\ /g;s/\w+/\U&/g;'\
's/^(.*(S[[:digit:]]+E[[:digit:]]+|\(?[[:digit:]]{4}\)?[^P])).*(MKV|MP4)$/\1/' <<<"${f}")" \
-codec copy "${f/.mkv/-modified.mkv}"; done
# FULL WORKING EXAMPLE ONE-LINER:
for f in *.mkv; do ffmpeg -loglevel verbose -hide_banner -y -i "$f" -map 0:v:0 -map 0:a:m:language:eng -map "0:s?" -dn -metadata title="$(sed -E 's/\./\ /g;s/\w+/\U&/g;s/^(.*(S[[:digit:]]+E[[:digit:]]+|\(?[[:digit:]]{4}\)?[^P])).*(MKV|M4V|WEBM|MP4|MOV|MPG|AVI)$/\1/' <<<"${f}")" -metadata:s:v:0 title= -metadata comment="Remuxed by visualblind $(date -Iseconds)" -metadata:s:v:0 language=eng -disposition:v:0 default -disposition:a:0 default -c:v copy -channel_layout "5.1" -c:a aac -c:s copy .working/"${f}"; done
# FULL WORKING EXAMPLE MULTI-LINER:
for f in *.mkv; do ffmpeg -loglevel verbose -hide_banner -y -i "$f" -map 0:v:0 -map \
0:a:m:language:eng -map "0:s?" -dn -metadata title=\
"$(sed -E 's/\./\ /g;s/\w+/\U&/g;s/^(.*(S[[:digit:]]+E[[:digit:]]+|'\
'\(?[[:digit:]]{4}\)?[^P])).*(MKV|M4V|WEBM|MP4|MOV|MPG|AVI)$/\1/' <<<"${f}")" \
-metadata:s:v:0 title= -metadata comment="Remuxed by visualblind $(date -Iseconds)" \
-metadata:s:v:0 language=eng -disposition:v:0 default -disposition:a:0 default \
-c:v copy -channel_layout "5.1" -c:a aac -c:s copy .working/"${f}"; done
Title Field Metadata Output from Sed
| Action | Result |
|---|---|
| Input | my.movie.s01e01.720p.hdtv.x264.mkv |
| After 1st sed | my movie s01e01 720p hdtv x264 mkv |
| After 2nd sed | MY MOVIE S01E01 720P HDTV X264 MKV |
| After 3rd sed | MY MOVIE S01E01 |
title metadata field in FFmpeg is ouput as the Movie name field when using the mediainfo tool.Mediainfo Output Example
General
Complete name : Clarksons.Farm.S04E01.1080p.AMZN.WEB-DL.AAC5.1.H264.mp4
Format : MPEG-4
Format profile : Base Media
Frame rate : 25.000 FPS
Movie name : CLARKSONS FARM S04E01
Writing application : Lavf60.16.100
Obsolete Full Working Examples (Don't Use)
# FFMPEG COMMAND FOR MOVIES:
for f in *.mp4; do ffmpeg -i "$f" -map 0 -movflags faststart -metadata title="$(sed -E 's/\./\ /g;s/\w+/\U&/g;s/^([A-Za-z -]+(\(?[[:digit:]]{4}\)?)?([^1080PI]|[^720PI]|[A-Za-z0-9 -])?(S[[:digit:]]{1,2}E[[:digit:]]{1,2})?).*$/\1/' <<<"${f}")" -codec copy .working/"$f"; done
# FFMPEG COMMAND FOR SHOWS:
for f in *.mkv; do ffmpeg -i "$f" -map 0 -metadata title="$(sed -E 's/\./\ /g;s/\w+/\U&/g;s/^([A-Za-z0-9 -]+\(?[[:digit:]]{4}\)?[^1080pPiI]|[A-Za-z0-9 -]+S[[:digit:]]{1,2}E[[:digit:]]{1,2}).*$/\1/' <<<"${f}")" -codec copy .working/"$f"; done
If you have any questions/comments regarding this article, click here or scroll down below (login isn't required to post comments and there's no waiting period).





