Unmanic / unmanic-plugins

Official plugins for the Unmanic application
21 stars 70 forks source link

Rename Audio #362

Open DJ-BrianC opened 1 year ago

DJ-BrianC commented 1 year ago

What is your new plugin request?

I have many video files with unknown audio. It would be nice to be able to map unknown audios to English. Just a cosmetic issue but it's anoying to see that when the audio is correct. I've been able to do it file by file with makemkv edit header but 1x1 is VERY tedious and slow.

Additional Context

No response

yajrendrag commented 11 months ago

You want to relabel any audio stream marked as "und" as "eng" instead? what if it's not really "eng"? Or is it that you know they are "eng" but they just just need to be correctly marked?

DJ-BrianC commented 11 months ago

That is correct. Like I said I already know that the audio was correct because I've watched most of the movies that I have. I just like to fix this cosmetic issue as a batch rather than a 1x1 which is very tedious.

yajrendrag commented 11 months ago

so, i started a plugin to do this, but i have no mkv files that have "und" tagged language streams, and oddly when i went to edit one (using ffmpeg or mkvpropedit), i can execute the command, but it simply wipes out the language tag. So net/net, i have no way to test it.

so here are 2 options - i can make the plugin available for you to try, OR, here is a bash shell script that you can run in your library that will make the changes. In EITHER case i would suggest making a test directory/library with a couple of files that you can try this on.

You can run these command interactively or put it in a shell script.

shopt -s globstar
for file in **/*.mkv; do as=$(ffprobe -v quiet -select_streams a -show_entries stream=index:stream_tags=language -of csv=p=0:nk=1 "$file"); if [[ "$as" =~ .*"und".* ]]; then for j in $as; do stream=$(echo "$j" | awk -F"," '{ print $1 + 1 }'); stream_lang=$(echo "$j" | awk -F"," '{ print $2 }'); if [[ "$stream_lang" == "und" ]]; then mkvpropedit "$file" --edit track:$stream --set language=eng; fi; done; fi; done
shopt -u globstar

For testing, i would put this/run this in a directory that only has a few test files in it. (cd to the directory you wish to run in - it will grab mkv files at that level and below). If you navigate to the root of your library and run it, it will select ALL mkv files in your library. This is the globstar functioning like this - without this it gets a little more complex to find all the mkv files and put them in an array and process them all. It equates to the same thing, but this is simpler to code.

phobiac commented 4 months ago
#!/bin/bash
# Input directory
dir=$1
set_lang=${2:-eng}
search_lang=${3:-und}

find "$dir" -type f -iname "*.mkv" \
        -execdir sh -c ' \
                set_lang=$1;
                search_lang=$2;
                mkvmerge -J "{}" | jq ".tracks[] | select(.type == \"audio\") | select(.properties.language == \"$search_lang\") | .properties.language" \
                | grep -q "$search_lang";
        ' {} "$set_lang" "$search_lang" \; \
        -execdir sh -c ' \
                set_lang=$1;
                search_lang=$2;
                audio_track_count=$(mkvmerge -J "{}" | jq ".tracks[] | select(.type == \"audio\") | select(.properties.language == \"$search_lang\") | .type" | wc -l);
                if [ "audio_track_count" -eq "1" ]
                then
                        track_id=1
                else
                        track_id=$(mkvmerge -J "{}" | jq ".tracks[] | select(.type == \"audio\") | select(.properties.language == \"$search_lang\") | .id" | head -n 1);
                fi;
                echo "Track [$track_id] to [$set_lang] on "$(basename "{}");
                mkvpropedit -v "{}" --edit track:a"$track_id" --set language="$set_lang";
        ' {} "$set_lang" "$search_lang" \; \
#-printf '%p\n'

# Manual commands
# FILE=""
# mkvmerge -J "$FILE" | jq ".tracks[] | select(.type == \"audio\")"
# find "$FILE" -type f -iname "*.mkv" -execdir sh -c 'mkvmerge -J "{}" | jq ".tracks[] | select(.type == \"audio\") | .properties.language"' {} \; -printf "%P\n"
# mkvpropedit -v "$FILE" --edit track:a1 --set language="eng"

# ffprobe way
# replaces first execdir #              ffprobe -v quiet -select_streams a -show_entries stream_tags=language -of csv=p=1:nk=0 "{}" | grep -q -w "stream,"
# replaces track #                      ffprobe -v quiet -select_streams a -show_entries stream=index -of csv=p=0:nk=1 "{}"

I've been playing with a solution for this same issue with my own files and wanted to share the above script I ended up going with. I originally wanted it to be contained within the unmanic docker container using the mkvpropedit plugin available in the repos. This should be able to be done using the above commands and the ffprobe plugin for file identification, though I'm probably just going to use the script and target specific files or folders.

I ended up finding a way to accomplish the file searching using ffprobe but also an alternative using mkvmerge that makes this script dependent entirely on the mkvinfo tools. The script by default is using the mkverge method. The main source of confusion I found was that ffprobe does NOT return und but instead returns a null so I'm taking advantage of the csv output to get a result that is more easily searchable. mkvmerge does return und when a tag is undefined.

It should be noted this search is a bit fragile. It's expecting your files to have only 1 audio track that's got an undefined language. It's pretty basic to run. I've named mine audiofixer.sh and you can run it as audiofixer.sh $FileOrFolder $SetLang $SearchLang where $FilerOrFolder is a path, and $SetLang/$SearchLang are any language code like eng, jpn, spa. The script will default to eng as the language to set and und as the language to search for if they are not provided. I also highly suggest commenting out the mkvpropedit line first and running a dry run against any files you want to modify just to be sure the output makes sense, or use the list of manual commands to get an idea of the changes the script would make before running it.