actually
This commit is contained in:
@@ -1,5 +1,117 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# plan-disk-burns.sh - Split a set of files into per-disc manifests that each
|
||||||
|
# fit on a Blu-ray (BD-R). Packs files greedily by accumulated size and writes
|
||||||
|
# one manifest per disc, then prints a summary you can review before burning.
|
||||||
|
#
|
||||||
|
# It does NOT burn anything. It only produces discNN_files.txt manifests.
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
echo "plan-disk-burns: placeholder"
|
usage() {
|
||||||
echo "runtime tools available: smartctl, lsblk"
|
cat <<'EOF'
|
||||||
|
Usage: plan-disk-burns.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-s DIR Source directory to scan (default: current directory)
|
||||||
|
-p GLOB Filename pattern, case-insensitive (default: *.raf)
|
||||||
|
-o DIR Output directory for manifests (default: ./br-manifests)
|
||||||
|
-l LABEL Volume label prefix for the burn commands; disc number is
|
||||||
|
appended, e.g. -l PHOTOS yields PHOTOS_1 (default: DISK)
|
||||||
|
-c GiB Per-disc capacity in GiB, may be fractional e.g. 22.5 (default: 23)
|
||||||
|
-h Show this help
|
||||||
|
|
||||||
|
Output:
|
||||||
|
DIR/disc01_files.txt, disc02_files.txt, ... absolute file paths (one per line)
|
||||||
|
|
||||||
|
Manifests use absolute paths so burning works from any directory. The printed
|
||||||
|
xorriso command strips the source root and roots everything at the disc root.
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
src="."
|
||||||
|
pattern="*.raf"
|
||||||
|
outdir="./br-manifests"
|
||||||
|
label="DISK"
|
||||||
|
cap_gib=23
|
||||||
|
|
||||||
|
while getopts ":s:p:o:l:c:h" opt; do
|
||||||
|
case "$opt" in
|
||||||
|
s) src="$OPTARG" ;;
|
||||||
|
p) pattern="$OPTARG" ;;
|
||||||
|
o) outdir="$OPTARG" ;;
|
||||||
|
l) label="$OPTARG" ;;
|
||||||
|
c) cap_gib="$OPTARG" ;;
|
||||||
|
h) usage; exit 0 ;;
|
||||||
|
\?) echo "Unknown option: -$OPTARG" >&2; usage; exit 2 ;;
|
||||||
|
:) echo "Option -$OPTARG requires an argument" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -d "$src" ]] || { echo "Source directory not found: $src" >&2; exit 1; }
|
||||||
|
[[ "$cap_gib" =~ ^[0-9]+(\.[0-9]+)?$ ]] || { echo "Capacity must be a number (GiB), e.g. 23 or 22.5: $cap_gib" >&2; exit 1; }
|
||||||
|
|
||||||
|
# Resolve to an absolute path so manifests and the burn command are CWD-independent.
|
||||||
|
src=$(realpath "$src")
|
||||||
|
|
||||||
|
limit=$(awk -v c="$cap_gib" 'BEGIN { printf "%d", c * 1024 * 1024 * 1024 }')
|
||||||
|
mkdir -p "$outdir"
|
||||||
|
rm -f "$outdir"/disc*_files.txt
|
||||||
|
|
||||||
|
# Collect files sorted by path so discs group naturally by folder/date.
|
||||||
|
mapfile -d '' entries < <(find "$src" -type f -iname "$pattern" -printf '%s\t%p\0' | sort -z -t$'\t' -k2)
|
||||||
|
|
||||||
|
if [[ ${#entries[@]} -eq 0 ]]; then
|
||||||
|
echo "No files matching '$pattern' under '$src'." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
disc=1
|
||||||
|
disc_bytes=0
|
||||||
|
total_bytes=0
|
||||||
|
total_files=0
|
||||||
|
declare -A disc_bytes_map disc_files_map
|
||||||
|
|
||||||
|
manifest() { printf '%s/disc%02d_files.txt' "$outdir" "$1"; }
|
||||||
|
|
||||||
|
for entry in "${entries[@]}"; do
|
||||||
|
size="${entry%%$'\t'*}"
|
||||||
|
path="${entry#*$'\t'}"
|
||||||
|
|
||||||
|
if (( size > limit )); then
|
||||||
|
echo "WARNING: '$path' ($((size/1024/1024)) MiB) exceeds disc capacity (${cap_gib} GiB); skipping." >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( disc_bytes + size > limit )); then
|
||||||
|
disc=$(( disc + 1 ))
|
||||||
|
disc_bytes=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "$path" >> "$(manifest "$disc")"
|
||||||
|
disc_bytes=$(( disc_bytes + size ))
|
||||||
|
disc_bytes_map[$disc]=$disc_bytes
|
||||||
|
disc_files_map[$disc]=$(( ${disc_files_map[$disc]:-0} + 1 ))
|
||||||
|
total_bytes=$(( total_bytes + size ))
|
||||||
|
total_files=$(( total_files + 1 ))
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== Manifest summary (cap ${cap_gib} GiB/disc) ==="
|
||||||
|
for ((d=1; d<=disc; d++)); do
|
||||||
|
[[ -f "$(manifest "$d")" ]] || continue
|
||||||
|
mib=$(( disc_bytes_map[$d] / 1024 / 1024 ))
|
||||||
|
printf ' disc%02d: %5d files, %6d MiB -> %s\n' \
|
||||||
|
"$d" "${disc_files_map[$d]}" "$mib" "$(manifest "$d")"
|
||||||
|
done
|
||||||
|
printf ' TOTAL : %5d files, %6d MiB across %d disc(s)\n' \
|
||||||
|
"$total_files" "$(( total_bytes / 1024 / 1024 ))" "$disc"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "=== Burn commands (review, then run one per disc) ==="
|
||||||
|
for ((d=1; d<=disc; d++)); do
|
||||||
|
[[ -f "$(manifest "$d")" ]] || continue
|
||||||
|
# SC2016 - The $(cat ...) is literal text printed for the user to run later, not meant to expand here.
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
printf 'xorriso -outdev /dev/sr0 -volid "%s_%d" \\\n -map_l %q / $(cat %q) -- -commit -eject\n\n' \
|
||||||
|
"$label" "$d" "$src" "$(manifest "$d")"
|
||||||
|
done
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ let
|
|||||||
# my-new-thing = { runtimeInputs = with pkgs; [ jq curl ]; };
|
# my-new-thing = { runtimeInputs = with pkgs; [ jq curl ]; };
|
||||||
# };
|
# };
|
||||||
scriptDefs = {
|
scriptDefs = {
|
||||||
plan-disk-burns = { runtimeInputs = with pkgs; [ smartmontools util-linux ]; };
|
plan-disk-burns = { runtimeInputs = with pkgs; [ coreutils findutils gawk xorriso ]; };
|
||||||
};
|
};
|
||||||
|
|
||||||
mkScript = name: def:
|
mkScript = name: def:
|
||||||
|
|||||||
Reference in New Issue
Block a user