From c31dcf60e95c4578f156b6638997fcaf3f41e468 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 11 Mar 2024 02:06:07 +0000 Subject: [PATCH 01/16] chore(prvt):Update private submodule --- prvt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prvt b/prvt index 1e677a1..77f7b15 160000 --- a/prvt +++ b/prvt @@ -1 +1 @@ -Subproject commit 1e677a1f0ad42cab02345a89a73234367f9e1170 +Subproject commit 77f7b156f3b0b92706967702088055768446807b -- 2.30.2 From 97ff1ce0a12ba1018a81ca873a1e63a2049304cd Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 18 Mar 2024 18:40:21 +0000 Subject: [PATCH 02/16] feat(user/rsync_tranches):Handle option args - Note: Handle by assuming non-paths are option args --- user/rsync_tranches.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/user/rsync_tranches.sh b/user/rsync_tranches.sh index 0456daa..eed19f6 100644 --- a/user/rsync_tranches.sh +++ b/user/rsync_tranches.sh @@ -7,7 +7,7 @@ function rsync_tranches() { # Desc: Runs rsync in parallel across different files size ranges # Example: rsync_tranches -avu --progress --dry-run ./SOURCE/ ./DEST/ # Depends: rsync 3.2.7 - # Version: 0.0.3 + # Version: 0.1.0 local -a rsync_opts=(); local source dest; @@ -20,7 +20,13 @@ function rsync_tranches() { shift; ;; *) - if [ -z "$source" ]; then + ## If not a file or directory, assume option + if [[ ! -f "$1" ]] && [[ ! -d "$1" ]]; then + rsync_opts+=("$1"); + shift; + fi; + ## If valid file or directory, assume source or dest path + if [[ -z "$source" ]]; then source="$1"; else dest="$1"; -- 2.30.2 From 5c0e013534dc8c8e7dea47f15eba3371ea103f08 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Tue, 19 Mar 2024 14:12:51 +0000 Subject: [PATCH 03/16] feat(user/rsync_tranches.sh):Update with manual initial delay --- user/rsync_tranches.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user/rsync_tranches.sh b/user/rsync_tranches.sh index eed19f6..bcae90e 100644 --- a/user/rsync_tranches.sh +++ b/user/rsync_tranches.sh @@ -7,7 +7,7 @@ function rsync_tranches() { # Desc: Runs rsync in parallel across different files size ranges # Example: rsync_tranches -avu --progress --dry-run ./SOURCE/ ./DEST/ # Depends: rsync 3.2.7 - # Version: 0.1.0 + # Version: 0.1.1 local -a rsync_opts=(); local source dest; @@ -43,11 +43,11 @@ function rsync_tranches() { fi; # Tranche 1: 0 to 1MiB-1 - rsync --min-size='0' --max-size='1MiB-1' "${rsync_opts[@]}" "$source" "$dest"; + rsync --min-size='0' --max-size='1MiB-1' "${rsync_opts[@]}" "$source" "$dest" & + sleep 2; # Tranche 2: 1MiB to 2MiB-1 rsync --min-size='1MiB' --max-size='2MiB-1' "${rsync_opts[@]}" "$source" "$dest" & - sleep 1; # Tranche 3: 2MiB to 4MiB-1 rsync --min-size='2MiB' --max-size='4MiB-1' "${rsync_opts[@]}" "$source" "$dest" & -- 2.30.2 From 27c0d929cf9b0ea0a2fb735978e9db36aec837dc Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Thu, 11 Apr 2024 13:55:03 +0000 Subject: [PATCH 04/16] feat(user/mp4_make480.sh):Add script to convert video to 480 res --- user/mp4_make480.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 user/mp4_make480.sh diff --git a/user/mp4_make480.sh b/user/mp4_make480.sh new file mode 100644 index 0000000..8fc75ed --- /dev/null +++ b/user/mp4_make480.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Desc: Reëncodes .mp4 videos in a specified directory. +# Usage: mp4_make480.sh [dir] +# Output: dir ./out/ +# file ./out/[files]_480.mp4 +# Version: 0.0.1 + +dir_in="$1"; +dir_out="./out"; +if [[ $# -ne 1 ]]; then echo "FATAL:No dir specified." 1>&2; exit 1; fi; +if [[ ! -d "$dir_in" ]]; then echo "FATAL:Not a dir:$dir_in" 1>&2; exit 1; fi; +mkdir -p "$dir_out"; +declare -p dir_in dir_out; # debug + +while read -r line; do + path_out=./"$dir_out"/"${line%.mp4}"_480.mp4; + declare -p line path_out; # debug + ffmpeg -nostdin -i "$dir_in" -vf "scale=-2:480" -c:a copy "$path_out"; +done < <(find . -mindepth 1 -maxdepth 1 -type f -name "*.mp4") -- 2.30.2 From 42e88e4dc65f6b2ab1d8fa6a1974fc7b795631cc Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Thu, 11 Apr 2024 16:59:32 +0000 Subject: [PATCH 05/16] feat(user/mp4_make480.sh):Use GNU parallel, split into functions --- user/mp4_make480.sh | 56 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/user/mp4_make480.sh b/user/mp4_make480.sh index 8fc75ed..abe5120 100644 --- a/user/mp4_make480.sh +++ b/user/mp4_make480.sh @@ -1,19 +1,49 @@ #!/bin/bash # Desc: Reëncodes .mp4 videos in a specified directory. -# Usage: mp4_make480.sh [dir] +# Usage: mp4_make480.sh [dir in] [dir out] +# Input: arg1 dir input dir +# arg2 dir output dir (optional) # Output: dir ./out/ # file ./out/[files]_480.mp4 -# Version: 0.0.1 +# Version: 0.1.0 +# Depends: GNU parallel 20161222, ffmpeg 4.3.6-0 +# Ref/Attrb: [1]: FFmpeg wiki: https://trac.ffmpeg.org/wiki/Scaling +declare -g dir_in dir_out; -dir_in="$1"; -dir_out="./out"; -if [[ $# -ne 1 ]]; then echo "FATAL:No dir specified." 1>&2; exit 1; fi; -if [[ ! -d "$dir_in" ]]; then echo "FATAL:Not a dir:$dir_in" 1>&2; exit 1; fi; -mkdir -p "$dir_out"; -declare -p dir_in dir_out; # debug +check_input() { + dir_in="$1"; + if [[ $# -le 0 ]]; then echo "FATAL:Insufficient args:$#" 1>&2; exit 1; fi; + if [[ $# -eq 2 ]]; then + dir_out="$2"; + else + dir_out="./out_480"; + fi; + export dir_out; + if [[ ! -d "$dir_in" ]]; then echo "FATAL:Not a dir:$dir_in" 1>&2; exit 1; fi; + mkdir -p "$dir_out"; + declare -p dir_in dir_out; # debug +}; +convert_video() { + find "$dir_in" -mindepth 1 -maxdepth 1 -type f -name "*.mp4" | \ + parallel job_ffmpeg "{}" "$path_out"; +}; +job_ffmpeg() { + path_in="$1"; + path_out="$2"; + file_in="$(basename "$path_in")"; + path_out=./"$dir_out"/"${file_in%.mp4}"_480.mp4; + opt_scale="scale=-2:480"; # See [1] + declare -p path_in path_out file_in dir_out path_out opt_scale; + ffmpeg -nostdin -i "$path_in" -vf "$opt_scale" -c:a copy "$path_out" 1>/dev/random 2>&1; +}; +export -f job_ffmpeg; -while read -r line; do - path_out=./"$dir_out"/"${line%.mp4}"_480.mp4; - declare -p line path_out; # debug - ffmpeg -nostdin -i "$dir_in" -vf "scale=-2:480" -c:a copy "$path_out"; -done < <(find . -mindepth 1 -maxdepth 1 -type f -name "*.mp4") +main() { + check_input "$@"; + convert_video; +}; + +main "$@"; + +# Author: Steven Baltakatei Sandoval +# License: GPLv3+ -- 2.30.2 From 35ccc7696545160b3d4249e0e3c5a11a37742d28 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Thu, 11 Apr 2024 19:02:55 +0000 Subject: [PATCH 06/16] feat(user/mp4_make480.sh):Do .MP4 as well as .mp4 files --- user/mp4_make480.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user/mp4_make480.sh b/user/mp4_make480.sh index abe5120..e460449 100644 --- a/user/mp4_make480.sh +++ b/user/mp4_make480.sh @@ -5,7 +5,7 @@ # arg2 dir output dir (optional) # Output: dir ./out/ # file ./out/[files]_480.mp4 -# Version: 0.1.0 +# Version: 0.1.1 # Depends: GNU parallel 20161222, ffmpeg 4.3.6-0 # Ref/Attrb: [1]: FFmpeg wiki: https://trac.ffmpeg.org/wiki/Scaling declare -g dir_in dir_out; @@ -24,7 +24,7 @@ check_input() { declare -p dir_in dir_out; # debug }; convert_video() { - find "$dir_in" -mindepth 1 -maxdepth 1 -type f -name "*.mp4" | \ + find "$dir_in" -mindepth 1 -maxdepth 1 -type f -iname "*.mp4" | \ parallel job_ffmpeg "{}" "$path_out"; }; job_ffmpeg() { -- 2.30.2 From ba98d97928b64a229d37bf405969b315adecc9da Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Thu, 11 Apr 2024 20:11:26 +0000 Subject: [PATCH 07/16] feat(user/mp4_make480.sh):Use default audio instead of copy --- user/mp4_make480.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user/mp4_make480.sh b/user/mp4_make480.sh index e460449..8799a2e 100644 --- a/user/mp4_make480.sh +++ b/user/mp4_make480.sh @@ -5,7 +5,7 @@ # arg2 dir output dir (optional) # Output: dir ./out/ # file ./out/[files]_480.mp4 -# Version: 0.1.1 +# Version: 0.1.2 # Depends: GNU parallel 20161222, ffmpeg 4.3.6-0 # Ref/Attrb: [1]: FFmpeg wiki: https://trac.ffmpeg.org/wiki/Scaling declare -g dir_in dir_out; @@ -34,7 +34,7 @@ job_ffmpeg() { path_out=./"$dir_out"/"${file_in%.mp4}"_480.mp4; opt_scale="scale=-2:480"; # See [1] declare -p path_in path_out file_in dir_out path_out opt_scale; - ffmpeg -nostdin -i "$path_in" -vf "$opt_scale" -c:a copy "$path_out" 1>/dev/random 2>&1; + ffmpeg -nostdin -i "$path_in" -vf "$opt_scale" "$path_out"; }; export -f job_ffmpeg; -- 2.30.2 From 8e83a444c372365bc923119a9d84885ea6567bf6 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 29 Apr 2024 17:45:45 +0000 Subject: [PATCH 08/16] fix(user/mp4_make480.sh):Use default audio conversion --- user/mp4_make480.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user/mp4_make480.sh b/user/mp4_make480.sh index e460449..8799a2e 100644 --- a/user/mp4_make480.sh +++ b/user/mp4_make480.sh @@ -5,7 +5,7 @@ # arg2 dir output dir (optional) # Output: dir ./out/ # file ./out/[files]_480.mp4 -# Version: 0.1.1 +# Version: 0.1.2 # Depends: GNU parallel 20161222, ffmpeg 4.3.6-0 # Ref/Attrb: [1]: FFmpeg wiki: https://trac.ffmpeg.org/wiki/Scaling declare -g dir_in dir_out; @@ -34,7 +34,7 @@ job_ffmpeg() { path_out=./"$dir_out"/"${file_in%.mp4}"_480.mp4; opt_scale="scale=-2:480"; # See [1] declare -p path_in path_out file_in dir_out path_out opt_scale; - ffmpeg -nostdin -i "$path_in" -vf "$opt_scale" -c:a copy "$path_out" 1>/dev/random 2>&1; + ffmpeg -nostdin -i "$path_in" -vf "$opt_scale" "$path_out"; }; export -f job_ffmpeg; -- 2.30.2 From ddcb78b71ab49dc48d484a63bba85a4a827b71cd Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 29 Apr 2024 22:26:56 +0000 Subject: [PATCH 09/16] feat(user/transcribe_whisper.sh):Add mp3 transcription script --- user/transcribe_whisper.sh | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 user/transcribe_whisper.sh diff --git a/user/transcribe_whisper.sh b/user/transcribe_whisper.sh new file mode 100644 index 0000000..a6fdad5 --- /dev/null +++ b/user/transcribe_whisper.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Usage: ./transcribe_whisper.sh 3 +# Input: arg1 CUDA graphics card number (zero-indexed) +# Version: 0.0.1 +# Depends: whisper ( https://github.com/openai/whisper ) + +yell() { echo "$0: $*" >&2; } # print script path and all args to stderr +die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status +must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails +checkInt() { + # Desc: Checks if arg is integer + # Usage: checkInt arg + # Input: arg: integer + # Output: - return code 0 (if arg is integer) + # - return code 1 (if arg is not integer) + # Example: if ! checkInt $arg; then echo "not int"; fi; + # Version: 0.0.2 + local returnState + + #===Process Arg=== + if [[ $# -ne 1 ]]; then + die "ERROR:Invalid number of arguments:$#"; + fi; + + RETEST1='^[0-9]+$'; # Regular Expression to test + if [[ ! "$1" =~ $RETEST1 ]] ; then + returnState="false"; + else + returnState="true"; + fi; + + #===Determine function return code=== + if [ "$returnState" = "true" ]; then + return 0; + else + return 1; + fi; +} # Checks if arg is integer +main() { + cuda_num="$1"; + if ! checkInt "$cuda_num"; then die "FATAL:No graphics card selected."; fi; + while read -r line; do + echo "STATUS:Processing:$line" 1>&2; + SECONDS=0; + dir_out="$(dirname "$line"; )"; + ftmp="$line".tmp; + #declare -p line dir_out ftmp; # debug + if [[ ! -f "$ftmp" ]] && \ + [[ ! -f "${line%.*}".srt ]] && \ + [[ ! -f "${line%.*}".vtt ]] && \ + [[ ! -f "${line%.*}".txt ]] && \ + [[ ! -f "${line%.*}".tsv ]] && \ + [[ ! -f "${line%.*}".json ]]; then + touch "$ftmp"; + yell "STATUS:No conflicts detected."; + else + yell "STATUS:Skipping:$line"; + continue; + fi; + whisper "$line" \ + --model large-v3 \ + --output_format all \ + --output_dir "$dir_out" \ + --language en \ + --device cuda:"$cuda_num" && \ + ( + echo "STATUS:$SECONDS:Finished:$line" 1>&2; + rm "$ftmp"; # remove .tmp file + ); + done < <(find . -type f -name "*.mp3" | shuf ) +}; # main program + +main "$@"; + + -- 2.30.2 From e51dd5e563c61b4652c3ec1fbbba9eae60f69785 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Tue, 30 Apr 2024 00:10:32 +0000 Subject: [PATCH 10/16] feat(user/mw_get_audiobook_chapters.sh): --- user/mw_get_audiobook_chapters.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 user/mw_get_audiobook_chapters.sh diff --git a/user/mw_get_audiobook_chapters.sh b/user/mw_get_audiobook_chapters.sh new file mode 100644 index 0000000..191b1e7 --- /dev/null +++ b/user/mw_get_audiobook_chapters.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Print mediawiki code chapter headings and timecodes from audiobook file +# Usage: mw_get_audiobook_chapters.sh [file] ([int offset]) +# Input: arg1: path to audiobook +# arg2: offset for chapter numbers +# Example: mw_get_audiobook_chapters.sh input.m4b +# Version: 0.0.1 + + +fin="$1"; +n=0 +while read -r line; do + if [[ $((n % 2)) -eq 0 ]]; then + chap="$(sed -e 's/^0*//' <<< "$line")"; # remove leading zero + printf "====%s====\n" "$chap"; # output wikicode header + elif [[ $((n % 2)) -eq 1 ]]; then + printf "%s\n\n" "$line"; # output timecode + fi; + #declare -p n line; # debug + ((n++)); +done < <(ffprobe -i "$fin" -print_format json -show_chapters -sexagesimal | \ + jq \ + -r '.chapters[] | (.tags.title) + "\n" + (.start_time | gsub("\\.\\d+$"; "") )' + ); -- 2.30.2 From 1deac7e514800559fc8b8bdd7cf3db083d8d5f9e Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 6 May 2024 04:37:41 +0000 Subject: [PATCH 11/16] feat(user/transcribe_whisper.sh):Apply to various media files --- user/transcribe_whisper.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) mode change 100644 => 100755 user/transcribe_whisper.sh diff --git a/user/transcribe_whisper.sh b/user/transcribe_whisper.sh old mode 100644 new mode 100755 index a6fdad5..68756c7 --- a/user/transcribe_whisper.sh +++ b/user/transcribe_whisper.sh @@ -1,9 +1,15 @@ #!/bin/bash +# Desc: Runs OpenAI Whisper on working directory media files # Usage: ./transcribe_whisper.sh 3 # Input: arg1 CUDA graphics card number (zero-indexed) -# Version: 0.0.1 +# Version: 0.1.0 # Depends: whisper ( https://github.com/openai/whisper ) +# Find settings +firegex=".+\(aac\|aif\|aiff\|flac\|m4a\|m4b\|mkv\|mp3\|mp4\|ogg\|opus\|wav\)$"; # update according to `find . -type f | grep -Eo "\.([[:alnum:]])+$" | sort -u` +fsize="10k"; # default: minimum "10k" +fdepth="10"; # find depth + yell() { echo "$0: $*" >&2; } # print script path and all args to stderr die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status must() { "$@" || die "cannot $*"; } # runs args as command, reports args if command fails @@ -36,8 +42,21 @@ checkInt() { return 1; fi; } # Checks if arg is integer +find_flist() { + # Desc: print file list to stdout via `find` using script parameters + # Input: arg1: path to dir + # var: fdepth find depth + # var: firegex pattern find iregex + # var: fsize find size + if [[ ! -d "$1" ]]; then return 1; fi; + must find "$1" -maxdepth "$fdepth" -type f -iregex "$firegex" -size +"$fsize"; +}; # print file list to stdout from dir with script parameters main() { + # Input: var: fdepth (find_flist) find depth + # var: firegex (find_flist) pattern find iregex + # var: fsize (find_flist) find size cuda_num="$1"; + dir_in="$(pwd)"; if ! checkInt "$cuda_num"; then die "FATAL:No graphics card selected."; fi; while read -r line; do echo "STATUS:Processing:$line" 1>&2; @@ -67,8 +86,9 @@ main() { echo "STATUS:$SECONDS:Finished:$line" 1>&2; rm "$ftmp"; # remove .tmp file ); - done < <(find . -type f -name "*.mp3" | shuf ) + done < <(find_flist "$dir_in"); }; # main program +export -f yell die must find_flist; main "$@"; -- 2.30.2 From 938954605f7af4f4c1732d66c692fbe631bf7a90 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 6 May 2024 04:40:37 +0000 Subject: [PATCH 12/16] feat(user/transcribe_whisper.sh):Specify dir_in via arg --- user/transcribe_whisper.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/user/transcribe_whisper.sh b/user/transcribe_whisper.sh index 68756c7..39944ce 100755 --- a/user/transcribe_whisper.sh +++ b/user/transcribe_whisper.sh @@ -1,8 +1,9 @@ #!/bin/bash # Desc: Runs OpenAI Whisper on working directory media files -# Usage: ./transcribe_whisper.sh 3 -# Input: arg1 CUDA graphics card number (zero-indexed) -# Version: 0.1.0 +# Usage: ./transcribe_whisper.sh [dir] 3 +# Input: arg1 input dir +# arg2 CUDA graphics card number (zero-indexed) +# Version: 0.2.0 # Depends: whisper ( https://github.com/openai/whisper ) # Find settings @@ -52,11 +53,13 @@ find_flist() { must find "$1" -maxdepth "$fdepth" -type f -iregex "$firegex" -size +"$fsize"; }; # print file list to stdout from dir with script parameters main() { - # Input: var: fdepth (find_flist) find depth - # var: firegex (find_flist) pattern find iregex - # var: fsize (find_flist) find size - cuda_num="$1"; - dir_in="$(pwd)"; + # Input: arg1: dir_in input dir + # arg2 cuda_num cuda GPU index + # var: fdepth (find_flist) find depth + # var: firegex (find_flist) pattern find iregex + # var: fsize (find_flist) find size + dir_in="$1"; + cuda_num="$2"; if ! checkInt "$cuda_num"; then die "FATAL:No graphics card selected."; fi; while read -r line; do echo "STATUS:Processing:$line" 1>&2; -- 2.30.2 From 6801e454e89cb21ea0ccb2a9d0b4959f46440b0c Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Mon, 6 May 2024 20:13:29 +0000 Subject: [PATCH 13/16] feat(user/transcribe_whisper.sh):Shuffle input files --- user/transcribe_whisper.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/user/transcribe_whisper.sh b/user/transcribe_whisper.sh index 39944ce..00dc313 100755 --- a/user/transcribe_whisper.sh +++ b/user/transcribe_whisper.sh @@ -3,7 +3,7 @@ # Usage: ./transcribe_whisper.sh [dir] 3 # Input: arg1 input dir # arg2 CUDA graphics card number (zero-indexed) -# Version: 0.2.0 +# Version: 0.4.0 # Depends: whisper ( https://github.com/openai/whisper ) # Find settings @@ -54,7 +54,7 @@ find_flist() { }; # print file list to stdout from dir with script parameters main() { # Input: arg1: dir_in input dir - # arg2 cuda_num cuda GPU index + # arg2: cuda_num cuda GPU index # var: fdepth (find_flist) find depth # var: firegex (find_flist) pattern find iregex # var: fsize (find_flist) find size @@ -89,7 +89,7 @@ main() { echo "STATUS:$SECONDS:Finished:$line" 1>&2; rm "$ftmp"; # remove .tmp file ); - done < <(find_flist "$dir_in"); + done < <(find_flist "$dir_in" | shuf); }; # main program export -f yell die must find_flist; -- 2.30.2 From 9a6c59b707c794c0c98af2653638d9b4d2a243b4 Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Wed, 15 May 2024 23:25:42 +0000 Subject: [PATCH 14/16] fix(user/bk-copy-rand-music):Follow direct symlinks to files --- user/bk-copy-rand-music | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user/bk-copy-rand-music b/user/bk-copy-rand-music index 7a7d299..597a663 100755 --- a/user/bk-copy-rand-music +++ b/user/bk-copy-rand-music @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Desc: Copies random audio files # Usage: bk-copy-rand-music [dir SOURCE] [dir DEST] [int DURATION] ([int BYTES]) -# Version: 0.4.0 +# Version: 0.4.1 # Depends: BK-2020-03: bkshuf v0.1.0 declare -Ag appRollCall # Associative array for storing app status @@ -463,7 +463,7 @@ main() { path_candfile="$line"; # path of candidate file ### Check size - siz_cand="$(du -b "$path_candfile" | awk '{ print $1 }')"; # size in bytes + siz_cand="$(du -Lb "$path_candfile" | awk '{ print $1 }')"; # size in bytes if ! checkInt "$siz_cand"; then continue; fi; # reject if [[ "$((siz + siz_cand))" -gt "$siz_dest" ]]; then continue; fi; # reject if [[ "$siz_cand" -lt "$min_file_size" ]]; then continue; fi; # reject -- 2.30.2 From 9a4c8b8875d41858afb24e33fd7672fb8771645b Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Wed, 29 May 2024 03:20:13 +0000 Subject: [PATCH 15/16] feat(user/mp3s_to_mkv.sh):Enable automatic chapter metadata creation --- user/mp3s_to_mkv.sh | 79 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/user/mp3s_to_mkv.sh b/user/mp3s_to_mkv.sh index 144e34b..7a89ed8 100755 --- a/user/mp3s_to_mkv.sh +++ b/user/mp3s_to_mkv.sh @@ -1,9 +1,10 @@ #!/bin/bash -# Desc: Converts a directory of mp3s into a single mkv file +# Desc: Converts a directory of mp3s into a single mkv file with chapters # Usage: mp3s_to_mkv.sh [DIR in] [DIR out] [BITRATE] # Example: mp3s_to_mkv.sh ./dir_source ./dir_output 48k -# Depends: GNU Coretils 8.32 (date) -# Version: 0.0.2 +# Depends: GNU Coreutils 8.32 (date), ffmpeg, ffprobe +# Ref/Attrib: [1] FFmpeg Formats Documentation https://ffmpeg.org/ffmpeg-formats.html#toc-Metadata-2 +# Version: 0.1.0 # plumbing opus_bitrate="$3"; # e.g. "48k" @@ -14,6 +15,7 @@ dir_out="$(readlink -f "$2")"; file_flist="$dir_tmp"/"$script_rundate"..flist.txt; file_out_mkv="$dir_out"/output.mkv; file_albumart="$dir_out"/albumart.png; +file_chapters="$dir_tmp"/"$script_rundate"..chapters.txt; yell() { echo "$0: $*" >&2; } # print script path and all args to stderr die() { yell "$*"; exit 111; } # same as yell() but non-zero exit status @@ -35,43 +37,89 @@ EOF } # Display information on how to use this script. check_depends() { if ! command -v ffmpeg; then show_usage; die "FATAL:Missing ffmpeg."; fi; + if ! command -v ffprobe; then show_usage; die "FATAL:Missing ffprobe."; fi; }; # check dependencies check_plumbing() { if [[ $# -ne 3 ]]; then show_usage; die "FATAL:Invalid arg count:$#"; fi; if [[ ! -d "$dir_in" ]]; then show_usage; die "FATAL:Not a dir:$dir_in"; fi; if [[ ! -d "$dir_out" ]]; then mkdir -p "$2"; fi; }; # check arguments -build_filelist() { +get_media_length() { + # Use ffprobe to get media container length in seconds (float) + # Usage: get_media_length arg1 + # Input: arg1: path to file + # Output: stdout: seconds (float) + # Depends: ffprobe 4.1.8 + # BK-2020-03: die() + local file_in + file_in="$1"; + if [[ ! -f $file_in ]]; then + die "ERROR:Not a file:$file_in"; + fi; + ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file_in"; +} # Get media container length in seconds via stdout +build_filelist_and_chapters() { # Depends: var dir_tmp temporary directory # var dir_in input dir # var file_flist path file list - # Output: file: $file_flist list of mp3 files for ffmpeg + # Output: file: $file_flist list of mp3 files for ffmpeg + # file: $file_chapters chapters file for ffmpeg # Change directory to input dir pushd "$dir_in" || die "FATAL:Directory error:$(pwd)"; + + local chapter_start=0; + local chapter_end; + local duration; + + + # Initialize chapter ffmetadata file. See [1]. + { + echo ";FFMETADATA1"; + } >> "$file_chapters"; + - while read -r line; do - yell "$(printf "file '%s'\n" "${line#./}")"; - printf "file '%s'\n" "${line#./}" >> "$file_flist"; - done < <(find "$dir_in" -type f -iname "*.mp3" | sort); + find "$dir_in" -type f -iname "*.mp3" | sort | while read -r line; do + local filename="${line#./}"; + yell "$(printf "file '%s'\n" "$filename")"; + printf "file '%s'\n" "$filename" >> "$file_flist"; + + # Get duration of the current file + duration=$(get_media_length "$filename"); + chapter_end=$(echo "$chapter_start + $duration" | bc -l); + + # Write chapter info + { + echo "[CHAPTER]"; + echo "TIMEBASE=1/1000"; + echo "START=$(echo "scale=0; $chapter_start * 1000" | bc -l)"; + echo "END=$(echo "scale=0; $chapter_end * 1000" | bc -l)"; + echo "title=$(basename "$filename" .mp3)"; + } >> "$file_chapters"; + + chapter_start=$chapter_end + done # Return to original dir popd || die "FATAL:Directory error:$(pwd)"; - -}; # build file list for ffmpeg +}; # build file list and chapters for ffmpeg ffmpeg_convert() { # Depends: var dir_tmp # dir_in # dir_out - # Input: file $file_flist list of mp3 files for ffmpeg + # Input: file $file_flist list of mp3 files for ffmpeg + # file $file_chapters chapters file for ffmpeg # Change directory to input dir pushd "$dir_in" || die "FATAL:Directory error:$(pwd)"; # Concatenate mp3 files into a single WAV file - # # Convert WAV to 48 kbps opus mkv file + # Convert WAV to 48 kbps opus mkv file ffmpeg -nostdin -f concat -safe 0 -i "$file_flist" -c:a pcm_s24le -rf64 auto -f wav - | \ - ffmpeg -i - -c:a libopus -b:a "$opus_bitrate" "$file_out_mkv"; + ffmpeg -i - -i "$file_chapters" \ + -map_metadata 1 -map_chapters 1 \ + -map 0 -c:a libopus -b:a "$opus_bitrate" \ + "$file_out_mkv"; # Return to original dir popd || die "FATAL:Directory error:$(pwd)"; @@ -85,10 +133,9 @@ save_albumart() { main() { check_depends && yell "DEBUG:check_depends OK"; check_plumbing "$@" && yell "DEBUG:check_plumbing OK"; - build_filelist "$@" && yell "DEBUG:build_filelist OK"; + build_filelist_and_chapters "$@" && yell "DEBUG:build_filelist_and_chapters OK"; ffmpeg_convert "$@" && yell "DEBUG:ffmpeg_convert OK"; save_albumart "$@" && yell "DEBUG:save_albumart OK"; }; # main program main "$@"; - -- 2.30.2 From 4b807ed1c506a488dd2b9b33601585e0a42b872c Mon Sep 17 00:00:00 2001 From: Steven Baltakatei Sandoval Date: Wed, 29 May 2024 03:45:42 +0000 Subject: [PATCH 16/16] chore(user/mw_get_audiobook_chapters.sh):Clean up desc --- user/mw_get_audiobook_chapters.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) mode change 100644 => 100755 user/mw_get_audiobook_chapters.sh diff --git a/user/mw_get_audiobook_chapters.sh b/user/mw_get_audiobook_chapters.sh old mode 100644 new mode 100755 index 191b1e7..2d1ff38 --- a/user/mw_get_audiobook_chapters.sh +++ b/user/mw_get_audiobook_chapters.sh @@ -2,9 +2,8 @@ # Print mediawiki code chapter headings and timecodes from audiobook file # Usage: mw_get_audiobook_chapters.sh [file] ([int offset]) # Input: arg1: path to audiobook -# arg2: offset for chapter numbers # Example: mw_get_audiobook_chapters.sh input.m4b -# Version: 0.0.1 +# Version: 0.1.0 fin="$1"; -- 2.30.2