.
This commit is contained in:
@@ -19,8 +19,15 @@ shopt -s nullglob
|
|||||||
# Subs, Sub, Subtitles, Subtitle
|
# Subs, Sub, Subtitles, Subtitle
|
||||||
# then muxes ALL .idx/.sub pairs found inside.
|
# then muxes ALL .idx/.sub pairs found inside.
|
||||||
#
|
#
|
||||||
# - Does NOT override language (VobSub language usually lives in the .idx)
|
# - Language naming strategy:
|
||||||
# - Sets track-name (heuristic) + forced flag based on idx filename only
|
# 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:
|
# mkvmerge:
|
||||||
# - auto-detect native OR flatpak (org.bunkus.mkvtoolnix-gui etc.)
|
# - auto-detect native OR flatpak (org.bunkus.mkvtoolnix-gui etc.)
|
||||||
@@ -46,8 +53,47 @@ VERBOSE=1
|
|||||||
# Robust subtitle folder detection (case-insensitive)
|
# Robust subtitle folder detection (case-insensitive)
|
||||||
SUBDIR_CANDIDATES=("Subs" "Sub" "Subtitles" "Subtitle")
|
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=()
|
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() {
|
usage() {
|
||||||
cat <<'EOF'
|
cat <<'EOF'
|
||||||
Usage:
|
Usage:
|
||||||
@@ -69,9 +115,6 @@ dbg() { (( VERBOSE == 1 )) && printf ' %s\n' "$*"; }
|
|||||||
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
# ---------------- INI parsing (minimal, safe for values with spaces) ----------------
|
# ---------------- INI parsing (minimal, safe for values with spaces) ----------------
|
||||||
# Reads first matching key in a section:
|
|
||||||
# [section]
|
|
||||||
# key=value
|
|
||||||
ini_get() {
|
ini_get() {
|
||||||
local ini_file="$1"
|
local ini_file="$1"
|
||||||
local section="$2"
|
local section="$2"
|
||||||
@@ -84,14 +127,11 @@ ini_get() {
|
|||||||
BEGIN{ insec=0 }
|
BEGIN{ insec=0 }
|
||||||
{
|
{
|
||||||
line=$0
|
line=$0
|
||||||
# strip BOM if present
|
sub(/^\xef\xbb\xbf/, "", line) # strip BOM if present
|
||||||
sub(/^\xef\xbb\xbf/, "", line)
|
|
||||||
|
|
||||||
# ignore comments (full-line)
|
|
||||||
if (line ~ /^[ \t]*#/) next
|
if (line ~ /^[ \t]*#/) next
|
||||||
if (line ~ /^[ \t]*;/) next
|
if (line ~ /^[ \t]*;/) next
|
||||||
|
|
||||||
# section header
|
|
||||||
if (match(line, /^[ \t]*\[[^]]+\][ \t]*$/)) {
|
if (match(line, /^[ \t]*\[[^]]+\][ \t]*$/)) {
|
||||||
insec = (trim(line) == sec)
|
insec = (trim(line) == sec)
|
||||||
next
|
next
|
||||||
@@ -99,7 +139,6 @@ ini_get() {
|
|||||||
|
|
||||||
if (!insec) next
|
if (!insec) next
|
||||||
|
|
||||||
# key=value, keep everything after first '=' (may contain spaces)
|
|
||||||
if (match(line, /^[ \t]*[^=]+=/)) {
|
if (match(line, /^[ \t]*[^=]+=/)) {
|
||||||
split(line, a, "=")
|
split(line, a, "=")
|
||||||
k = trim(a[1])
|
k = trim(a[1])
|
||||||
@@ -109,7 +148,6 @@ ini_get() {
|
|||||||
v = substr(line, pos+1)
|
v = substr(line, pos+1)
|
||||||
v = trim(v)
|
v = trim(v)
|
||||||
|
|
||||||
# remove optional surrounding quotes
|
|
||||||
if (v ~ /^".*"$/) { sub(/^"/, "", v); sub(/"$/, "", v) }
|
if (v ~ /^".*"$/) { sub(/^"/, "", v); sub(/"$/, "", v) }
|
||||||
print v
|
print v
|
||||||
exit
|
exit
|
||||||
@@ -157,7 +195,6 @@ detect_mkvmerge() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ---------------- sample exclusion ----------------
|
# ---------------- sample exclusion ----------------
|
||||||
# Returns 0 if path should be excluded as sample; otherwise 1.
|
|
||||||
is_sample_mkv() {
|
is_sample_mkv() {
|
||||||
local f="$1"
|
local f="$1"
|
||||||
local base bn lc
|
local base bn lc
|
||||||
@@ -165,17 +202,14 @@ is_sample_mkv() {
|
|||||||
bn="${base%.*}"
|
bn="${base%.*}"
|
||||||
lc="${bn,,}"
|
lc="${bn,,}"
|
||||||
|
|
||||||
# 1) exactly "sample"
|
|
||||||
if [[ "$lc" == "sample" ]]; then
|
if [[ "$lc" == "sample" ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2) token "sample" in basename (separators: dot, underscore, hyphen, space)
|
|
||||||
if [[ "$lc" =~ (^|[._\ \-])sample([._\ \-]|$) ]]; then
|
if [[ "$lc" =~ (^|[._\ \-])sample([._\ \-]|$) ]]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3) any directory component equals "sample" (case-insensitive)
|
|
||||||
local d seg
|
local d seg
|
||||||
d="$(dirname "$f")"
|
d="$(dirname "$f")"
|
||||||
while [[ "$d" != "/" && -n "$d" ]]; do
|
while [[ "$d" != "/" && -n "$d" ]]; do
|
||||||
@@ -189,7 +223,7 @@ is_sample_mkv() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ---------------- subtitle folder finder (case-insensitive candidates) ----------------
|
# ---------------- subtitle folder finder ----------------
|
||||||
find_subs_dir() {
|
find_subs_dir() {
|
||||||
local mkv="$1"
|
local mkv="$1"
|
||||||
local dir cand hit
|
local dir cand hit
|
||||||
@@ -206,55 +240,13 @@ find_subs_dir() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ---------------- language helpers ----------------
|
||||||
# ---------------- language "database" for display names (GERMAN) ----------------
|
lang_name_from_code() {
|
||||||
# Maps ISO 639-1/2 codes to German display names
|
local code="${1,,}"
|
||||||
declare -A LANG_DB=(
|
[[ -n "${LANG_DB[$code]:-}" ]] && printf '%s' "${LANG_DB[$code]}"
|
||||||
# Deutsch
|
}
|
||||||
[de]="Deutsch" [deu]="Deutsch" [ger]="Deutsch"
|
|
||||||
# Englisch
|
|
||||||
[en]="Englisch" [eng]="Englisch"
|
|
||||||
# Französisch
|
|
||||||
[fr]="Französisch" [fra]="Französisch" [fre]="Französisch"
|
|
||||||
# Spanisch
|
|
||||||
[es]="Spanisch" [spa]="Spanisch"
|
|
||||||
# Italienisch
|
|
||||||
[it]="Italienisch" [ita]="Italienisch"
|
|
||||||
# Niederländisch
|
|
||||||
[nl]="Niederländisch" [nld]="Niederländisch" [dut]="Niederländisch"
|
|
||||||
# Polnisch
|
|
||||||
[pl]="Polnisch" [pol]="Polnisch"
|
|
||||||
# Russisch
|
|
||||||
[ru]="Russisch" [rus]="Russisch"
|
|
||||||
# Portugiesisch
|
|
||||||
[pt]="Portugiesisch" [por]="Portugiesisch"
|
|
||||||
# Türkisch
|
|
||||||
[tr]="Türkisch" [tur]="Türkisch"
|
|
||||||
# Schwedisch
|
|
||||||
[sv]="Schwedisch" [swe]="Schwedisch"
|
|
||||||
# Norwegisch
|
|
||||||
[no]="Norwegisch" [nor]="Norwegisch"
|
|
||||||
# Dänisch
|
|
||||||
[da]="Dänisch" [dan]="Dänisch"
|
|
||||||
# Finnisch
|
|
||||||
[fi]="Finnisch" [fin]="Finnisch"
|
|
||||||
# Tschechisch
|
|
||||||
[cs]="Tschechisch" [ces]="Tschechisch" [cze]="Tschechisch"
|
|
||||||
# Ungarisch
|
|
||||||
[hu]="Ungarisch" [hun]="Ungarisch"
|
|
||||||
# Rumänisch
|
|
||||||
[ro]="Rumänisch" [ron]="Rumänisch" [rum]="Rumänisch"
|
|
||||||
# Bulgarisch
|
|
||||||
[bg]="Bulgarisch" [bul]="Bulgarisch"
|
|
||||||
# Griechisch
|
|
||||||
[el]="Griechisch" [ell]="Griechisch" [gre]="Griechisch"
|
|
||||||
# Hebräisch
|
|
||||||
[he]="Hebräisch" [heb]="Hebräisch"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Extract language code from VobSub .idx (first "id:" line), lowercased.
|
# Extract language code from VobSub .idx (first "id:" line), lowercased.
|
||||||
# Example line: "id: en, index: 0"
|
|
||||||
idx_lang_from_file() {
|
idx_lang_from_file() {
|
||||||
local idx="$1"
|
local idx="$1"
|
||||||
local code=""
|
local code=""
|
||||||
@@ -279,20 +271,20 @@ idx_lang_from_file() {
|
|||||||
[[ -n "$code" ]] && printf '%s' "$code"
|
[[ -n "$code" ]] && printf '%s' "$code"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Try to infer a language "code" from filename tokens (best effort).
|
# Infer a language code from filename tokens (best effort).
|
||||||
# Returns: code or empty
|
# Returns: code or empty (exit 1)
|
||||||
lang_code_from_filename() {
|
lang_code_from_filename() {
|
||||||
local stem="$1"
|
local stem="$1"
|
||||||
local lc="${stem,,}"
|
local lc="${stem,,}"
|
||||||
|
|
||||||
# English tokens
|
# English
|
||||||
if [[ "$lc" =~ (^|[._\ \-])eng([._\ \-]|$) ]] || \
|
if [[ "$lc" =~ (^|[._\ \-])eng([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])en([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])en([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" == *english* ]]; then
|
[[ "$lc" == *english* ]]; then
|
||||||
printf 'en'; return 0
|
printf 'en'; return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# German tokens
|
# German
|
||||||
if [[ "$lc" =~ (^|[._\ \-])deu([._\ \-]|$) ]] || \
|
if [[ "$lc" =~ (^|[._\ \-])deu([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])ger([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])ger([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])de([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])de([._\ \-]|$) ]] || \
|
||||||
@@ -300,7 +292,7 @@ lang_code_from_filename() {
|
|||||||
printf 'de'; return 0
|
printf 'de'; return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# French tokens
|
# French
|
||||||
if [[ "$lc" =~ (^|[._\ \-])fra([._\ \-]|$) ]] || \
|
if [[ "$lc" =~ (^|[._\ \-])fra([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])fre([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])fre([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])fr([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])fr([._\ \-]|$) ]] || \
|
||||||
@@ -308,38 +300,85 @@ lang_code_from_filename() {
|
|||||||
printf 'fr'; return 0
|
printf 'fr'; return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Spanish tokens
|
# Spanish
|
||||||
if [[ "$lc" =~ (^|[._\ \-])spa([._\ \-]|$) ]] || \
|
if [[ "$lc" =~ (^|[._\ \-])spa([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])es([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])es([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" == *spanish* ]]; then
|
[[ "$lc" == *spanish* ]]; then
|
||||||
printf 'es'; return 0
|
printf 'es'; return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Italian tokens
|
# Italian
|
||||||
if [[ "$lc" =~ (^|[._\ \-])ita([._\ \-]|$) ]] || \
|
if [[ "$lc" =~ (^|[._\ \-])ita([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" =~ (^|[._\ \-])it([._\ \-]|$) ]] || \
|
[[ "$lc" =~ (^|[._\ \-])it([._\ \-]|$) ]] || \
|
||||||
[[ "$lc" == *italian* ]]; then
|
[[ "$lc" == *italian* ]]; then
|
||||||
printf 'it'; return 0
|
printf 'it'; return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add more here if you want...
|
# 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
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Convert a language code to a nice name using LANG_DB.
|
# Infer language code for sorting:
|
||||||
# Returns: name or empty
|
# 1) filename tokens, else 2) idx id:xx, else "und"
|
||||||
lang_name_from_code() {
|
infer_lang_code_for_idx() {
|
||||||
local code="${1,,}"
|
local idx="$1"
|
||||||
[[ -n "${LANG_DB[$code]:-}" ]] && printf '%s' "${LANG_DB[$code]}"
|
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
|
# Echoes: track_name|forcedFlag
|
||||||
# Strategy:
|
|
||||||
# 1) Prefer filename tokens (more reliable for scene releases)
|
|
||||||
# 2) If filename gives no hint -> read idx "id: xx" and map to display name
|
|
||||||
# 3) If still unknown -> empty name (=> don't set --track-name)
|
|
||||||
infer_meta_for_idx() {
|
infer_meta_for_idx() {
|
||||||
local idx="$1"
|
local idx="$1"
|
||||||
local stem lc forced name code_file code_idx
|
local stem lc forced name code_file code_idx
|
||||||
@@ -351,14 +390,11 @@ infer_meta_for_idx() {
|
|||||||
[[ "$lc" == *forced* ]] && forced="yes"
|
[[ "$lc" == *forced* ]] && forced="yes"
|
||||||
|
|
||||||
name=""
|
name=""
|
||||||
|
|
||||||
# 1) from filename
|
|
||||||
code_file="$(lang_code_from_filename "$stem" || true)"
|
code_file="$(lang_code_from_filename "$stem" || true)"
|
||||||
if [[ -n "${code_file:-}" ]]; then
|
if [[ -n "${code_file:-}" ]]; then
|
||||||
name="$(lang_name_from_code "$code_file" || true)"
|
name="$(lang_name_from_code "$code_file" || true)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2) fallback: from idx file content
|
|
||||||
if [[ -z "$name" ]]; then
|
if [[ -z "$name" ]]; then
|
||||||
code_idx="$(idx_lang_from_file "$idx" || true)"
|
code_idx="$(idx_lang_from_file "$idx" || true)"
|
||||||
if [[ -n "${code_idx:-}" ]]; then
|
if [[ -n "${code_idx:-}" ]]; then
|
||||||
@@ -366,11 +402,11 @@ infer_meta_for_idx() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Optional: warn on mismatch if both exist (filename wins)
|
# warn on mismatch if both exist (filename wins)
|
||||||
if [[ -n "${code_file:-}" ]]; then
|
if [[ -n "${code_file:-}" ]]; then
|
||||||
code_idx="$(idx_lang_from_file "$idx" || true)"
|
code_idx="$(idx_lang_from_file "$idx" || true)"
|
||||||
if [[ -n "${code_idx:-}" && "$code_idx" != "$code_file" ]]; then
|
if [[ -n "${code_idx:-}" && "${code_idx,,}" != "${code_file,,}" ]]; then
|
||||||
dbg "WARN: language mismatch for $(basename "$idx"): filename=$code_file, idx=$code_idx (using filename)"
|
dbg "WARN: Sprach-Mismatch für $(basename "$idx"): Dateiname=${code_file}, IDX=${code_idx} (Dateiname gewinnt)"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -378,8 +414,50 @@ infer_meta_for_idx() {
|
|||||||
echo "${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 ----------------
|
||||||
mux_one_mkv() {
|
mux_one_mkv() {
|
||||||
@@ -407,7 +485,31 @@ mux_one_mkv() {
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dbg "IDX files: ${#idxs[@]}"
|
# ---- 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
|
for f in "${idxs[@]}"; do dbg " - $(basename "$f")"; done
|
||||||
|
|
||||||
local out_tmp="${mkv%.*}.with-subs.tmp.mkv"
|
local out_tmp="${mkv%.*}.with-subs.tmp.mkv"
|
||||||
@@ -417,7 +519,6 @@ mux_one_mkv() {
|
|||||||
cmd=("${MKVMERGE_CMD[@]}" -o "$out_tmp" "$mkv")
|
cmd=("${MKVMERGE_CMD[@]}" -o "$out_tmp" "$mkv")
|
||||||
|
|
||||||
local added=0
|
local added=0
|
||||||
local idx=""
|
|
||||||
for idx in "${idxs[@]}"; do
|
for idx in "${idxs[@]}"; do
|
||||||
local sub="${idx%.*}.sub"
|
local sub="${idx%.*}.sub"
|
||||||
if [[ ! -f "$sub" ]]; then
|
if [[ ! -f "$sub" ]]; then
|
||||||
@@ -425,24 +526,24 @@ mux_one_mkv() {
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local meta name forcedFlag
|
local meta name forcedFlag2
|
||||||
meta="$(infer_meta_for_idx "$idx")"
|
meta="$(infer_meta_for_idx "$idx")"
|
||||||
IFS='|' read -r name forcedFlag <<<"$meta"
|
IFS='|' read -r name forcedFlag2 <<<"$meta"
|
||||||
|
|
||||||
dbg "Add VobSub: $(basename "$idx") -> name='$name', forced=$forcedFlag"
|
dbg "Add VobSub: $(basename "$idx") -> name='${name:-<none>}', forced=$forcedFlag2"
|
||||||
|
|
||||||
# IMPORTANT: do NOT override language; keep what is in the .idx
|
# Do NOT override language; keep what is in the .idx
|
||||||
if [[ -n "$name" ]]; then
|
if [[ -n "$name" ]]; then
|
||||||
cmd+=( --track-name 0:"$name" )
|
cmd+=( --track-name 0:"$name" )
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$forcedFlag" == "yes" ]]; then
|
if [[ "$forcedFlag2" == "yes" ]]; then
|
||||||
cmd+=( --forced-track 0:yes --default-track 0:no )
|
cmd+=( --forced-track 0:yes --default-track 0:no )
|
||||||
else
|
else
|
||||||
cmd+=( --forced-track 0:no --default-track 0:no )
|
cmd+=( --forced-track 0:no --default-track 0:no )
|
||||||
fi
|
fi
|
||||||
cmd+=( "$idx" )
|
|
||||||
|
|
||||||
|
cmd+=( "$idx" )
|
||||||
((++added))
|
((++added))
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -493,12 +594,9 @@ process_root() {
|
|||||||
done < <(find "$root" -type f -iname "*.mkv" -print0)
|
done < <(find "$root" -type f -iname "*.mkv" -print0)
|
||||||
|
|
||||||
log "Found MKVs: ${#mkvs[@]} under $root"
|
log "Found MKVs: ${#mkvs[@]} under $root"
|
||||||
if [[ ${#mkvs[@]} -eq 0 ]]; then
|
[[ ${#mkvs[@]} -eq 0 ]] && return 0
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
local processed=0
|
local processed=0 mkv=""
|
||||||
local mkv=""
|
|
||||||
for mkv in "${mkvs[@]}"; do
|
for mkv in "${mkvs[@]}"; do
|
||||||
mux_one_mkv "$mkv"
|
mux_one_mkv "$mkv"
|
||||||
((++processed))
|
((++processed))
|
||||||
@@ -509,7 +607,7 @@ process_root() {
|
|||||||
|
|
||||||
# ---------------- main ----------------
|
# ---------------- main ----------------
|
||||||
main() {
|
main() {
|
||||||
ROOTS=()
|
local -a ROOTS=()
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
-n|--dry-run) DRY_RUN=1; shift ;;
|
-n|--dry-run) DRY_RUN=1; shift ;;
|
||||||
@@ -527,7 +625,7 @@ main() {
|
|||||||
base_dir="$(cd "$script_dir/.." && pwd)"
|
base_dir="$(cd "$script_dir/.." && pwd)"
|
||||||
ini_file="$base_dir/settings.ini"
|
ini_file="$base_dir/settings.ini"
|
||||||
|
|
||||||
# If no CLI roots: read input_folder from settings.ini
|
# settings.ini only needed if no CLI roots
|
||||||
if [[ ${#ROOTS[@]} -eq 0 ]]; then
|
if [[ ${#ROOTS[@]} -eq 0 ]]; then
|
||||||
if [[ ! -f "$ini_file" ]]; then
|
if [[ ! -f "$ini_file" ]]; then
|
||||||
echo
|
echo
|
||||||
@@ -539,7 +637,6 @@ main() {
|
|||||||
|
|
||||||
local input_folder
|
local input_folder
|
||||||
input_folder="$(ini_get "$ini_file" "pathes" "input_folder" || true)"
|
input_folder="$(ini_get "$ini_file" "pathes" "input_folder" || true)"
|
||||||
|
|
||||||
if [[ -z "${input_folder:-}" ]]; then
|
if [[ -z "${input_folder:-}" ]]; then
|
||||||
echo
|
echo
|
||||||
echo -e "${WHITE_ON_RED} input_folder fehlt in settings.ini ([pathes]) ${NC}"
|
echo -e "${WHITE_ON_RED} input_folder fehlt in settings.ini ([pathes]) ${NC}"
|
||||||
@@ -547,10 +644,22 @@ main() {
|
|||||||
read -n 1 -s -r -p "Press any key to exit"
|
read -n 1 -s -r -p "Press any key to exit"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ROOTS=("$input_folder")
|
ROOTS=("$input_folder")
|
||||||
fi
|
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
|
detect_mkvmerge
|
||||||
|
|
||||||
echo
|
echo
|
||||||
@@ -558,11 +667,12 @@ main() {
|
|||||||
echo -e "${BLACK_ON_WHITE} Auto Add Sub Folder (VobSub) ${NC}"
|
echo -e "${BLACK_ON_WHITE} Auto Add Sub Folder (VobSub) ${NC}"
|
||||||
echo "────────────────────────────────────────────────────────────────"
|
echo "────────────────────────────────────────────────────────────────"
|
||||||
echo " "
|
echo " "
|
||||||
echo -e "${WHITE_ON_GRAY} Input Folder ${NC} ${ROOTS[*]}"
|
echo -e "${WHITE_ON_GRAY} Input Folder ${NC} ${ROOTS[*]}"
|
||||||
echo -e "${WHITE_ON_GRAY} Subs Folders ${NC} ${SUBDIR_CANDIDATES[*]}"
|
echo -e "${WHITE_ON_GRAY} Subs Folders ${NC} ${SUBDIR_CANDIDATES[*]}"
|
||||||
echo -e "${WHITE_ON_GRAY} MKVMerge ${NC} ${MKVMERGE_CMD[*]}"
|
echo -e "${WHITE_ON_GRAY} Reihenfolge ${NC} ${SUB_ORDER_RAW}"
|
||||||
echo -e "${WHITE_ON_GRAY} Dry Run ${NC} ${DRY_RUN}"
|
echo -e "${WHITE_ON_GRAY} MKVMerge ${NC} ${MKVMERGE_CMD[*]}"
|
||||||
echo -e "${WHITE_ON_GRAY} Keep Backup ${NC} ${KEEP_BAK}"
|
echo -e "${WHITE_ON_GRAY} Dry Run ${NC} ${DRY_RUN}"
|
||||||
|
echo -e "${WHITE_ON_GRAY} Keep Backup ${NC} ${KEEP_BAK}"
|
||||||
echo " "
|
echo " "
|
||||||
echo "────────────────────────────────────────────────────────────────"
|
echo "────────────────────────────────────────────────────────────────"
|
||||||
echo
|
echo
|
||||||
|
|||||||
Reference in New Issue
Block a user