693 lines
19 KiB
Bash
Executable File
693 lines
19 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
shopt -s nullglob
|
|
|
|
############################################
|
|
# auto_add_sub_folder.sh
|
|
#
|
|
# Reads input_folder from ../settings.ini (relative to this script in bin/)
|
|
# - If CLI args are provided, they override input_folder.
|
|
#
|
|
# Behavior:
|
|
# - Recursively finds *.mkv under root(s) (case-insensitive)
|
|
# - Robust sample exclusion (case-insensitive):
|
|
# * filename is "sample.mkv"
|
|
# * OR filename contains token "sample" (basename-sample.mkv, basename.Sample.mkv, basename_sample.mkv, etc.)
|
|
# * OR ANY directory component equals "sample" (sample/, SAMPLE/, SaMPle/)
|
|
#
|
|
# - For each MKV: finds a sibling subtitle folder (case-insensitive) via candidates list:
|
|
# Subs, Sub, Subtitles, Subtitle
|
|
# then muxes ALL .idx/.sub pairs found inside.
|
|
#
|
|
# - Language naming strategy:
|
|
# 1) Prefer filename tokens (more reliable for scene releases)
|
|
# 2) If filename yields no hint -> read .idx "id: xx" and map to German display names
|
|
# 3) If still unknown -> do not set --track-name
|
|
#
|
|
# - Subtitle ordering is configurable via settings.ini [subtitle_order] (read ONCE):
|
|
# order=de:forced,de,en:forced,en
|
|
# unknown=end
|
|
# unknown_forced_first=1
|
|
#
|
|
# mkvmerge:
|
|
# - auto-detect native OR flatpak (org.bunkus.mkvtoolnix-gui etc.)
|
|
#
|
|
# Safety:
|
|
# - Writes output to temp file then replaces original
|
|
# - Keeps .bak by default (disable via --no-bak)
|
|
############################################
|
|
|
|
# ───────────────────────── 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
|
|
|
|
DRY_RUN=0
|
|
KEEP_BAK=1
|
|
VERBOSE=1
|
|
|
|
# Robust subtitle folder detection (case-insensitive)
|
|
SUBDIR_CANDIDATES=("Subs" "Sub" "Subtitles" "Subtitle")
|
|
|
|
# Defaults for subtitle order (can be overridden in settings.ini)
|
|
SUB_ORDER_RAW_DEFAULT="de:forced,de,en:forced,en"
|
|
SUB_UNKNOWN_MODE_DEFAULT="end" # end | keep
|
|
SUB_UNKNOWN_FORCED_FIRST_DEFAULT="1" # 1/0
|
|
|
|
# Will be filled in main() once
|
|
SUB_ORDER_RAW=""
|
|
SUB_UNKNOWN_MODE=""
|
|
SUB_UNKNOWN_FORCED_FIRST=""
|
|
|
|
# mkvmerge command (native or flatpak)
|
|
MKVMERGE_CMD=()
|
|
|
|
# Order rank map built once from SUB_ORDER_RAW
|
|
declare -A SUB_ORDER_RANK=()
|
|
|
|
# ---------------- language "database" for display names (GERMAN) ----------------
|
|
# Maps ISO 639-1/2 codes to German display names
|
|
declare -A LANG_DB=(
|
|
[de]="Deutsch" [deu]="Deutsch" [ger]="Deutsch"
|
|
[en]="Englisch" [eng]="Englisch"
|
|
[fr]="Französisch" [fra]="Französisch" [fre]="Französisch"
|
|
[es]="Spanisch" [spa]="Spanisch"
|
|
[it]="Italienisch" [ita]="Italienisch"
|
|
[nl]="Niederländisch" [nld]="Niederländisch" [dut]="Niederländisch"
|
|
[pl]="Polnisch" [pol]="Polnisch"
|
|
[ru]="Russisch" [rus]="Russisch"
|
|
[pt]="Portugiesisch" [por]="Portugiesisch"
|
|
[tr]="Türkisch" [tur]="Türkisch"
|
|
[sv]="Schwedisch" [swe]="Schwedisch"
|
|
[no]="Norwegisch" [nor]="Norwegisch"
|
|
[da]="Dänisch" [dan]="Dänisch"
|
|
[fi]="Finnisch" [fin]="Finnisch"
|
|
[cs]="Tschechisch" [ces]="Tschechisch" [cze]="Tschechisch"
|
|
[hu]="Ungarisch" [hun]="Ungarisch"
|
|
[ro]="Rumänisch" [ron]="Rumänisch" [rum]="Rumänisch"
|
|
[bg]="Bulgarisch" [bul]="Bulgarisch"
|
|
[el]="Griechisch" [ell]="Griechisch" [gre]="Griechisch"
|
|
[he]="Hebräisch" [heb]="Hebräisch"
|
|
)
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
auto_add_sub_folder.sh [options] [root] [root2 ...]
|
|
|
|
If no root is given:
|
|
- reads [pathes] input_folder from ../settings.ini
|
|
|
|
Options:
|
|
-n, --dry-run Print commands only, do not execute
|
|
--no-bak Do not keep .bak of original MKV
|
|
-q, --quiet Less logging
|
|
-h, --help Show this help
|
|
EOF
|
|
}
|
|
|
|
log() { (( VERBOSE == 1 )) && printf '[%s] %s\n' "$(date +'%F %T')" "$*"; }
|
|
dbg() { (( VERBOSE == 1 )) && printf ' %s\n' "$*"; }
|
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
|
|
|
# ---------------- INI parsing (minimal, safe for values with spaces) ----------------
|
|
ini_get() {
|
|
local ini_file="$1"
|
|
local section="$2"
|
|
local key="$3"
|
|
|
|
awk -v sec="[$section]" -v key="$key" '
|
|
function ltrim(s){ sub(/^[ \t\r\n]+/, "", s); return s }
|
|
function rtrim(s){ sub(/[ \t\r\n]+$/, "", s); return s }
|
|
function trim(s){ return rtrim(ltrim(s)) }
|
|
BEGIN{ insec=0 }
|
|
{
|
|
line=$0
|
|
sub(/^\xef\xbb\xbf/, "", line) # strip BOM if present
|
|
|
|
if (line ~ /^[ \t]*#/) next
|
|
if (line ~ /^[ \t]*;/) next
|
|
|
|
if (match(line, /^[ \t]*\[[^]]+\][ \t]*$/)) {
|
|
insec = (trim(line) == sec)
|
|
next
|
|
}
|
|
|
|
if (!insec) next
|
|
|
|
if (match(line, /^[ \t]*[^=]+=/)) {
|
|
split(line, a, "=")
|
|
k = trim(a[1])
|
|
if (k != key) next
|
|
|
|
pos = index(line, "=")
|
|
v = substr(line, pos+1)
|
|
v = trim(v)
|
|
|
|
if (v ~ /^".*"$/) { sub(/^"/, "", v); sub(/"$/, "", v) }
|
|
print v
|
|
exit
|
|
}
|
|
}
|
|
' "$ini_file"
|
|
}
|
|
|
|
# ---------------- mkvmerge detection ----------------
|
|
detect_mkvmerge() {
|
|
log "Detecting mkvmerge…"
|
|
if command -v mkvmerge >/dev/null 2>&1; then
|
|
MKVMERGE_CMD=(mkvmerge)
|
|
log "Using native mkvmerge: $(command -v mkvmerge)"
|
|
return 0
|
|
fi
|
|
|
|
if command -v flatpak >/dev/null 2>&1; then
|
|
local candidates=(
|
|
"org.bunkus.mkvtoolnix-gui"
|
|
"org.bunkus.mkvtoolnix.MKVToolNix"
|
|
"org.bunkus.mkvtoolnix"
|
|
)
|
|
|
|
local appid="" c=""
|
|
for c in "${candidates[@]}"; do
|
|
if flatpak info "$c" >/dev/null 2>&1; then
|
|
appid="$c"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -z "$appid" ]]; then
|
|
appid="$(flatpak list --app --columns=application 2>/dev/null | grep -i 'mkvtoolnix' | head -n 1 || true)"
|
|
fi
|
|
|
|
if [[ -n "$appid" ]]; then
|
|
MKVMERGE_CMD=(flatpak run --command=mkvmerge "$appid")
|
|
log "Using mkvmerge via Flatpak: $appid"
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
die "Neither native mkvmerge nor a MKVToolNix Flatpak installation was found."
|
|
}
|
|
|
|
# ---------------- sample exclusion ----------------
|
|
is_sample_mkv() {
|
|
local f="$1"
|
|
local base bn lc
|
|
base="$(basename "$f")"
|
|
bn="${base%.*}"
|
|
lc="${bn,,}"
|
|
|
|
if [[ "$lc" == "sample" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ "$lc" =~ (^|[._\ \-])sample([._\ \-]|$) ]]; then
|
|
return 0
|
|
fi
|
|
|
|
local d seg
|
|
d="$(dirname "$f")"
|
|
while [[ "$d" != "/" && -n "$d" ]]; do
|
|
seg="$(basename "$d")"
|
|
if [[ "${seg,,}" == "sample" ]]; then
|
|
return 0
|
|
fi
|
|
d="$(dirname "$d")"
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
# ---------------- subtitle folder finder ----------------
|
|
find_subs_dir() {
|
|
local mkv="$1"
|
|
local dir cand hit
|
|
dir="$(dirname "$mkv")"
|
|
|
|
for cand in "${SUBDIR_CANDIDATES[@]}"; do
|
|
hit="$(find "$dir" -maxdepth 1 -mindepth 1 -type d -iname "$cand" -print -quit 2>/dev/null || true)"
|
|
if [[ -n "$hit" ]]; then
|
|
echo "$hit"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
return 1
|
|
}
|
|
|
|
# ---------------- language helpers ----------------
|
|
lang_name_from_code() {
|
|
local code="${1,,}"
|
|
[[ -n "${LANG_DB[$code]:-}" ]] && printf '%s' "${LANG_DB[$code]}"
|
|
}
|
|
|
|
# Extract language code from VobSub .idx (first "id:" line), lowercased.
|
|
idx_lang_from_file() {
|
|
local idx="$1"
|
|
local code=""
|
|
|
|
code="$(awk '
|
|
BEGIN{IGNORECASE=1}
|
|
/^[ \t]*id:[ \t]*[a-z][a-z][a-z]?[ \t]*,/ {
|
|
gsub(/^[ \t]*/, "", $0)
|
|
sub(/^id:[ \t]*/, "", $0)
|
|
sub(/,.*/, "", $0)
|
|
print tolower($0)
|
|
exit
|
|
}
|
|
/^[ \t]*id:[ \t]*[a-z][a-z][a-z]?[ \t]*$/ {
|
|
gsub(/^[ \t]*/, "", $0)
|
|
sub(/^id:[ \t]*/, "", $0)
|
|
print tolower($0)
|
|
exit
|
|
}
|
|
' "$idx" 2>/dev/null || true)"
|
|
|
|
[[ -n "$code" ]] && printf '%s' "$code"
|
|
}
|
|
|
|
# Infer a language code from filename tokens (best effort).
|
|
# Returns: code or empty (exit 1)
|
|
lang_code_from_filename() {
|
|
local stem="$1"
|
|
local lc="${stem,,}"
|
|
|
|
# English
|
|
if [[ "$lc" =~ (^|[._\ \-])eng([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])en([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *english* ]]; then
|
|
printf 'en'; return 0
|
|
fi
|
|
|
|
# German
|
|
if [[ "$lc" =~ (^|[._\ \-])deu([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])ger([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])de([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *german* ]]; then
|
|
printf 'de'; return 0
|
|
fi
|
|
|
|
# French
|
|
if [[ "$lc" =~ (^|[._\ \-])fra([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])fre([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])fr([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *french* ]]; then
|
|
printf 'fr'; return 0
|
|
fi
|
|
|
|
# Spanish
|
|
if [[ "$lc" =~ (^|[._\ \-])spa([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])es([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *spanish* ]]; then
|
|
printf 'es'; return 0
|
|
fi
|
|
|
|
# Italian
|
|
if [[ "$lc" =~ (^|[._\ \-])ita([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])it([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *italian* ]]; then
|
|
printf 'it'; return 0
|
|
fi
|
|
|
|
# Dutch
|
|
if [[ "$lc" =~ (^|[._\ \-])nld([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])dut([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])nl([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *dutch* ]]; then
|
|
printf 'nl'; return 0
|
|
fi
|
|
|
|
# Polish
|
|
if [[ "$lc" =~ (^|[._\ \-])pol([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])pl([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *polish* ]]; then
|
|
printf 'pl'; return 0
|
|
fi
|
|
|
|
# Russian
|
|
if [[ "$lc" =~ (^|[._\ \-])rus([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])ru([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *russian* ]]; then
|
|
printf 'ru'; return 0
|
|
fi
|
|
|
|
# Portuguese
|
|
if [[ "$lc" =~ (^|[._\ \-])por([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])pt([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *portuguese* ]]; then
|
|
printf 'pt'; return 0
|
|
fi
|
|
|
|
# Turkish
|
|
if [[ "$lc" =~ (^|[._\ \-])tur([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])tr([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *turkish* ]]; then
|
|
printf 'tr'; return 0
|
|
fi
|
|
|
|
# Hebrew
|
|
if [[ "$lc" =~ (^|[._\ \-])heb([._\ \-]|$) ]] || \
|
|
[[ "$lc" =~ (^|[._\ \-])he([._\ \-]|$) ]] || \
|
|
[[ "$lc" == *hebrew* ]]; then
|
|
printf 'he'; return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Infer language code for sorting:
|
|
# 1) filename tokens, else 2) idx id:xx, else "und"
|
|
infer_lang_code_for_idx() {
|
|
local idx="$1"
|
|
local stem code
|
|
|
|
stem="$(basename "$idx" .idx)"
|
|
|
|
code="$(lang_code_from_filename "$stem" || true)"
|
|
[[ -n "${code:-}" ]] && { printf '%s' "$code"; return 0; }
|
|
|
|
code="$(idx_lang_from_file "$idx" || true)"
|
|
[[ -n "${code:-}" ]] && { printf '%s' "$code"; return 0; }
|
|
|
|
printf 'und'
|
|
}
|
|
|
|
# Track naming (German display names), prefers filename tokens, falls back to idx content.
|
|
# Echoes: track_name|forcedFlag
|
|
infer_meta_for_idx() {
|
|
local idx="$1"
|
|
local stem lc forced name code_file code_idx
|
|
|
|
stem="$(basename "$idx" .idx)"
|
|
lc="${stem,,}"
|
|
|
|
forced="no"
|
|
[[ "$lc" == *forced* ]] && forced="yes"
|
|
|
|
name=""
|
|
code_file="$(lang_code_from_filename "$stem" || true)"
|
|
if [[ -n "${code_file:-}" ]]; then
|
|
name="$(lang_name_from_code "$code_file" || true)"
|
|
fi
|
|
|
|
if [[ -z "$name" ]]; then
|
|
code_idx="$(idx_lang_from_file "$idx" || true)"
|
|
if [[ -n "${code_idx:-}" ]]; then
|
|
name="$(lang_name_from_code "$code_idx" || true)"
|
|
fi
|
|
fi
|
|
|
|
# warn on mismatch if both exist (filename wins)
|
|
if [[ -n "${code_file:-}" ]]; then
|
|
code_idx="$(idx_lang_from_file "$idx" || true)"
|
|
if [[ -n "${code_idx:-}" && "${code_idx,,}" != "${code_file,,}" ]]; then
|
|
dbg "WARN: Sprach-Mismatch für $(basename "$idx"): Dateiname=${code_file}, IDX=${code_idx} (Dateiname gewinnt)"
|
|
fi
|
|
fi
|
|
|
|
[[ -n "$name" && "$forced" == "yes" ]] && name+=" (Forced)"
|
|
echo "${name}|${forced}"
|
|
}
|
|
|
|
# ---------------- subtitle order helpers ----------------
|
|
build_sub_order_rank() {
|
|
local raw="$1"
|
|
SUB_ORDER_RANK=() # reset
|
|
local i=0 item
|
|
IFS=',' read -r -a _items <<<"$raw"
|
|
for item in "${_items[@]}"; do
|
|
item="${item//[[:space:]]/}"
|
|
[[ -z "$item" ]] && continue
|
|
SUB_ORDER_RANK["$item"]=$i
|
|
((i++))
|
|
done
|
|
}
|
|
|
|
subtitle_sort_rank() {
|
|
local lang="$1" forced="$2"
|
|
local ftok="normal"
|
|
[[ "$forced" == "yes" ]] && ftok="forced"
|
|
|
|
# exact match: de:forced
|
|
if [[ -n "${SUB_ORDER_RANK["$lang:$ftok"]+x}" ]]; then
|
|
printf '%05d' "${SUB_ORDER_RANK["$lang:$ftok"]}"
|
|
return 0
|
|
fi
|
|
# fallback: de
|
|
if [[ -n "${SUB_ORDER_RANK["$lang"]+x}" ]]; then
|
|
printf '%05d' "${SUB_ORDER_RANK["$lang"]}"
|
|
return 0
|
|
fi
|
|
|
|
# unknown handling
|
|
if [[ "${SUB_UNKNOWN_MODE:-end}" == "end" ]]; then
|
|
# unknowns go behind knowns; optionally forced unknown before non-forced unknown
|
|
local base=90000 bump=0
|
|
if [[ "${SUB_UNKNOWN_FORCED_FIRST:-1}" == "1" ]]; then
|
|
[[ "$forced" == "yes" ]] && bump=0 || bump=1
|
|
fi
|
|
printf '%05d' "$((base + bump))"
|
|
return 0
|
|
fi
|
|
|
|
# keep
|
|
printf '%05d' 90000
|
|
}
|
|
|
|
# ---------------- mux one mkv ----------------
|
|
mux_one_mkv() {
|
|
local mkv="$1"
|
|
local subs
|
|
|
|
log "MKV found: $mkv"
|
|
|
|
subs="$(find_subs_dir "$mkv" || true)"
|
|
dbg "Subs dir : ${subs:-<not found>}"
|
|
|
|
if [[ -z "${subs:-}" ]]; then
|
|
dbg "No subtitles folder found (tried: ${SUBDIR_CANDIDATES[*]}) -> skip"
|
|
return 0
|
|
fi
|
|
|
|
local -a idxs=()
|
|
local f=""
|
|
while IFS= read -r -d '' f; do
|
|
idxs+=("$f")
|
|
done < <(find "$subs" -maxdepth 1 -type f -iname "*.idx" -print0)
|
|
|
|
if [[ ${#idxs[@]} -eq 0 ]]; then
|
|
dbg "Subtitle folder exists but no .idx files -> skip"
|
|
return 0
|
|
fi
|
|
|
|
# ---- sort idxs according to settings-defined order ----
|
|
local -a decorated=()
|
|
local idx forcedFlag langcode rank
|
|
for idx in "${idxs[@]}"; do
|
|
forcedFlag="no"
|
|
[[ "${idx,,}" == *forced* ]] && forcedFlag="yes"
|
|
|
|
langcode="$(infer_lang_code_for_idx "$idx")"
|
|
rank="$(subtitle_sort_rank "$langcode" "$forcedFlag")"
|
|
|
|
# Decorate line for sorting:
|
|
# rank<TAB>lang<TAB>forced<TAB>path
|
|
decorated+=( "${rank}"$'\t'"${langcode}"$'\t'"${forcedFlag}"$'\t'"${idx}" )
|
|
done
|
|
|
|
IFS=$'\n' decorated=($(printf '%s\n' "${decorated[@]}" | LC_ALL=C sort))
|
|
unset IFS
|
|
|
|
idxs=()
|
|
local row
|
|
for row in "${decorated[@]}"; do
|
|
idxs+=( "$(printf '%s' "$row" | cut -f4-)" )
|
|
done
|
|
|
|
dbg "IDX files (sorted): ${#idxs[@]}"
|
|
for f in "${idxs[@]}"; do dbg " - $(basename "$f")"; done
|
|
|
|
local out_tmp="${mkv%.*}.with-subs.tmp.mkv"
|
|
local bak="${mkv}.bak"
|
|
|
|
local -a cmd=()
|
|
cmd=("${MKVMERGE_CMD[@]}" -o "$out_tmp" "$mkv")
|
|
|
|
local added=0
|
|
for idx in "${idxs[@]}"; do
|
|
local sub="${idx%.*}.sub"
|
|
if [[ ! -f "$sub" ]]; then
|
|
dbg "Skip idx (missing paired .sub): $(basename "$idx")"
|
|
continue
|
|
fi
|
|
|
|
local meta name forcedFlag2
|
|
meta="$(infer_meta_for_idx "$idx")"
|
|
IFS='|' read -r name forcedFlag2 <<<"$meta"
|
|
|
|
dbg "Add VobSub: $(basename "$idx") -> name='${name:-<none>}', forced=$forcedFlag2"
|
|
|
|
# Do NOT override language; keep what is in the .idx
|
|
if [[ -n "$name" ]]; then
|
|
cmd+=( --track-name 0:"$name" )
|
|
fi
|
|
|
|
if [[ "$forcedFlag2" == "yes" ]]; then
|
|
cmd+=( --forced-track 0:yes --default-track 0:no )
|
|
else
|
|
cmd+=( --forced-track 0:no --default-track 0:no )
|
|
fi
|
|
|
|
cmd+=( "$idx" )
|
|
((++added))
|
|
done
|
|
|
|
if [[ $added -eq 0 ]]; then
|
|
dbg "No usable subtitle sets (.idx + .sub) -> nothing to mux"
|
|
return 0
|
|
fi
|
|
|
|
log "Muxing $added subtitle input(s) into: $(basename "$mkv")"
|
|
dbg "Temp out : $out_tmp"
|
|
dbg "Command : ${cmd[*]}"
|
|
|
|
if [[ $DRY_RUN -eq 1 ]]; then
|
|
log "Dry-run: not executing."
|
|
return 0
|
|
fi
|
|
|
|
"${cmd[@]}"
|
|
|
|
[[ -f "$out_tmp" ]] || die "mkvmerge finished but temp output file not found: $out_tmp"
|
|
|
|
if [[ $KEEP_BAK -eq 1 ]]; then
|
|
mv -f -- "$mkv" "$bak"
|
|
mv -f -- "$out_tmp" "$mkv"
|
|
log "Done. Backup kept: $(basename "$bak")"
|
|
else
|
|
mv -f -- "$out_tmp" "$mkv"
|
|
log "Done. Original replaced (no .bak)."
|
|
fi
|
|
}
|
|
|
|
# ---------------- scan root ----------------
|
|
process_root() {
|
|
local root="$1"
|
|
[[ -e "$root" ]] || { log "Root not found: $root"; return 0; }
|
|
|
|
log "Scanning root: $root"
|
|
|
|
local -a mkvs=()
|
|
local f=""
|
|
|
|
while IFS= read -r -d '' f; do
|
|
if is_sample_mkv "$f"; then
|
|
dbg "Skip MKV (sample rule): $f"
|
|
continue
|
|
fi
|
|
mkvs+=("$f")
|
|
done < <(find "$root" -type f -iname "*.mkv" -print0)
|
|
|
|
log "Found MKVs: ${#mkvs[@]} under $root"
|
|
[[ ${#mkvs[@]} -eq 0 ]] && return 0
|
|
|
|
local processed=0 mkv=""
|
|
for mkv in "${mkvs[@]}"; do
|
|
mux_one_mkv "$mkv"
|
|
((++processed))
|
|
done
|
|
|
|
log "Finished root: $root (processed $processed MKV(s))"
|
|
}
|
|
|
|
# ---------------- main ----------------
|
|
main() {
|
|
local -a ROOTS=()
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-n|--dry-run) DRY_RUN=1; shift ;;
|
|
--no-bak) KEEP_BAK=0; shift ;;
|
|
-q|--quiet) VERBOSE=0; shift ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
--) shift; break ;;
|
|
-*) die "Unknown option: $1" ;;
|
|
*) ROOTS+=("$1"); shift ;;
|
|
esac
|
|
done
|
|
|
|
local script_dir base_dir ini_file
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
base_dir="$(cd "$script_dir/.." && pwd)"
|
|
ini_file="$base_dir/settings.ini"
|
|
|
|
# settings.ini only needed if no CLI roots
|
|
if [[ ${#ROOTS[@]} -eq 0 ]]; then
|
|
if [[ ! -f "$ini_file" ]]; then
|
|
echo
|
|
echo -e "${WHITE_ON_RED} Settingsfile nicht gefunden: $ini_file ${NC}"
|
|
echo
|
|
read -n 1 -s -r -p "Press any key to exit"
|
|
exit 1
|
|
fi
|
|
|
|
local input_folder
|
|
input_folder="$(ini_get "$ini_file" "pathes" "input_folder" || true)"
|
|
if [[ -z "${input_folder:-}" ]]; then
|
|
echo
|
|
echo -e "${WHITE_ON_RED} input_folder fehlt in settings.ini ([pathes]) ${NC}"
|
|
echo
|
|
read -n 1 -s -r -p "Press any key to exit"
|
|
exit 1
|
|
fi
|
|
ROOTS=("$input_folder")
|
|
fi
|
|
|
|
# Read subtitle order ONCE (only if ini exists; if script is run with CLI roots and no ini, use defaults)
|
|
if [[ -f "$ini_file" ]]; then
|
|
SUB_ORDER_RAW="$(ini_get "$ini_file" "subtitle_order" "order" || true)"
|
|
SUB_UNKNOWN_MODE="$(ini_get "$ini_file" "subtitle_order" "unknown" || true)"
|
|
SUB_UNKNOWN_FORCED_FIRST="$(ini_get "$ini_file" "subtitle_order" "unknown_forced_first" || true)"
|
|
fi
|
|
|
|
SUB_ORDER_RAW="${SUB_ORDER_RAW:-$SUB_ORDER_RAW_DEFAULT}"
|
|
SUB_UNKNOWN_MODE="${SUB_UNKNOWN_MODE:-$SUB_UNKNOWN_MODE_DEFAULT}"
|
|
SUB_UNKNOWN_FORCED_FIRST="${SUB_UNKNOWN_FORCED_FIRST:-$SUB_UNKNOWN_FORCED_FIRST_DEFAULT}"
|
|
|
|
build_sub_order_rank "$SUB_ORDER_RAW"
|
|
|
|
detect_mkvmerge
|
|
|
|
echo
|
|
echo "────────────────────────────────────────────────────────────────"
|
|
echo -e "${BLACK_ON_WHITE} Auto Add Sub Folder (VobSub) ${NC}"
|
|
echo "────────────────────────────────────────────────────────────────"
|
|
echo " "
|
|
echo -e "${WHITE_ON_GRAY} Input Folder ${NC} ${ROOTS[*]}"
|
|
echo -e "${WHITE_ON_GRAY} Subs Folders ${NC} ${SUBDIR_CANDIDATES[*]}"
|
|
echo -e "${WHITE_ON_GRAY} Reihenfolge ${NC} ${SUB_ORDER_RAW}"
|
|
echo -e "${WHITE_ON_GRAY} MKVMerge ${NC} ${MKVMERGE_CMD[*]}"
|
|
echo -e "${WHITE_ON_GRAY} Dry Run ${NC} ${DRY_RUN}"
|
|
echo -e "${WHITE_ON_GRAY} Keep Backup ${NC} ${KEEP_BAK}"
|
|
echo " "
|
|
echo "────────────────────────────────────────────────────────────────"
|
|
echo
|
|
|
|
local r=""
|
|
for r in "${ROOTS[@]}"; do
|
|
process_root "$r"
|
|
done
|
|
|
|
echo
|
|
echo "────────────────────────────────────────────────────────────────"
|
|
echo -e "${GREEN}✔ Finished${NC}"
|
|
echo
|
|
read -n 1 -s -r -p "Press any key to exit"
|
|
exit 0
|
|
}
|
|
|
|
main "$@" |