Created
January 23, 2025 10:12
This interactive script is used to correctly tag the audio language of a video when this information is missing or unknown (i.e in Plex Media Player), with a language of your choice.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -e | |
# PURPOSE | |
# This interactive script is used to correctly tag the audio language of a video when this information is missing or unknown, with a language of your choice. | |
#DESCRIPTION | |
# Sometimes Plex and other media players display “unknown” as the film language in the library. This is annoying, especially when many of the films in your collection are in this case, and these films are not necessarily in the same original language. | |
# | |
# This is because the file's audio track is not correctly tagged and contains no language information. The problem is that this tag is read directly into the file and cannot be easily modified without touching the video file. | |
# | |
# This script therefore serves to find files in your library that have no language defined or whose language is unknown, and then offers to tag them with the 3-letter ISO 639-2 language code of your choice. | |
# HOW IT WORKS | |
# When a file is found, you got this prompt: | |
# Options: | |
# [lng] Type directly the language code you want (ISO 639-2, 3 letters) | |
# [2] Skip this file | |
# [3] Quit the script | |
# Your choice: eng | |
# | |
# It then tags/remux the first audio stream with the chosen language. | |
# | |
# - If the file is an MKV, it just changes the property instantly with MKVToolnix (which must be installed, otherwise you'll have to adapt this part of the script). | |
# - If the file is an MP4, it copies the audio stream, tagging it correctly (remux) - I haven't found how to do it any other way. The file is temporarily copied with a .processing extension, then copied with its original name. The temporary file is then deleted. | |
# - If the file is an AVI (often old, low-quality divx), it converts it to MKV, correctly tagging the language in the process. The file is copied temporarily with a .processing extension, then copied with its original name and the mkv extension. The temporary file is then deleted. | |
# | |
# This script corresponds to my use on a multilingual library, and is supplied as is and without support. | |
# You can adapt it to your needs, for example by hard-coding the choice of language if you want 100% automatic use with a single language. | |
# HOW TO USE | |
# Copy the script on your server (eg in the /movies folder) and make it executable | |
# go to your movies folder and execute the script here (cd /path/to/movies) | |
# It will scan all files in subfolders and write the list in a temporary text file | |
# If the audio track language is unknow, it will prompt you to enter a language (3 letters ISO 639-2 https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) | |
# Remux / tagging happens and the script moves to the next file | |
# List of ISO 639-2 languages | |
valid_languages="aar abk afr aka amh ara arg asm ava ave aym aze bak bam bel ben bis bod bos bre bul cat ces cha che chu chv cor cos cre dan deu div dzo ell eng epo est eus ewe fao fas fij fin fra fry ful gla gle glg glv grn guj hat hau heb her hin hmo hrv hun hye ibo ido iii iku ile ina ind ipk isl ita jav jpn kal kan kas kat kau kaz khm kik kin kom kon kor kua kur lao lat lav lim lin lit ltz lub lug luo mac mah mal mar mkd mlg mlt mon mri msa mya nau nav nbl nde ndo nep nld nno nob nor nya oci oji ori orm oss pan pli pol por pus que roh ron run rus sag san sin slk slv sme smo sna snd som sot spa sqi srd srp ssw sun swa swe tah tam tat tel tgk tgl tha tib tir ton tsn tso tuk tur twi uig ukr urd uzb ven vie vol wln wol xho yid yor zha zho zul" | |
# Check if language is valid | |
function is_valid_language() { | |
local lang="$1" | |
[[ "$valid_languages" =~ (^|[[:space:]])"$lang"($|[[:space:]]) ]] | |
} | |
# Video temp file cleanup | |
find . -type f -name "*.processing" -exec rm -f {} \; | |
# Scan video files and save the list in temp file | |
file_list="file_list.txt" | |
find . -type f \( -iname "*.avi" -o -iname "*.mp4" -o -iname "*.mkv" \) > "$file_list" | |
# Process each listed video file | |
while IFS= read -r file <&3; do | |
echo "Analysing file: \"$file\"" | |
# Extract first track audio language | |
audio_lang=$(ffprobe -v error -select_streams a:0 -show_entries stream_tags=language -of csv=p=0 "$file" 2>/dev/null || echo "") | |
if [[ -z "$audio_lang" || "$audio_lang" == "und" || "$audio_lang" == "unknown" ]]; then | |
echo "Stream audio language is absent or unknown" | |
while true; do | |
echo "Options:" | |
echo " [lng] Type directly the language code you want (ISO 639-2, 3 letters)" | |
echo " [2] Skip this file" | |
echo " [3] Quit the script" | |
read -p "Your choice: " choice | |
if is_valid_language "$choice"; then | |
echo "Language code OK: $choice" | |
# Extensions handling | |
case "${file##*.}" in | |
avi) | |
mv "$file" "$file.processing" | |
ffmpeg -fflags +genpts -i "$file.processing" -c:v copy -c:a copy -metadata:s:a:0 language="$choice" "${file%.avi}.mkv" | |
rm "$file.processing" | |
;; | |
mp4) | |
mv "$file" "$file.processing" | |
ffmpeg -i "$file.processing" -c copy -metadata:s:a:0 language="$choice" "$file" | |
rm "$file.processing" | |
;; | |
mkv) | |
mkvpropedit "$file" --edit track:a1 --set language="$choice" | |
;; | |
*) | |
echo "Unknown extension, no processing done: $file" | |
;; | |
esac | |
break | |
elif [[ "$choice" == "2" ]]; then | |
echo "File skipped: $file" | |
break | |
elif [[ "$choice" == "3" ]]; then | |
echo "Script halted by user." | |
exit 0 | |
else | |
echo "Invalid choice, please retry" | |
fi | |
done | |
else | |
echo "Audio stream language is already set: $audio_lang" | |
fi | |
done 3<"$file_list" | |
# Delete temp file | |
rm -f "$file_list" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment