#!/bin/sh
# SPDX-License-Identifier: CC0-1.0
# VideoStore Codec by Digital Assistant
# 56 years after UNIX epoch
# v0.0.3
# Single-hash comments written or verified by human.
## Double-hash comments (other than this one) written by bot, not verified.
# More info -
# Direct nostr link: View quoted note →
# Legacy web link:
# Internet Archive legacy web backup:
set -e
VERSION=$(sed -n '6s/^# //p' "$0" 2>/dev/null || echo "unknown")
## --- Localization Setup ---
export TEXTDOMAIN="videostore-codec"
export TEXTDOMAINDIR="${TEXTDOMAINDIR:-/usr/share/locale}"
## Fallback function if gettext is not installed on the system
if ! command -v gettext >/dev/null 2>&1; then
gettext() {
printf "%s" "$1"
}
fi
## Ensure consistent numeric and sorting behavior across POSIX shells
export LC_NUMERIC=C
export LC_COLLATE=C
START_TIME=$(date +%s 2>/dev/null) || { echo "$(gettext "Warning: Process timer maybe unavailable on this system.")"; START_TIME=""; }
usage() {
exit_code="${1:-1}"
## If exiting with an error, redirect help text to stderr to avoid pipe corruption
[ "$exit_code" -ne 0 ] && exec 1>&2
printf "$(gettext "VideoStore Codec %s\n")" "$VERSION"
echo "$(gettext "Usage:")"
echo "$(gettext " Encoding Mode (Directory -> Video/Images):")"
printf "$(gettext " %s -e <input_dir> <output_dir> [options]\n")" "$0"
echo "$(gettext " Add par2 recovery files, pack tarball, encode video")"
echo "$(gettext " (default filename: <input_dir>.tar.mkv)")"
echo ""
echo "$(gettext " Decoding Mode (Video -> Restored Tarball):")"
printf "$(gettext " %s -d <input_video> <output_dir> [options]\n")" "$0"
echo "$(gettext " Should also accept an input directory of still frame images")"
echo ""
echo "$(gettext "Options:")"
echo "$(gettext " -o DIR Use a permanent directory for intermediate files (bypasses mktemp)")"
echo "$(gettext " -h Show this help message")"
echo "$(gettext "Decoder only:")"
echo "$(gettext " -b Bin-only mode: stop at .bin extraction if input is video,")"
echo "$(gettext " start reassembler from .bin directory if input is directory")"
echo "$(gettext " -g NUM Grayscale mode (1=on, 0=off) - If not set, may interrupt to prompt")"
echo "$(gettext " (Set 1 unless payload uses color)")"
echo "$(gettext "Encoder only:")"
echo "$(gettext " -i Output image folders (dm/ and qr/) instead of a video")"
echo "$(gettext " -r Raw mode: input a single file, skip tarball+par2")"
echo "$(gettext " -l NUM Number of extra loop sets to generate, default 0 (play once)")"
echo "$(gettext " (Setting any -l value also should also disable the nag screen)")"
echo "$(gettext " -k NUM Include bootstrapping kit (mini script QR codes, full script embed)")"
echo "$(gettext " 0=off, 1=on (default)")"
echo "$(gettext " -s NUM Set par2 block size in 8-bit bytes")"
echo "$(gettext " Default 1500, can handle around 48,000,000 bytes maximum")"
echo "$(gettext " INCREASE on large payloads to DECREASE overhead")"
echo "$(gettext " -c FG BG Set custom Hex colors for Foreground and Background")"
echo "$(gettext " Defaults: 000000 6B6B6B (black and gray)")"
echo "$(gettext " Can use 000000 FFFFFF for standard black and white")"
echo "$(gettext " -t LEVEL Throughput level (0-15), default 8 (364 bytes/frame)")"
echo "$(gettext " Levels: 0=40, 1=58, 2=82, 3=110, 4=140, 5=170, 6=200, 7=276, 8=364,")"
echo " 9=452, 10=572, 11=692"
echo ""
echo "$(gettext " More info -")"
echo "$(gettext " Direct nostr link:")"
echo "$(gettext " View quoted note →")"
echo "$(gettext " Legacy web link:")"
echo "$(gettext " https://jumble.social/notes/nevent1qvzqqqqqqypzqamkcvk5k8g730e2j6atadp6mxk7z4aaxc7cnwrlkclx79z4tzygqy88wumn8ghj7mn0wvhxcmmv9uq32amnwvaz7tmjv4kxz7fwv3sk6atn9e5k7tcqyq00jwdmshl99qk993hek9tasux80kuauxn95v6qzgyu3deag7qygget24w")"
echo "$(gettext " Internet Archive legacy web backup:")"
echo "$(gettext " https://archive.org/details/videostore-codec")"
exit "$exit_code"
}
check_encoder_dependencies() {
## $1: is_video mode (requires ffmpeg)
## $2: is_raw mode (bypasses par2)
if ! command -v mktemp >/dev/null 2>&1; then
echo "$(gettext "Warning: 'mktemp' not found. Can try again with -o for a permanent work directory.")" >&2
fi
if ! command -v zint >/dev/null 2>&1; then
echo "$(gettext "Error: can't find tool 'zint'")" >&2
exit 1
fi
if [ "$2" != "true" ] && ! command -v par2 >/dev/null 2>&1; then
echo "$(gettext "Error: can't find tool 'par2'")" >&2
exit 1
fi
if [ "$1" = "true" ] && ! command -v ffmpeg >/dev/null 2>&1; then
echo "$(gettext "Error: bro, do you somehow not have ffmpeg?")" >&2
exit 1
fi
}
check_decoder_dependencies() {
## $1: input_is_dir (skips ffmpeg)
## $2: raw_mode (skips par2)
if ! command -v mktemp >/dev/null 2>&1; then
echo "$(gettext "Warning: 'mktemp' not found. Can try again with -o for a permanent work directory.")" >&2
fi
if ! command -v ZXingReader >/dev/null 2>&1; then
echo "$(gettext "Error: can't find tool 'ZXingReader' (from package: zxing-cpp or zxing-cpp-tools)")" >&2
exit 1
fi
if [ "$1" != "true" ] && ! command -v ffmpeg >/dev/null 2>&1; then
echo "$(gettext "Error: bro, do you somehow not have ffmpeg installed?")" >&2
exit 1
fi
if [ "$2" != "true" ] && ! command -v par2 >/dev/null 2>&1; then
echo "$(gettext "Error: can't find tool 'par2'")" >&2
exit 1
fi
}
get_file_md5_full() {
infile="$1"
if command -v md5sum >/dev/null 2>&1; then
md5sum -- "$infile" | awk '{print $1}'
elif command -v md5 >/dev/null 2>&1; then
## Try -q (BSD), fallback to awk parsing if -q is unsupported
md5 -q -- "$infile" 2>/dev/null || md5 -- "$infile" | awk '{print $NF}'
else
echo "00000000000000000000000000000000"
fi
}
hex_to_octal_fmt() {
printf '%s\n' "$1" | tr -d ' ' | awk '
BEGIN {
for (i = 0; i < 256; i++) {
h = sprintf("%02x", i); H = sprintf("%02X", i); o = sprintf("\\%03o", i)
map[h] = o; map[H] = o
}
}
{ for (i = 1; i <= length($0); i += 2) printf "%s", map[substr($0, i, 2)] }
'
}
EXTRACTOR_SCRIPT=$(cat << 'EOF'
#!/bin/sh
# VideoStore MiniExtractor 0.0.3
# Nostr info link - View quoted note →
# Internet Archive legacy web backup -
set -e
export TEXTDOMAIN=videostore-codec TEXTDOMAINDIR=${TEXTDOMAINDIR:-/usr/share/locale} LC_NUMERIC=C LC_COLLATE=C
! command -v gettext >/dev/null && gettext() { printf %s "$1"; }
# usage info
u() {
z=${1:-1}; [ $z != 0 ] && exec 1>&2; echo
echo "$(gettext "This script goes before reassembler, to prepare files.")"
echo "$(gettext "REQUIREMENTS: ffmpeg + zxing-cpp / zxing-cpp-tools")"
printf "$(gettext "USAGE: %s <input_vid> <output_dir> [options]\n")" "$0"; echo "$(gettext "OPTIONS:")"
echo "$(gettext " -o DIR | Permanent work directory, to bypass mktemp/cleanup")"
echo "$(gettext " -g 0/1 | Grayscale mode, unset with 0 if payload requires color")"
exit $z
}
e() { echo; echo "$(gettext "Error (miniscript has no detailed error messages)")"; u; }
h2o() {
printf %s "$1" | tr -d ' ' | awk 'BEGIN{for(i=0;i<256;i++)m[sprintf("%02x",i)]=m[sprintf("%02X",i)]=sprintf("\\%03o",i)}{for(k=1;k<=length($0);k+=2)printf"%s",m[substr($0,k,2)]}'
}
i2b() {
[ "$4" = 1 ] && p="-pix_fmt gray" || p=""
[ -d "$1" ] && s=$1 || { ffmpeg -i "$1" $p "$2/f_%08d.png"; s=$2; }
[ "$4" = 1 ] && echo "$(gettext "Grayscale mode conserves I/O, unset with -g 0 if payload requires color")"
echo "$(gettext "Attempting extraction, feed output to reassembler when done")"
M=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1); j=0
for i in "$s"/*.png; do
[ -f "$i" ] || continue
(
D=$(ZXingReader -format DataMatrix -single "$i" 2>/dev/null || :)
H=$(printf '%s\n' "$D" | sed -n 's/^Bytes: *//p' | tr -d '\r\n')
if [ -n "$H" ]; then
t="$3/tmp_$(basename "$i").bin"
printf %b "$(h2o "$H")" > "$t"
Q=$(ZXingReader -format QRCode -single "$i" 2>/dev/null | sed -n 's/^Text: *"\([^"]*\)".*/\1/p' | tr -d '\r\n' || :)
if [ -n "$Q" ]; then
mv -- "$t" "$3/$Q.bin" || { touch "$2/.failed"; e; }
else rm -f -- "$t"; fi; fi
) &
j=$((j+1)); [ $j -ge $M ] && wait && j=0
done
wait; [ -f "$2/.failed" ] && e
}
G="1"; P=""; I=""; O=""
while [ $# -gt 0 ]; do
case "$1" in
-o) if [ -z "$2" ] || [ "$2" != "${2#-}" ]; then e; fi; P="$2"; shift 2 ;;
-g) if [ -z "$2" ] || ! echo "$2" | grep -Eq '^[01]$'; then e; fi; G="$2"; shift 2 ;;
-*) e ;;
*) if [ -z "$I" ]; then I="$1"; elif [ -z "$O" ]; then O="$1"; else e; fi; shift ;;
esac
done
[ -z "$I" ] || [ -z "$O" ] || ! command -v ZXingReader >/dev/null 2>&1 && e
[ ! -e "$I" ] && e
[ ! -d "$I" ] && ! command -v ffmpeg >/dev/null 2>&1 && e
if [ -n "$P" ]; then W="$P"; mkdir -p -- "$W" || e
else W=$(mktemp -d); trap 'r=$?; rm -rf "$W"; exit $r' 0 INT TERM HUP; fi
mkdir -p -- "$O" || e
i2b "$I" "$W" "$O" "$G"
EOF
)
REASSEMBLER_SCRIPT=$(cat << 'EOF'
#!/bin/sh
# VideoStore MiniReassembler 0.0.3
# Nostr info link - View quoted note →
# Internet Archive legacy web backup -
set -e
export TEXTDOMAIN=videostore-codec TEXTDOMAINDIR=${TEXTDOMAINDIR:-/usr/share/locale} LC_NUMERIC=C LC_COLLATE=C
! command -v gettext >/dev/null && gettext() { printf %s "$1"; }
# usage info
u() {
z=${1:-1}; [ $z != 0 ] && exec 1>&2; echo
echo "$(gettext "This script goes after the extractor, needs it to prepare files.")"
echo "$(gettext "Might also need par2 installed for error correction.")"
printf "$(gettext "USAGE: %s <bin_dir> <output_dir> [options]\n")" "$0"; echo "$(gettext "OPTIONS:")"
echo "$(gettext " -o DIR | Permanent work directory, to bypass mktemp/cleanup")"
exit $z
}
e() { echo; echo "$(gettext "Error (miniscript has no detailed error messages)")"; u; }
r2f() {
s="$1"; d="$2"; m="$3"; w="$4"; p="$w/p"
: > "$p"
for f in "$s"/*.bin; do
[ -f "$f" ] || continue
n=${f##*/}; t=${n#*.}; t=${t%.bin}
[ "$t" != "$n" ] && printf '%s\n' "$t" >> "$p"
done
[ -s "$p" ] || return 0
sort -u "$p" | while read -r t; do
[ -z "$t" ] && continue
c="$s/00000000.$t.bin"
[ -f "$c" ] || continue
Z=$(sed -n '3p' "$c" | awk '{print $1}')
k=$(sed -n '4p' "$c" | awk '{print $3}')
[ -n "$Z" ] && [ -n "$k" ] || continue
E=$(( (Z + k - 1) / k )); o="$d/$t"; : > "$o"; b=0
i=1
while [ "$i" -le "$E" ]; do
h="$s/$(printf "%08d" "$i").$t.bin"
if [ -f "$h" ]; then cat "$h" >> "$o"
else
b=1; [ "$i" -eq "$E" ] && l=$(( Z - (E - 1) * k )) || l="$k"
dd if=/dev/zero bs=1 count="$l" >> "$o" 2>/dev/null
fi
i=$((i + 1))
done
if [ "$m" != "true" ]; then
tar -xf "$o" -C "$d"
x="$d/${t%.tar}"
R=0
if [ "$b" = 1 ]; then
if [ -d "$x" ] && [ -f "$x/videostore.par2" ]; then
(cd "$x" && par2 repair videostore.par2 >/dev/null 2>&1) && R=1 || R=0
else
R=0
fi
else
R=1
fi
[ "$R" -eq 1 ] && rm -f "$o" && [ -d "$x" ] && rm -f "$x"/videostore*.par2
fi
done
}
echo "$(gettext "Running, check output when done (may need par2 installed to work)")"
I="" O="" P=""
while [ $# -gt 0 ]; do
case "$1" in
-o) if [ -z "$2" ] || [ "$2" != "${2#-}" ]; then e; fi; P="$2"; shift 2 ;;
-*) e ;;
*) if [ -z "$I" ]; then I="$1"; elif [ -z "$O" ]; then O="$1"; else e; fi; shift ;;
esac
done
[ -z "$I" ] || [ -z "$O" ] && e
if [ -n "$P" ]; then W="$P"; mkdir -p -- "$W" || e
else W=$(mktemp -d); trap 'r=$?; rm -rf "$W"; exit $r' 0 INT TERM HUP; fi
mkdir -p -- "$O" || e; r2f "$I" "$O" "false" "$W"
EOF
)
## --- Core Logic Functions ---
encode_dm() {
input_source="$1"
out_target="$2"
fg_color="$3"
bg_color="$4"
barcode_loops="$5"
chunk_size="$6"
dm_scale="$7"
perm_dir="$8"
l_flag_set="$9"
images_only="${10}"
raw_mode="${11}"
par2_size="${12}"
gen_scripts="${13}"
EXTRA_WRAPPER=""
work_dir=""
CLEAN_EXIT="false"
cleanup_encode() {
## Reset traps to prevent recursion
trap - EXIT INT TERM HUP
if [ "$CLEAN_EXIT" != "true" ]; then
echo "" >&2
echo "$(gettext "EXIT: Attempting to clean up temporary files…")" >&2
fi
## Kill background jobs safely
_pids=$(jobs -p)
[ -n "$_pids" ] && kill $_pids 2>/dev/null || true
[ -n "$EXTRA_WRAPPER" ] && [ -d "$EXTRA_WRAPPER" ] && rm -rf "$EXTRA_WRAPPER"
[ -n "$work_dir" ] && [ -d "$work_dir" ] && rm -rf "$work_dir"
}
if [ "$images_only" = "true" ]; then
mode_v="false"
else
mode_v="true"
fi
## Edge case: A file was provided as input, but the user did not specify raw mode (-r)
if [ "$raw_mode" = "false" ] && [ -f "$input_source" ]; then
echo "========================================================================="
echo "$(gettext " You may have input a single file while raw mode (-r) was not selected.")"
echo "$(gettext " Standard mode expects a directory.")"
echo ""
echo "$(gettext " How would you like to proceed?")"
echo "$(gettext " 1) Enable Raw Mode (-r) automatically (no error correction)")"
echo "$(gettext " 2) Wrap in Tarball (adds overhead for error correction)")"
echo "$(gettext " 3) Cancel Encoding / Start Over")"
echo "========================================================================="
printf "$(gettext "Selection [1/2/3]: ")"
read -r file_choice
case "$file_choice" in
1)
raw_mode="true"
;;
2)
## Create a temporary wrapper to satisfy the directory mode requirements
EXTRA_WRAPPER=$(mktemp -d)
BASE_NAME=$(basename -- "$input_source")
mkdir -p -- "$EXTRA_WRAPPER/$BASE_NAME"
cp -- "$input_source" "$EXTRA_WRAPPER/$BASE_NAME/"
## Establish cleanup if we've created temp files
[ -z "$perm_dir" ] && trap cleanup_encode EXIT INT TERM HUP
input_source="$EXTRA_WRAPPER/$BASE_NAME"
;;
0|3|*)
echo "$(gettext "Operation canceled.")"
exit 1
;;
esac
fi
check_encoder_dependencies "$mode_v" "$raw_mode"
if [ "$raw_mode" = "true" ]; then
if [ ! -f "$input_source" ]; then
printf "$(gettext "Error: File '%s' not found\n")" "$input_source" >&2
exit 1
fi
filename=$(basename -- "$input_source")
else
if [ ! -d "$input_source" ]; then
printf "$(gettext "Error: Directory '%s' not found\n")" "$input_source" >&2
exit 1
fi
dir_basename=$(basename -- "$input_source")
filename="$dir_basename.tar"
fi
## Reject problematic shell characters and leading dashes, but allow Unicode in general
case "$filename" in
-*)
printf "$(gettext "Error: Name '%s' cannot start with a dash.\n")" "$filename" >&2
exit 1
;;
*[[:space:]]*)
printf "$(gettext "Error: Name '%s' cannot contain spaces or whitespace.\n")" "$filename" >&2
exit 1
;;
*[\`\$\(\)\<\>\\\|\;\&\*\"\'\?\!]*)
printf "$(gettext "Error: Name '%s' contains prohibited shell metacharacters.\n")" "$filename" >&2
echo "$(gettext "Please avoid using characters like: \` $ ( ) < > \\ | ; & * \" ' ? !")" >&2
exit 1
;;
esac
input_source_parent=$(dirname -- "$input_source")
input_source_abs="$(cd -- "$input_source_parent" && pwd)/$(basename -- "$input_source")"
mkdir -p -- "$out_target"
out_target_abs="$(cd -- "$out_target" && pwd)"
echo "$(gettext "Trying to create work folder…")"
if [ -n "$perm_dir" ]; then
mkdir -p -- "$perm_dir"
work_dir="$(cd -- "$perm_dir" && pwd)"
else
work_dir=$(mktemp -d)
trap cleanup_encode EXIT INT TERM HUP
fi
orig_pwd=$(pwd)
if [ "$raw_mode" = "true" ]; then
input_abs_path="$input_source_abs"
else
if [ -n "$perm_dir" ]; then
if [ "$gen_scripts" = "1" ]; then
cp -- "$0" "$input_source/$(basename -- "$0")"
fi
printf "$(gettext "Using permanent mode: Generating par2 for '%s'...\n")" "$input_source"
(cd "$input_source" && rm -f videostore*.par2 && par2 create -r30 -n2 -s"$par2_size" videostore * >/dev/null)
tar_file="$work_dir/$filename"
(cd "$input_source_parent" && tar -cf "$tar_file" "$dir_basename")
input_abs_path="$tar_file"
else
printf "$(gettext "Preparing '%s' with par2 recovery...\n")" "$dir_basename"
mkdir -p -- "$work_dir/pack/$dir_basename"
cp -r "$input_source/." "$work_dir/pack/$dir_basename/"
if [ "$gen_scripts" = "1" ]; then
cp -- "$0" "$work_dir/pack/$dir_basename/$(basename -- "$0")"
fi
## Generate parity for all files in the directory
(cd "$work_dir/pack/$dir_basename" && rm -f videostore*.par2 && par2 create -r30 -n2 -s"$par2_size" videostore * >/dev/null)
tar_file="$work_dir/$filename"
(cd "$work_dir/pack" && tar -cf "$tar_file" "$dir_basename")
input_abs_path="$tar_file"
fi
fi
cd -- "$work_dir"
printf "$(gettext "Splitting '%s' into %s-byte chunks (adjust with -t option)...\n")" "$filename" "$chunk_size"
mkdir -p chunks
split -b "$chunk_size" -- "$input_abs_path" chunks/_
echo "$(gettext "Preparing data matrix sequence...")"
file_md5_full=$(get_file_md5_full "$input_abs_path")
mkdir -p staging_dm
mkdir -p staging_qr
## 1. Generate Metadata Header Matrix
header_dm="staging_dm/00000000.png"
filesize=$(wc -c < "$input_abs_path" | tr -d ' ')
printf "MD5: %s\n%s\n%s bytes\nChunk size: %s\n%s\n%s\nMore info / readme (nostr link) -\nView quoted note →" "$file_md5_full" "$filename" "$filesize" "$chunk_size" "VideoStore codec $VERSION" "file data from next data matrix until metadata code repeats" > header.tmp
zint -b 71 --scale="$dm_scale" --whitesp=6 --vwhitesp=6 --fg="$fg_color" --bg="$bg_color" -i header.tmp -o "$header_dm"
## 2. Generate Data Matrix Payload
chunk_count=0
for f in chunks/_*; do
[ -f "$f" ] || continue
chunk_count=$((chunk_count + 1))
seq_num=$(printf "%08d" "$chunk_count")
zint -b 71 --binary --scale="$dm_scale" --whitesp=6 --vwhitesp=6 --fg="$fg_color" --bg="$bg_color" -o "staging_dm/${seq_num}.png" -i "./$f"
done
## 3. Generate QR codes
header_bc_content="00000000.${filename}"
zint -b 58 --secure=4 --fg="$fg_color" --bg="$bg_color" --scale=3 --whitesp=6 --vwhitesp=6 -o "staging_qr/00000000.png" -d "$header_bc_content"
i=1
while [ "$i" -le "$chunk_count" ]; do
seq_num=$(printf "%08d" "$i")
barcode_content="${seq_num}.${filename}"
zint -b 58 --secure=4 --fg="$fg_color" --bg="$bg_color" --scale=3 --whitesp=6 --vwhitesp=6 -o "staging_qr/${seq_num}.png" -d "$barcode_content"
i=$((i + 1))
done
## Buffer for video mode
if [ "$mode_v" = "true" ]; then
j=1
while [ "$j" -le 10 ]; do
extra_seq=$(printf "%08d" "$((chunk_count + j))")
cp "staging_qr/00000000.png" "staging_qr/${extra_seq}.png"
cp "staging_dm/00000000.png" "staging_dm/${extra_seq}.png"
j=$((j + 1))
done
fi
if [ "$mode_v" = "true" ]; then
echo "$(gettext "Compiling side-by-side lossless VP9 video...")"
if [ "$l_flag_set" = "false" ]; then
echo "========================================================================="
echo "$(gettext " NOTICE: It is recommended to loop the output sequence,")"
echo "$(gettext " in your video editor, at least once (playing twice).")"
echo "$(gettext " There is a less reliable loop function included in")"
echo "$(gettext " this script, not recommended, but available.")"
echo ""
echo "$(gettext " Would you like to use the less reliable built-in loop?")"
echo "$(gettext " Press ENTER to skip / handle later (RECOMMENDED)")"
echo "$(gettext " Type a number to set loop count (NOT RECOMMENDED)")"
echo "$(gettext " Type 'C' to cancel encode / start over")"
echo "$(gettext " (this screen may be skipped from the start with -l 0)")"
echo "========================================================================="
read -r user_loops
case "$user_loops" in
[cC]|[cC][aA][nN][cC][eE][lL])
echo "$(gettext "Operation canceled. Exiting.")"
exit 0
;;
esac
if [ -n "$user_loops" ] && echo "$user_loops" | grep -Eq '^[0-9]+$'; then
barcode_loops="$user_loops"
fi
fi
loop_opts=""
if [ "$barcode_loops" -gt 0 ]; then
loop_opts="-stream_loop $barcode_loops"
fi
video_out_abs="$out_target_abs/$filename.mkv"
if [ "$raw_mode" = "false" ] && [ "$gen_scripts" = "1" ]; then
echo "$(gettext "Trying to generate recovery miniscript QR codes...")"
printf "%s" "$EXTRACTOR_SCRIPT" > "$work_dir/extractor.sh"
printf "%s" "$REASSEMBLER_SCRIPT" > "$work_dir/reassembler.sh"
zint -b 58 --fg="$fg_color" --bg="$bg_color" --scale=2 --whitesp=4 --vwhitesp=4 -i "$work_dir/extractor.sh" -o "$out_target_abs/$filename.extractor.qr.png"
zint -b 58 --fg="$fg_color" --bg="$bg_color" --scale=2 --whitesp=4 --vwhitesp=4 -i "$work_dir/reassembler.sh" -o "$out_target_abs/$filename.reassembler.qr.png"
fi
ffmpeg -reinit_filter 0 $loop_opts -thread_queue_size 16384 -framerate 25 -i staging_dm/%08d.png \
-reinit_filter 0 $loop_opts -thread_queue_size 16384 -framerate 25 -i staging_qr/%08d.png \
-filter_complex "[0:v]pad=540:540:0:540-ih:color=black@0[dm]; \
[1:v]pad=540:540:0:540-ih:color=black@0[qr]; \
[dm][qr]hstack=inputs=2,format=yuva420p[out]" \
-map "[out]" -c:v libvpx-vp9 -lossless 1 "$video_out_abs"
else
printf "$(gettext "Trying to move generated artifacts to '%s'...\n")" "$out_target_abs"
mkdir -p -- "$out_target_abs/dm" "$out_target_abs/qr"
mv staging_dm/*.png "$out_target_abs/dm/"
mv staging_qr/*.png "$out_target_abs/qr/"
cd -- "$orig_pwd"
fi
CLEAN_EXIT="true"
if [ -z "$perm_dir" ]; then
trap - EXIT INT TERM HUP
cleanup_encode
fi
}
# -------------------------------------------------------------------------
## Sub-Function: Extractor Phase (Video -> .bin chunks)
# -------------------------------------------------------------------------
extract_frames_to_bins() {
input_video="$1"
tmp_dir="$2"
bin_staging="$3"
grayscale_mode="$4"
if [ -d "$input_video" ]; then
printf "$(gettext "Input seems to be a directory. Reading frames from '%s'...\n")" "$input_video"
frame_source="$input_video"
else
echo "$(gettext "Attempting frame extraction from video...")"
pix_fmt_opt=""
if [ "$grayscale_mode" = "1" ]; then
pix_fmt_opt="-pix_fmt gray"
fi
ffmpeg -i "$input_video" $pix_fmt_opt "$tmp_dir/f_%08d.png"
frame_source="$tmp_dir"
fi
echo "$(gettext "Attempting binary chunk extraction from frames...")"
MAX_JOBS=$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1)
FAILED_MARKER="$tmp_dir/.extract_failed"
rm -f "$FAILED_MARKER"
job_count=0
for img in "$frame_source"/*.png; do
[ -f "$img" ] || continue
(
## Note: exit calls here only terminate the subshell, not the main script.
ZXING_DM=$(ZXingReader -format DataMatrix -single "$img" 2>/dev/null) || exit 0
HEX_BYTES=$(printf '%s\n' "$ZXING_DM" | sed -n 's/^Bytes: *//p' | tr -d '\r\n') || exit 0
if [ -n "$HEX_BYTES" ]; then
img_base=$(basename "$img")
tmp_chunk="$bin_staging/tmp_${img_base}.bin"
PRINTF_FMT=$(hex_to_octal_fmt "$HEX_BYTES")
printf '%b' "$PRINTF_FMT" > "$tmp_chunk" || { touch "$FAILED_MARKER"; exit 1; }
## Prevent failure to find a QR code from aborting the subshell before cleanup
ZXING_QR=$(ZXingReader -format QRCode -single "$img" 2>/dev/null) || :
QR_CONTENT=$(printf '%s\n' "$ZXING_QR" | sed -n 's/^Text: *"\([^"]*\)".*/\1/p' | tr -d '\r\n')
if [ -n "$QR_CONTENT" ]; then
mv -- "$tmp_chunk" "$bin_staging/${QR_CONTENT}.bin" || { touch "$FAILED_MARKER"; exit 1; }
else
rm -f -- "$tmp_chunk"
fi
fi
) &
job_count=$((job_count + 1))
if [ "$job_count" -ge "$MAX_JOBS" ]; then
wait
job_count=0
fi
done
wait
if [ -f "$FAILED_MARKER" ]; then
echo "$(gettext "Error: One or more frame extractions or file moves may have failed.")" >&2
rm -f "$FAILED_MARKER"
exit 1
fi
}
# -------------------------------------------------------------------------
## Sub-Function: Reassembler Phase (.bin chunks -> Target File)
# -------------------------------------------------------------------------
reassemble_bins_to_file() {
bin_staging="$1"
output_dir="$2"
raw_mode="$3"
tmp_dir="$4"
keep_intermediate="$5"
processed_targets="$tmp_dir/processed_targets"
: > "$processed_targets"
for filepath in "$bin_staging"/*.bin; do
[ -f "$filepath" ] || continue
filename=${filepath##*/}
tmp_name=${filename#*.}
target_name=${tmp_name%.bin}
[ "$target_name" != "$filename" ] && printf '%s\n' "$target_name" >> "$processed_targets"
done
if [ -s "$processed_targets" ]; then
unique_targets=$(LC_ALL=C sort -u "$processed_targets")
else
echo "$(gettext "No valid data streams found from video.")"
return 0
fi
while read -r target; do
[ -z "$target" ] && continue
metadata_chunk="$bin_staging/00000000.${target}.bin"
if [ ! -f "$metadata_chunk" ]; then
printf "$(gettext "Must skip %s: Metadata chunk (sequence number 0) seems to be missing, cannot reassemble safely.\n")" "$target"
continue
fi
filesize_line=$(sed -n '3p' "$metadata_chunk")
filesize=$(echo "$filesize_line" | awk '{print $1}')
chunk_size_line=$(sed -n '4p' "$metadata_chunk")
chunk_size=$(echo "$chunk_size_line" | awk '{print $3}')
if [ -z "$filesize" ] || [ -z "$chunk_size" ]; then
printf "$(gettext "Must skip %s: Metadata seems corrupted (could not find filesize or chunk_size).\n")" "$target"
continue
fi
expected_chunks=$(( (filesize + chunk_size - 1) / chunk_size ))
printf "$(gettext "Attempting reassembly of %s (%s bytes, %s chunks)...\n")" "$target" "$filesize" "$expected_chunks"
output_file="$output_dir/$target"
: > "$output_file"
corrupted_chunks="false"
i=1
while [ "$i" -le "$expected_chunks" ]; do
seq_num=$(printf "%08d" "$i")
chunk_path="$bin_staging/${seq_num}.${target}.bin"
if [ -f "$chunk_path" ]; then
cat "$chunk_path" >> "$output_file"
else
printf "$(gettext " Warning: Chunk %s missing. Will try to pad with zeros.\n")" "$seq_num"
corrupted_chunks="true"
if [ "$i" -eq "$expected_chunks" ]; then
pad_size=$(( filesize - (expected_chunks - 1) * chunk_size ))
else
pad_size="$chunk_size"
fi
dd if=/dev/zero bs=1 count="$pad_size" >> "$output_file" 2>/dev/null
fi
i=$((i + 1))
done
printf "$(gettext "Reassembly attempt done, check output: %s\n")" "$target"
if [ "$raw_mode" != "true" ]; then
printf "$(gettext "Attempting to extract %s...\n")" "$target"
tar -xf "$output_file" -C "$output_dir"
target_basename="${target%.tar}"
extracted_dir="$output_dir/$target_basename"
repair_success=0
if [ "$corrupted_chunks" = "true" ]; then
if [ -d "$extracted_dir" ] && [ -f "$extracted_dir/videostore.par2" ]; then
echo "$(gettext "Missing chunks detected during reassembly! Attempting par2 repair...")"
if (cd "$extracted_dir" && par2 repair videostore.par2); then
repair_success=1
else
echo "$(gettext "par2 repair seemingly failed. Will try to keep intermediate files for future recovery.")"
fi
else
echo "$(gettext "Warning: no videostore.par2 file was found to repair the damage.")"
fi
else
echo "$(gettext "All chunks arrived intact. Could skip par2 repair.")"
repair_success=1
fi
if [ "$keep_intermediate" != "true" ] && [ "$repair_success" -eq 1 ]; then
rm -f "$output_file"
[ -d "$extracted_dir" ] && rm -f "$extracted_dir"/videostore*.par2
fi
fi
done <<VAR_EOF
$unique_targets
VAR_EOF
}
# -------------------------------------------------------------------------
## Core Decoder Router
# -------------------------------------------------------------------------
decode_and_reassemble() {
input_video="$1"
output_dir="$2"
perm_dir="$3"
raw_mode="$4"
bin_only="$5"
grayscale_mode="$6"
if [ ! -e "$input_video" ]; then
printf "$(gettext "Error: Input path '%s' not found.\n")" "$input_video" >&2
exit 1
fi
is_dir="false"; [ -d "$input_video" ] && is_dir="true"
check_decoder_dependencies "$is_dir" "$raw_mode"
mkdir -p -- "$output_dir"
CLEAN_EXIT="false"
cleanup_decoder() {
trap - EXIT INT TERM HUP
if [ "$CLEAN_EXIT" != "true" ]; then
echo "" >&2
echo "$(gettext "EXIT: Attempting to clean up temporary files…")" >&2
fi
_pids=$(jobs -p)
[ -n "$_pids" ] && kill $_pids 2>/dev/null || true
[ -d "$tmp_dir" ] && rm -rf "$tmp_dir"
}
keep_intermediate="false"
if [ -n "$perm_dir" ]; then
tmp_dir="$perm_dir"
mkdir -p -- "$tmp_dir"
keep_intermediate="true"
else
tmp_dir=$(mktemp -d)
trap cleanup_decoder EXIT INT TERM HUP
fi
if [ -z "$grayscale_mode" ] && [ ! -d "$input_video" ]; then
echo "========================================================================="
echo "$(gettext " Does the data matrix sequence use color?")"
echo "$(gettext " (Color backgrounds are a huge waste of I/O overhead")"
echo "$(gettext " unless the payload is color too)")"
echo ""
echo "$(gettext " Enter 1 for Grayscale")"
echo "$(gettext " Enter 0 for Color")"
echo "========================================================================="
printf "$(gettext "Selection [0/1]: ")"
read -r grayscale_mode
fi
if [ "$bin_only" = "true" ] && [ -d "$input_video" ]; then
printf "$(gettext "Bin-only mode: Using existing bin directory '%s' for reassembly.\n")" "$input_video"
bin_staging="$input_video"
else
bin_staging="$tmp_dir/extracted_chunks"
mkdir -p -- "$bin_staging"
extract_frames_to_bins "$input_video" "$tmp_dir" "$bin_staging" "$grayscale_mode"
if [ "$bin_only" = "true" ]; then
printf "$(gettext "Bin-only mode: Copying extracted chunks to '%s'...\n")" "$output_dir"
cp -r "$bin_staging/." "$output_dir/"
printf "$(gettext "Bin extraction attempted, check output: '%s'.\n")" "$output_dir"
CLEAN_EXIT="true"
if [ -z "$perm_dir" ]; then
trap - EXIT INT TERM HUP
cleanup_decoder
fi
return 0
fi
fi
reassemble_bins_to_file "$bin_staging" "$output_dir" "$raw_mode" "$tmp_dir" "$keep_intermediate"
CLEAN_EXIT="true"
printf "$(gettext "Output ready to check: '%s'.\n")" "$output_dir"
if [ -z "$perm_dir" ]; then
trap - EXIT INT TERM HUP
cleanup_decoder
fi
}
## --- Main CLI Parser & Router ---
INPUT_FILE=""
INPUT_VIDEO=""
OUTPUT_TARGET=""
FG_COLOR="000000"
BG_COLOR="6B6B6B"
BARCODE_LOOPS=0
THROUGHPUT_LEVEL=8
EXEC_MODE=""
PERM_DIR=""
L_FLAG_SET="false"
IMAGES_ONLY="false"
RAW_MODE="false"
BIN_ONLY="false"
GRAYSCALE_MODE=""
GENERATE_SCRIPTS="1"
PAR2_BLOCK_SIZE=1500
while [ $# -gt 0 ]; do
case "$1" in
-h|--help|-v|--version)
usage 0
;;
-e)
if [ -n "$EXEC_MODE" ]; then echo "$(gettext "Error: Cannot mix -e and -d")" >&2; usage; fi
if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then echo "$(gettext "Error: -e requires an argument.")" >&2; usage; fi
INPUT_FILE="$2"
EXEC_MODE="ENCODE"
shift 2
;;
-d)
if [ -n "$EXEC_MODE" ]; then echo "$(gettext "Error: Cannot mix -e and -d")" >&2; usage; fi
if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then echo "$(gettext "Error: -d requires an argument.")" >&2; usage; fi
INPUT_VIDEO="$2"
EXEC_MODE="DECODE"
shift 2
;;
-b)
BIN_ONLY="true"
shift
;;
-g)
if [ -z "$2" ] || ! echo "$2" | grep -E -q '^[01]$'; then
echo "$(gettext "Error: -g requires 0 or 1.")" >&2
usage
fi
GRAYSCALE_MODE="$2"
shift 2
;;
-r)
RAW_MODE="true"
shift
;;
-i)
IMAGES_ONLY="true"
shift
;;
-k)
if [ -z "$2" ] || ! echo "$2" | grep -E -q '^[01]$'; then
echo "$(gettext "Error: -k requires 0 or 1.")" >&2
usage
fi
GENERATE_SCRIPTS="$2"
shift 2
;;
-o)
if [ -z "$2" ] || [ "${2#-}" != "$2" ]; then echo "$(gettext "Error: -o requires a directory.")" >&2; usage; fi
PERM_DIR="$2"
shift 2
;;
-c)
if [ -z "$2" ] || [ "${2#-}" != "$2" ] || [ -z "$3" ] || [ "${3#-}" != "$3" ]; then
echo "$(gettext "Error: -c requires two hex colors.")" >&2
usage
fi
if ! echo "$2" | grep -Eq '^[0-9a-fA-F]{6}$' || ! echo "$3" | grep -Eq '^[0-9a-fA-F]{6}$'; then
echo "$(gettext "Error: Colors must be valid 6-digit hex values.")" >&2
usage
fi
FG_COLOR="$2"
BG_COLOR="$3"
shift 3
;;
-l)
if [ -z "$2" ] || [ "${2#-}" != "$2" ] || ! echo "$2" | grep -E -q '^[0-9]+$'; then
echo "$(gettext "Error: -l requires a valid non-negative integer.")" >&2
usage
fi
BARCODE_LOOPS="$2"
L_FLAG_SET="true"
shift 2
;;
-s)
if [ -z "$2" ] || [ "${2#-}" != "$2" ] || ! echo "$2" | grep -E -q '^[0-9]+$'; then
echo "$(gettext "Error: -s requires a valid non-negative integer block size.")" >&2
usage
fi
PAR2_BLOCK_SIZE="$2"
shift 2
;;
-t)
if [ -z "$2" ] || ! echo "$2" | grep -E -q '^[0-9]+$'; then
echo "$(gettext "Error: -t requires an integer level (0-11).")" >&2
usage
fi
THROUGHPUT_LEVEL="$2"
shift 2
;;
-*)
printf "$(gettext "Unknown option: %s\n")" "$1" >&2
usage
;;
*)
if [ -n "$OUTPUT_TARGET" ]; then echo "$(gettext "Error: Too many arguments.")" >&2; usage; fi
OUTPUT_TARGET="$1"
shift
;;
esac
done
if [ -z "$EXEC_MODE" ] || [ -z "$OUTPUT_TARGET" ]; then
echo "$(gettext "Error: Missing operational mode (-e/-d) or final output target.")" >&2
usage
fi
ZINT_DM_SCALE=3
case "$THROUGHPUT_LEVEL" in
0) CHUNK_SIZE=40 ;;
1) CHUNK_SIZE=58 ;;
2) CHUNK_SIZE=82 ;;
3) CHUNK_SIZE=110 ;;
4) CHUNK_SIZE=140 ;;
5) CHUNK_SIZE=170 ;;
6) CHUNK_SIZE=200 ;;
7) CHUNK_SIZE=276 ;;
8) CHUNK_SIZE=364 ;;
9) CHUNK_SIZE=452; ZINT_DM_SCALE=2.5 ;;
10) CHUNK_SIZE=572; ZINT_DM_SCALE=2.5 ;;
11) CHUNK_SIZE=692; ZINT_DM_SCALE=2.5 ;;
12) CHUNK_SIZE=812; ZINT_DM_SCALE=2 ;;
13) CHUNK_SIZE=1046; ZINT_DM_SCALE=2 ;;
14) CHUNK_SIZE=1234; ZINT_DM_SCALE=1.5 ;;
15) CHUNK_SIZE=1554; ZINT_DM_SCALE=1.5 ;;
*) echo "$(gettext "Error: Throughput level must be between 0 and 15.")"; usage ;;
esac
## Execute routing
if [ "$EXEC_MODE" = "ENCODE" ]; then
encode_dm "$INPUT_FILE" "$OUTPUT_TARGET" "$FG_COLOR" "$BG_COLOR" "$BARCODE_LOOPS" "$CHUNK_SIZE" "$ZINT_DM_SCALE" "$PERM_DIR" "$L_FLAG_SET" "$IMAGES_ONLY" "$RAW_MODE" "$PAR2_BLOCK_SIZE" "$GENERATE_SCRIPTS"
printf "$(gettext "Output ready to check: '%s'.\n")" "$OUTPUT_TARGET"
elif [ "$EXEC_MODE" = "DECODE" ]; then
decode_and_reassemble "$INPUT_VIDEO" "$OUTPUT_TARGET" "$PERM_DIR" "$RAW_MODE" "$BIN_ONLY" "$GRAYSCALE_MODE"
fi
if [ -n "$START_TIME" ]; then
END_TIME=$(date +%s 2>/dev/null) || END_TIME=""
if [ -n "$END_TIME" ]; then
TOTAL_TIME=$((END_TIME - START_TIME))
printf "$(gettext "Total runtime: %s seconds.\n")" "$TOTAL_TIME"
else
echo "$(gettext "Notice: Failed to capture total script runtime.")"
fi
fi
if [ "$EXEC_MODE" = "ENCODE" ]; then
case "$OUTPUT_TARGET" in
*.mkv)
echo "========================================================================="
echo "$(gettext " USAGE TIPS FOR YOUR OUTPUT VIDEO:")"
echo "$(gettext " 1. At throughput levels 0-8 (6x6 pixels per square),")"
echo "$(gettext " scaling should be incremented in multiples of one sixth,")"
echo "$(gettext " with NO interpolation, to retain sharp pixel edges.")"
echo "$(gettext " (Level 9: 5x5 / Levels 12+ unreliably small, max 4x4)")"
echo "$(gettext " 2. Use lossless encoding when uploading to apps which add")"
echo "$(gettext " their own compression, such as YouTube.")"
echo "$(gettext " 3. Test-decode your own file before publishing!")"
echo "========================================================================="
;;
esac
fi

Jumble
A user-friendly Nostr client for exploring relay feeds
Internet Archive
VideoStore Codec : Digital Assistant : Free Download, Borrow, and Streaming : Internet Archive
VideoStore Codec - a shell script for converting files to embeddable video sequences and backLink to original postEncoder requirements: zint, ffmpe...
Internet Archive
VideoStore Codec : Digital Assistant : Free Download, Borrow, and Streaming : Internet Archive
VideoStore Codec - a shell script for converting files to embeddable video sequences and backLink to original postEncoder requirements: zint, ffmpe...
Internet Archive
VideoStore Codec : Digital Assistant : Free Download, Borrow, and Streaming : Internet Archive
VideoStore Codec - a shell script for converting files to embeddable video sequences and backLink to original postEncoder requirements: zint, ffmpe...