From d74f23134f6f6e39296c08d1d0fb288f3be76795 Mon Sep 17 00:00:00 2001 From: Andrea Rogers Date: Fri, 29 Dec 2023 17:59:20 -0600 Subject: [PATCH] [NEW] psp-enc: Convert batches of videos to native PSP AVC --- scripts/psp-enc | 433 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100755 scripts/psp-enc diff --git a/scripts/psp-enc b/scripts/psp-enc new file mode 100755 index 0000000..1e4cb35 --- /dev/null +++ b/scripts/psp-enc @@ -0,0 +1,433 @@ +#!/usr/bin/env bash + +progname=$(basename $0) + +read -d '' _usage <&2) +eval set -- "$parsed" + +var_file=0 +do_subs=0 +no_subs=1 +verbose=0 +dry_run=0 +get_fonts=0 + +while :; do + case "$1" in + -o | --out-dir) + if [ $var_file -eq 1 ]; then + (echo 'Can'\''t use both -I and -o!' >&2) + (usage >&2) + exit 1 + fi + out_dir="$2" + shift 2 + ;; + -s | --subtitles) + do_subs=1 + no_subs=0 + shift + ;; + -n | --no-subtitles) + do_subs=0 + no_subs=1 + shift + ;; + -I | --ingest-def) + . "$2" + var_file=1 + shift 2 + ;; + -l | --lang) + lang="$1" + shift 2 + break + ;; + -v) + ((verbose++)) + shift + ;; + -e | --extract-fonts) + get_fonts=1 + shift + ;; + -d | --dry-run) + verbose=2 + dry_run=1 + shift + ;; + -h | --help) + usage + exit 0 + ;; + --) + shift + break + ;; + *) + (usage >&2) + exit 1 + ;; + esac +done + +redf() { tput setaf 1; } +normf() { tput setaf 9; } + +[ $var_file -eq 0 ] && ingest_dir="$1" +if [ ! -d "$ingest_dir" ]; then + (echo $(redf)"[ERR]: \"$ingest_dir\" does not exist!" >&2) + (normf >&2) + exit 1 +fi + +[ -z ${out_dir+nz} ] && out_dir="$ingest_dir"'_out' +if [ ! -d "$out_dir" ]; then + mkdir "$out_dir" + mkdir_s=$? + if [ $mkdir_s -ne 0 ]; then + (echo $(redf)"[ERR]: Couldn't create output directory named \"$out_dir\"" >&2) + (normf >&2) + exit $mkdir_s + fi +fi + +[ -z ${lang+nz} ] && lang='eng' + +noext() { basename "$@" | sed 's/\.[a-z0-9]\+$//g'; } + +werker() { + #proberx='^\s*Stream\s\+#[0-9]\+:[0-9]\+.*:\s*' + proberx='^\s*Stream\s\+#[0-9]\+:\([0-9]\+\)(\{0,1\}\(.*\))\{0,1\}:\s*' + langrx='^\s*Stream\s\+#[0-9]\+:\([0-9]\+\)(\{0,1\}\('"$lang"'\))\{0,1\}:\s*' + undlangrx='^\s*Stream\s\+#[0-9]\+:\([0-9]\+\)(\{0,1\}\(und\))\{0,1\}:\s*' + + for file in $ingest_dir/*; do + noextname=$(noext "$file") + #subfile="$out_dir/PSP_$noextname.ass" + subfile="$out_dir/$(hostname).$(date +%s).ass" + avcfile="$out_dir/PSP_$noextname.mp4" + thmfile="$out_dir/PSP_$noextname.thm" + + nfo=$(ffprobe "$file" 2>&1) + nfostat=$? + if [ $nfostat -ne 0 ]; then + (echo <<<"$nfo" >&2) + exit $nfostat + fi + unset nfostat + + [ -e "$thmfile" ] && continue + [ -e "$avcfile" ] && continue + + audiotracks=$(grep "$proberx"'Audio' <<<$nfo) + videotracks=$(grep "$proberx"'Video' <<<$nfo) + subtracks=$(grep "$proberx"'Subtitle' <<<$nfo) + unset nfo + + n_audiotracks=$(wc -l <<<$audiotracks) + n_videotracks=$(wc -l <<<$videotracks) + n_subtracks=$(wc -l <<<$subtracks) + + # Useful for youtube-dl downloads + if [ $n_audiotracks -eq 0 ]; then + (echo "[WARN]: Skipping \"$file\" due to missing audio stream..." >&2) + continue + elif [ $n_videotracks -eq 0 ]; then + (echo "[WARN]: Skipping \"$file\" due to missing video stream..." >&2) + continue + fi + [ $verbose -gt 0 ] && (echo -e "[LOG]: Processing file \"$file\"" >&2) + + foreign_audiotracks=$(grep -v "$langrx"Audio <<<$audiotracks | grep -v "$undlangrx"Audio) + und_audiotracks=$(grep "$undlangrx"Audio <<<$audiotracks) + lang_audiotracks=$(grep "$langrx"Audio <<<$audiotracks) + foreign_audiotracks_byid=$(sed "s/$proberx.*$/\1/" <<<$foreign_audiotracks) + und_audiotracks_byid=$(sed "s/$proberx.*$/\1/" <<<$und_audiotracks) + lang_audiotracks_byid=$(sed "s/$langrx.*$/\1/" <<<$lang_audiotracks) + + foreign_subtracks=$(grep -v "$langrx"Subtitle <<<$subtracks | grep -v "$undlangrx"Subtitle) + und_subtracks=$(grep "$undlangrx"Subtitle <<<$subtracks) + lang_subtracks=$(grep "$langrx"Subtitle <<<$subtracks) + foreign_subtracks_byid=$(sed "s/$proberx.*$/\1/" <<<$foreign_subtracks) + und_subtracks_byid=$(sed "s/$proberx.*$/\1/" <<<$und_subtracks) + lang_subtracks_byid=$(sed "s/$langrx.*$/\1/" <<<$lang_subtracks) + + + if [ $verbose -gt 1 ]; then + (echo "[DBG]: AUDIO INFO:" >&2) + (echo "[DBG]: ===========" >&2) + (echo -e "[DBG]: all audio:\n$audiotracks" >&2) + (echo -e "[DBG]: foreign audio:\n$foreign_audiotracks" >&2) + (echo -e "[DBG]: lang audio ($lang):\n$lang_audiotracks" >&2) + (echo -e "[DBG]: undef audio ($lang):\n$und_audiotracks" >&2) + (echo -e "[DBG]: [BY ID] lang audio ($lang):\n$lang_audiotracks_byid" >&2) + (echo -e "[DBG]: [BY ID] undef audio ($lang):\n$und_audiotracks_byid" >&2) + (echo -e "[DBG]: [BY ID] foreign audio ($lang):\n$foreign_audiotracks_byid" >&2) + + (echo "[DBG]: SUBTITLE INFO:" >&2) + (echo "[DBG]: ==============" >&2) + (echo -e "[DBG]: all subtitle tracks:\n$subtracks" >&2) + (echo -e "[DBG]: foreign subtitle tracks:\n$foreign_subtracks" >&2) + (echo -e "[DBG]: lang subtitle tracks ($lang):\n$lang_subtracks" >&2) + (echo -e "[DBG]: undef subtitle tracks ($lang):\n$und_subtracks" >&2) + (echo -e "[DBG]: [BY ID] lang subtitle tracks ($lang):\n$lang_subtracks_byid" >&2) + (echo -e "[DBG]: [BY ID] undef subtitle tracks ($lang):\n$und_subtracks_byid" >&2) + (echo -e "[DBG]: [BY ID] foreign subtitle tracks ($lang):\n$foreign_subtracks_byid" >&2) + fi + + # Set the default audio stream for FFmpeg mapping + audiostream=$(sed "s/$proberx.*$/\1/" <<<$audiotracks | head -n 1) + audiostream_offset=$audiostream + atype='first' + + # Try to select the correct audio track for the selected language. + if [ $n_audiotracks -eq 1 ]; then + (echo "[WARN]: Only one audio track found, using that one..." >&2) + elif [ -n "$lang_audiotracks" ]; then + audiostream=$(head -n 1 <<<$lang_audiotracks_byid) + atype='lang' + [ $(wc -l <<<$lang_audiotracks) -gt 1 ] && \ + (echo "[WARN]: More than one audio track in \"$lang\" found, using first one found." >&2) + atype='lang_first' + elif [ -z "$lang_audiotracks" ]; then + (echo "[WARN]: No audio track in \"$lang\" found!" >&2) + if [ -n "$und_audiotracks" ]; then + audiostream=$(head -n 1 <<<$und_audiotracks_byid) + (echo "[LOG]: Using first undefined audio track, in hope that it is in the right language." >&2) + atype='undef' + fi + elif [ -n "$foreign_audiotracks" -a "$atype" != "undef" ]; then + [ $no_subs -gt 0 ] && do_subs=1 + audiostream=$(head -n 1 <<<$foreign_audiotracks_byid) + atype='foreign' + fi + ((audiostream -= audiostream_offset)) + [ $verbose -gt 0 ] && (echo "[LOG]: Using audio track $audiostream." >&2) + + subvf="" + # Warn for missing subs, when baking is requested. + if [ $do_subs -eq 1 ]; then + if [ $n_subtracks -eq 0 ]; then + (echo "[WARN]: No subtitles found in \"$file\" to bake into video..." >&2) + else + subtrack=$(sed "s/$proberx.*$/\1/" <<<$subtracks | head -n 1) + subtrack_offset=$subtrack + if [ $n_subtracks -eq 1 ]; then + (echo "[WARN]: Only one subtitle track found, using that one..." >&2) + elif [ -n "$lang_subtracks" ]; then + subtrack=$(head -n 1 <<<$lang_subtracks_byid) + [ $(wc -l <<<$lang_subtracks) -gt 1 ] && \ + (echo "[WARN]: More than one subtitle track in \"$lang\" found, using first one found." >&2) + fi + ((subtrack -= subtrack_offset)) + subvf="" + fi + fi + + if [ $dry_run -gt 0 ]; then continue; fi + + w=160 + h=120 + ffmpeg -i "$file" \ + -vf "scale=$w:$h:force_original_aspect_ratio=decrease,pad=$w:$h:(ow-iw)/2:(oh-ih)/2" \ + -f image2 \ + -vframes 1 \ + -r 1 \ + -s $w'x'$h \ + -an \ + "$thmfile" \ + ; + + [ -e "$avcfile" ] && continue + + #lnfile=.$(hostname).$(date +%s) + #ln -s "$file" "$lnfile" + + if [ $do_subs -gt 0 ]; then + ffmpeg -i "$file" \ + -an \ + -vn \ + -c:s ass \ + -map 0:s:$subtrack \ + "$subfile" \ + ; + subvf=",subtitles=$subfile:si=0[v]" + fi + #continue + + #w=720 + #h=480 + w=480 + h=270 + ffmpeg -i "$file" \ + -vf "scale=$w:$h:force_original_aspect_ratio=decrease,pad=$w:$h:(ow-iw)/2:(oh-ih)/2$subvf" \ + -c:a aac \ + -b:a 192k \ + -ar 48000 \ + -ac 2 \ + -map 0:a:$audiostream \ + -c:v libx264 \ + -movflags +faststart \ + -bufsize 2M \ + -profile:v main -level 3 -refs 3 -b-pyramid none -tune film -preset veryslow -flags +mv4+aic \ + -pix_fmt yuv420p \ + -b:v 2000k \ + -maxrate 5000k \ + -s $w'x'$h \ + -aspect 16:9 \ + -metadata title="$noextname encoded by tuxlovesyou" \ + -map 0:v:0 \ + "$avcfile" \ + ; + ffstat=$? + # Haven't gotten this to work yet, had to resort to bash kludge... + #-map m:language:$lang \ + #-vf "scale=$w:$h:force_original_aspect_ratio=decrease,pad=$w:$h:(ow-iw)/2:(oh-ih)/2,subtitles=$lnfile:si=$subtrack" \ + + if [ $ffstat -eq 0 ]; then + rm -vf "$subfile" + continue + #unlink $file + #unlink "$lnfile" + else + (echo $(redf)"[ERR]: FFmpeg exited with code $ffstat while transforming \"$file\" into \"$avcfile\"" >&2) + (normf >&2) + rm -vf "$avcfile" + rm -vf "$thmfile" + rm -vf "$subfile" + #unlink "$lnfile" + exit $ffstat + fi + done +} + +extract_fonts() { + ingest_dir=$(realpath "$ingest_dir") + out_dir=$(realpath "$out_dir") + font_dir="$out_dir/fonts" + + mkdir "$font_dir" + cd "$font_dir" + + attachment_rx='^Attachment\s\+ID\s\+\([0-9]\+\):.*$' + for file in $ingest_dir/*.mkv; do + n_attach=$(mkvmerge -i "$file" | grep "$attachment_rx" | sed "s/$attachment_rx/\1/" | tail -n 1) + attachment_ids=$(eval echo {1..$n_attach}) + mkvextract attachments "$file" $attachment_ids + done + + ls -1 *.ttf *.TTF *.otf *.OTF | wc -l + command -v fc-cache + if [ $? -eq 0 ]; then + command -v md5sum + if [ $? -eq 0 ]; then + md5sum * | \ + sort | \ + awk 'BEGIN{lasthash = ""} $1 == lasthash {print $2} {lasthash = $1}' | \ + xargs rm -v \ + ; + fi + ls -1 *.ttf *.TTF *.otf *.OTF | wc -l + + mkdir -p ~/.local/share/fonts/opentype + cp -v *.ttf *.TTF ~/.local/share/fonts/ + cp -v *.otf *.OTF ~/.local/share/fonts/opentype/ + + fc-cache -f -v + fi + + cd - +} + +if [ $get_fonts -gt 0 ]; then + extract_fonts + cat <<'EODONE' +.___________________________________________________________________________. +| _____ _ _ _ _ _ | +| | ___|__ _ __ | |_ ___ _____ _| |_ _ __ __ _ ___| |_ ___ __| | | | +| | |_ / _ \| '_ \| __/ __| / _ \ \/ / __| '__/ _` |/ __| __/ _ \/ _` | | | +| | _| (_) | | | | |_\__ \ | __/> <| |_| | | (_| | (__| || __/ (_| |_| | +| |_| \___/|_| |_|\__|___/ \___/_/\_\\__|_| \__,_|\___|\__\___|\__,_(_) | +`===========================================================================" + +Now time to encode for real! +EODONE + exit +fi + +werker