Initial commit

This commit is contained in:
2024-12-16 20:34:13 +01:00
commit 2a9406b370
6 changed files with 647 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
template.json
.vscode
settings.ini

7
auto_mkvmerge.desktop Normal file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Name=auto_mkvmerge
Exec=sh -e -c "exec \\"\\$(dirname \\"\\$0\\")/bin/auto_mkvmerge.sh\\"" %k
Icon=mkvmerge
Terminal=true
Type=Application

472
bin/auto_mkvmerge.sh Normal file
View File

@@ -0,0 +1,472 @@
#!/bin/bash
#─────────────────────────────────── user settings ───────────────────────────────────#
#─────────────────────────────────────────────────────────────────────────────────────#
# pathes
input_folder=""
output_folder=""
jsonfile_path="./template.json"
#─────────────────────────────────────────────────────────────────────────────────────#
# determine what to do if the video files have different audio and subtitle tracks
# possible values:
# normal | if credibility check returns with no errors start merging
# check | stops after credibility check
# nocheck | merge without a credibility check
mode=normal
#─────────────────────────────────────────────────────────────────────────────────────#
# choose which mkvmerge version is used
# possible values:
# binary | use "/usr/bin/mkvmerge"
# flatpak | use "flatpak run org.bunkus.mkvtoolnix-gui mkvmerge"
# path | use your own path to mkvmerge
mkvmerge=binary
#─────────────────────────────────────────────────────────────────────────────────────#
# get text notifications of errors and when the script is done (via notify-send)
# possible values:
# on | notifications on
# off | notifications off
notification_text=on
#─────────────────────────────────────────────────────────────────────────────────────#
# get audio notifications of errors and when the script is done
# possible values:
# off | no sound
# beep | beep sounds via speaker-test
# voice | tts via spd-say
# file | play wav files from $(pwd)./bin folder namend done.wav & error.wav
nofification_audio=beep
#─────────────────────────────────────────────────────────────────────────────────────#
# overwrite the above settings with the values from a ini file
# possible values:
# on | ignore above values and use $settings_file
# off | use the values from script
overwrite_with_settings_ini=on
settings_file="./settings.ini"
#─────────────────────────────────────────────────────────────────────────────────────#
#─────────────────────────────────── script start ────────────────────────────────────#
# ffprobe
ffprobe_path="/usr/bin/ffprobe"
# global variables
declare -a json_array
declare -a video_file_list
outputpath_index=-1
inputpath_index=-1
#colors
RED='\033[0;31m'
GREEN='\033[0;92m'
BLUE='\033[0;94m'
WHITE_ON_GRAY='\033[0;37;100m'
BLACK_ON_WHITE='\033[0;30;47m'
WHITE_ON_RED='\033[0;37;41m'
NC='\033[0m' # No Color
# Main Function
main() {
# get the user variables of the ini file
if [ "$overwrite_with_settings_ini" = "on" ]; then
get_user_variables_from_ini_file
fi
# convert relativ to absolut pathes
jsonfile_path=$(relativ_to_fullpath "$jsonfile_path")
# apply user varible $mkvmerge to $mkvmerge_path
if [ "$mkvmerge" = "binary" ]; then
mkvmerge_path="/usr/bin/mkvmerge"
elif [ "$mkvmerge" = "flatpak" ]; then
mkvmerge_path="flatpak run org.bunkus.mkvtoolnix-gui mkvmerge"
else
mkvmerge_path=$mkvmerge
fi
echo "────────────────────────────────────────────────────────────────"
echo -e "${BLACK_ON_WHITE} Auto MKVMerge start ${NC}"
echo "────────────────────────────────────────────────────────────────"
echo " "
echo -e "${WHITE_ON_GRAY} Input Folder ${NC} ""$input_folder"""
echo -e "${WHITE_ON_GRAY} Output Folder ${NC} ""$output_folder"""
echo -e "${WHITE_ON_GRAY} JSON File ${NC} ""$jsonfile_path"""
echo -e "${WHITE_ON_GRAY} MKVMerge ${NC} ""$mkvmerge_path"""
echo -e "${WHITE_ON_GRAY} Mode ${NC} ""$mode"""
echo " "
echo "────────────────────────────────────────────────────────────────"
# map json array to bash array
check_dependecies
init
#credibility check
if [ ! "$mode" = "nocheck" ]; then
check_credibility "${video_file_list[@]}"
fi
#start merging
if [ ! "$mode" = "check" ]; then
echo -e "${BLACK_ON_WHITE} Starting MKVMerge ${NC}"
local filecount=1 #count variable for the echo output
local filecountdecimal="" #same count variable in 3 decimal
local filetotaldecimal=$(printf "%03d" ${#video_file_list[@]}) #converting filescount to a 3 decimal output
for file_path in "${video_file_list[@]}"; do
# highlight_episode
highlighted_episode=$(highlight_episode "$file_path")
filecountdecimal=$(printf "%03d" $filecount)
echo "────────────────────────────────────────────────────────────────"
echo -e "${WHITE_ON_GRAY} ""$filecountdecimal""|""$filetotaldecimal"" ${NC} Merging: $highlighted_episode${NC}"
echo "────────────────────────────────────────────────────────────────"
((filecount++))
output_path="$output_folder/$(basename "$file_path")"
apply_mkvmerge_on_file "$file_path" "$output_path"
done
fi
_exit 0
}
check_dependecies(){
# Error check: Input path is invalid
if [ ! -e "$input_folder" ]; then
_exit 1 "Input path is invalid!"
fi
# Error check: Output path is invalid and cannot be created
if [ ! -e "$output_folder" ]; then
# try to create the path
mkdir -p "$output_folder" > /dev/null 2>&1
if [ $? -ne 0 ]; then
#echo "Output path is invalid or cannot be created!"
_exit 1 "Output path is invalid or cannot be created!"
fi
fi
# Error check: Input and Output paths must not be identical
if [[ "$input_folder" == "$output_folder" ]]; then
_exit 1 "Input and Output paths must not be identical!"
fi
if ! test -x "$ffprobe_path"; then
_exit 1 "ffprobe is not installed. Please install it before running this script."
fi
# Error check: Videos.json file not found
if [ ! -e "$jsonfile_path" ]; then
_exit 1 "Videos.json file not found!"
fi
# Error check: mkvmerge not found
if [ "$mkvmerge" = "binary" ]; then
if ! test -x "$mkvmerge_path"; then
_exit 1 "mkvmerge not found!"
fi
elif [ "$mkvmerge" = "flatpak" ]; then
if ! test -x "/var/lib/flatpak/app/org.bunkus.mkvtoolnix-gui"; then
_exit 1 "mkvmerge not found!"
fi
else
if ! test -x "$mkvmerge_path"; then
_exit 1 "mkvmerge not found!"
fi
fi
}
init() {
mapfile -t json_array < <(jq -r '.[]' "$jsonfile_path")
#loop over indices of bash array
for index in "${!json_array[@]}";do
#get item on current position
item="${json_array[$index]}"
#if current position has value --output the next index is the output path
if [[ "$item" == "--output" ]]; then
outputpath_index=$(($index + 1))
fi
#if current position has value ( the next index is the input path (hopefully)
if [[ "$item" == "(" ]]; then
inputpath_index=$(($index + 1))
fi
done
#if one path is not found: invalid json
if [[ $outputpath_index -eq -1 || $inputpath_index -eq -1 ]]; then
_exit 1 "there is an error with the json config file: input or output path not found"
fi
#save all mp4 and mkv file paths in video_file_list array
mapfile -t video_file_list < <(find "$input_folder" -type f \( -name "*.mp4" -o -name "*.mkv" -o -name "*.flv" -o -name "*.avi" \) | sort)
if [ "${#json_array[@]}" -eq 0 ]; then
_exit 1 "no files found in $input_folder"
fi
}
get_user_variables_from_ini_file() {
# get absolut path of the ini file
settings_file=$(relativ_to_fullpath "$settings_file")
# abort script if settings file can't be found
if [ ! -e "$settings_file" ]; then
_exit 1 "Settingsfile not found at: "$settings_file
fi
# read ini values
input_folder=$(sed -nr "/^\[pathes\]/ { :l /^input_folder[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
output_folder=$(sed -nr "/^\[pathes\]/ { :l /^output_folder[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
jsonfile_path=$(sed -nr "/^\[pathes\]/ { :l /^jsonfile_path[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
mode=$(sed -nr "/^\[mode\]/ { :l /^mode[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
mkvmerge=$(sed -nr "/^\[mkvmerge\]/ { :l /^mkvmerge[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
notification_text=$(sed -nr "/^\[notification_text\]/ { :l /^notification_text[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
nofification_audio=$(sed -nr "/^\[nofification_audio\]/ { :l /^nofification_audio[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
}
check_credibility(){
local global_error=false
local file_error=false
local first_iteration_audio_language_array=()
local first_iteration_subtitle_language_array=()
local files=("$@")
local filetotaldecimal=$(printf "%03d" ${#files[@]}) #converting filescount to a 3 decimal output
local filecount=1 #count variable for the echo output
local filecountdecimal="" #same count variable in 3 decimal
local audio_language_array_sep=""
local subtitle_language_array_sep=""
local seperator="|"
echo -e "${BLACK_ON_WHITE} Checking credibility ${NC}"
echo "────────────────────────────────────────────────────────────────"
for file in "${files[@]}"; do
ffprobe_json=$("$ffprobe_path" -v error -show_streams -of json "$file")
# Run ffprobe and extract language information using jq, store it in a bash array
readarray -t audio_language_array < <(echo "$ffprobe_json" | jq '.streams[] | select(.codec_type == "audio") | .tags.language')
readarray -t subtitle_language_array < <(echo "$ffprobe_json" | jq '.streams[] | select(.codec_type == "subtitle") | .tags.language')
# Debug Print the contents of the array
#printf '%s\n' "${language_array[@]}"
if [[ $filecount = 1 ]]; then
first_iteration_audio_language_array=("${audio_language_array[@]}")
first_iteration_subtitle_language_array=("${subtitle_language_array[@]}")
elif [[ "${first_iteration_audio_language_array[*]}" != "${audio_language_array[*]}" || "${first_iteration_subtitle_language_array[*]}" != "${subtitle_language_array[*]}" ]]; then
global_error=true
file_error=true
fi
#converting filecount to a 3 decimal output
filecountdecimal=$(printf "%03d" $filecount)
#converadding seperator to audio_language_array ($seperator)
audio_language_array_sep=$(printf "%s$seperator" "${audio_language_array[@]}")
#cutting off last seperator
audio_language_array_sep=${audio_language_array_sep%$seperator}
#adding seperator to subtitle_language_array ($seperator)
subtitle_language_array_sep=$(printf "%s$seperator" "${subtitle_language_array[@]}")
#cutting last off seperator
subtitle_language_array_sep=${subtitle_language_array_sep%$seperator}
# highlight_episode
highlighted_episode=$(highlight_episode "$file")
if [ $file_error = true ]; then
echo -e "${WHITE_ON_GRAY} ""$filecountdecimal""|""$filetotaldecimal"" ${NC} ${RED}Audio: ${#audio_language_array[@]} [${audio_language_array_sep[*]}]${NC} ${WHITE_ON_GRAY}|${NC} ${RED}Subtitle: ${#subtitle_language_array[@]} [${subtitle_language_array_sep[*]}]${NC} $highlighted_episode${NC}"
else
echo -e "${WHITE_ON_GRAY} ""$filecountdecimal""|""$filetotaldecimal"" ${NC} ${GREEN}Audio: ${#audio_language_array[@]} [${audio_language_array_sep[*]}]${NC} ${WHITE_ON_GRAY}|${NC} ${GREEN}Subtitle: ${#subtitle_language_array[@]} [${subtitle_language_array_sep[*]}]${NC} $highlighted_episode${NC}"
fi
((filecount++))
file_error=false
done
if [ $global_error = true ]; then
#echo "────────────────────────────────────────────────────────────────"
#echo -e "${WHITE_ON_RED} The credibility check returned with errors! Aborting... ${NC}"
#echo "────────────────────────────────────────────────────────────────"
_exit 1 "The credibility check returned with errors! Aborting"
else
echo "────────────────────────────────────────────────────────────────"
echo -e "${BLACK_ON_WHITE} The credibility test went smoothly ${NC}"
echo "────────────────────────────────────────────────────────────────"
fi
}
# MKV Merge Batch Function
apply_mkvmerge_on_file() {
#parameter is path of a video file to be merged
local input_file=$1
local output_file=$2
local command="$mkvmerge_path"
#update input path in video json array
json_array[inputpath_index]="$input_file"
#update output path in video json array
json_array[outputpath_index]="$output_file"
for argument in "${json_array[@]}"; do
command+=" \"$argument\""
done
eval "$command"
}
#beep sounds via speaker-test
playsound_speaker_sound() {
(
\speaker-test --frequency $1 --test sine > /dev/null 2>&1 &
pid=$!
\sleep 0.${2}s
\kill -9 $pid > /dev/null 2>&1
) > /dev/null 2>&1
}
_exit(){
#get the exit message
local error_state="$1"
local error_msg="$2"
local scriptpath="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
#script stopted with errors
if [ "$error_state" = 1 ]; then
#echo output
echo "────────────────────────────────────────────────────────────────"
echo -e "${WHITE_ON_RED} Error ${NC}"
echo
echo -e "${RED}$error_msg${NC}"
echo "────────────────────────────────────────────────────────────────"
#notification_text
if [ "$notification_text" = "on" ]; then
notify-send --urgency=critical "Auto MKVMerge Warning" "$error_msg"
fi
#nofification_audio
if [ "$nofification_audio" = "beep" ]; then
playsound_speaker_sound 1500 150
playsound_speaker_sound 1500 150
playsound_speaker_sound 1500 150
elif [ "$nofification_audio" = "voice" ]; then
spd-say "Auto MKVMerge Error"
elif [ "$nofification_audio" = "file" ]; then
aplay "$scriptpath/error.wav" > /dev/null 2>&1
fi
#exit the script
echo
read -n 1 -s -r -p "Press any key to exit"
exit
fi
#normal exit
if [ "$error_state" = 0 ]; then
#echo output
echo "────────────────────────────────────────────────────────────────"
echo -e "${BLACK_ON_WHITE} Finished! ${NC}"
echo "────────────────────────────────────────────────────────────────"
#notification_text
if [ "$notification_text" = "on" ]; then
notify-send --urgency=normal "Auto MKVMerge" "Finished!"
fi
#nofification_audio
if [ "$nofification_audio" = "beep" ]; then
playsound_speaker_sound 200 200
playsound_speaker_sound 400 200
elif [ "$nofification_audio" = "voice" ]; then
spd-say "Auto MKVMerge ist fertig"
elif [ "$nofification_audio" = "file" ]; then
aplay "$scriptpath/done.wav" > /dev/null 2>&1
fi
#exit the script
echo
read -n 1 -s -r -p "Press any key to exit"
exit
fi
#exit the script
echo
read -n 1 -s -r -p "Press any key to exit"
exit
}
# coloring SxxExx in string
highlight_episode() {
local input="$1"
local normal_color=${NC}
local episode_color=${BLUE}
local highlighted_episode=$(echo "$input" | awk -v normal_color="$normal_color" -v episode_color="$episode_color" 'BEGIN { IGNORECASE=1 } { gsub(/s[0-9]{2}e[0-9]{2}([e][0-9]{2})?/, episode_color"&\033[0m", $0); print normal_color$0"\033[0m" }')
echo "$highlighted_episode"
}
relativ_to_fullpath() {
local path="$1"
scriptpath="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
if [ ! -e "$path" ]; then
pathfull=$(realpath "$(dirname "$scriptpath")/$path")
else
pathfull=$path
fi
echo "$pathfull"
}
# Main
main
$SHELL

165
bin/auto_sort.sh Normal file
View File

@@ -0,0 +1,165 @@
#!/bin/bash
# global variables
declare -a video_file_list
settings_file="./settings.ini"
get_user_variables_from_ini_file
#save all mp4 and mkv file paths in video_file_list array
mapfile -t video_file_list < <(find "$input_folder" -type f \( -name "*.mp4" -o -name "*.mkv" -o -name "*.avi" \) | sort)
# Funktion, um den kleinsten gemeinsamen übergeordneten Ordner zu finden
find_common_parent() {
local common_parent
local first_file=true
# Durchlaufe alle übergebenen Dateien
for file in "$@"; do
# Überprüfe, ob die Datei existiert und eine Datei ist
if [ -f "$file" ]; then
# Bestimme den Ordner der Datei
file_dir=$(dirname "$file")
# Wenn dies die erste Datei ist, setze den gemeinsamen übergeordneten Ordner auf den Ordner dieser Datei
if [ "$first_file" = true ]; then
common_parent="$file_dir"
first_file=false
else
# Andernfalls bestimme den gemeinsamen übergeordneten Ordner zwischen dem aktuellen und dem bisherigen
common_parent=$(realpath --relative-to="$common_parent" "$file_dir")
fi
else
echo "Datei existiert nicht: $file"
return 1
fi
done
# Gib den gemeinsamen übergeordneten Ordner zurück
echo "$common_parent"
}
get_user_variables_from_ini_file() {
# get absolut path of the ini file
settings_file=$(relativ_to_fullpath "$settings_file")
# abort script if settings file can't be found
if [ ! -e "$settings_file" ]; then
_exit 1 "Settingsfile not found at: "$settings_file
fi
# read ini values
input_folder=$(sed -nr "/^\[pathes\]/ { :l /^input_folder[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
output_folder=$(sed -nr "/^\[pathes\]/ { :l /^output_folder[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
jsonfile_path=$(sed -nr "/^\[pathes\]/ { :l /^jsonfile_path[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
mode=$(sed -nr "/^\[mode\]/ { :l /^mode[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
mkvmerge=$(sed -nr "/^\[mkvmerge\]/ { :l /^mkvmerge[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
notification_text=$(sed -nr "/^\[notification_text\]/ { :l /^notification_text[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
nofification_audio=$(sed -nr "/^\[nofification_audio\]/ { :l /^nofification_audio[ ]*=/ { s/[^=]*=[ ]*//; p; q;}; n; b l;}" "$settings_file")
}
relativ_to_fullpath() {
local path="$1"
scriptpath="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
if [ ! -e "$path" ]; then
pathfull=$(realpath "$(dirname "$scriptpath")/$path")
else
pathfull=$path
fi
}
_exit(){
#get the exit message
local error_state="$1"
local error_msg="$2"
local scriptpath="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
#script stopted with errors
if [ "$error_state" = 1 ]; then
#echo output
echo "────────────────────────────────────────────────────────────────"
echo -e "${WHITE_ON_RED} Error ${NC}"
echo
echo -e "${RED}$error_msg${NC}"
echo "────────────────────────────────────────────────────────────────"
#notification_text
if [ "$notification_text" = "on" ]; then
notify-send --urgency=critical "Auto MKVMerge Warning" "$error_msg"
fi
#nofification_audio
if [ "$nofification_audio" = "beep" ]; then
playsound_speaker_sound 1500 150
playsound_speaker_sound 1500 150
playsound_speaker_sound 1500 150
elif [ "$nofification_audio" = "voice" ]; then
spd-say "Auto MKVMerge Error"
elif [ "$nofification_audio" = "file" ]; then
aplay "$scriptpath/error.wav" > /dev/null 2>&1
fi
#exit the script
echo
read -n 1 -s -r -p "Press any key to exit"
exit
fi
#normal exit
if [ "$error_state" = 0 ]; then
#echo output
echo "────────────────────────────────────────────────────────────────"
echo -e "${BLACK_ON_WHITE} Finished! ${NC}"
echo "────────────────────────────────────────────────────────────────"
#notification_text
if [ "$notification_text" = "on" ]; then
notify-send --urgency=normal "Auto MKVMerge" "Finished!"
fi
#nofification_audio
if [ "$nofification_audio" = "beep" ]; then
playsound_speaker_sound 200 200
playsound_speaker_sound 400 200
elif [ "$nofification_audio" = "voice" ]; then
spd-say "Auto MKVMerge ist fertig"
elif [ "$nofification_audio" = "file" ]; then
aplay "$scriptpath/done.wav" > /dev/null 2>&1
fi
#exit the script
echo
read -n 1 -s -r -p "Press any key to exit"
exit
fi
#exit the script
echo
read -n 1 -s -r -p "Press any key to exit"
exit
}

BIN
bin/done.wav Normal file

Binary file not shown.

BIN
bin/error.wav Normal file

Binary file not shown.