@@ -12,6 +12,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). |
||
| 12 | 12 |
- `--continue-on-error` renamed to `--keep-going` (old name kept as alias) |
| 13 | 13 |
### Added |
| 14 | 14 |
- `--unattended` preset (`--delete-source` + `--keep-going`) for long unattended runs |
| 15 |
+- `--staging-dir DIR` to place intermediate transcoding temp files on a fast local path, |
|
| 16 |
+ with automatic fallback to destination temp files when staging is unavailable |
|
| 15 | 17 |
|
| 16 | 18 |
- `cleanup_garmin_varia_media_folder.sh` helper for media-folder hygiene (import + transcoded output): |
| 17 | 19 |
removes AppleDouble artifacts (`._*`), removes zero-size MP4 files, |
@@ -28,6 +28,9 @@ manifest placeholder for a future FIT sync pipeline. |
||
| 28 | 28 |
# Long unattended run preset (delete-source + keep-going) |
| 29 | 29 |
./garmin_varia_transcode.sh -s SampleFootage -d Output --unattended |
| 30 | 30 |
|
| 31 |
+# NFS destination with local staging for temp outputs |
|
| 32 |
+./garmin_varia_transcode.sh -s SampleFootage -d ~/Autofs/xdev/is-baobab/nvme0n1/@backup/Garmin --staging-dir /tmp/varia_staging |
|
| 33 |
+ |
|
| 31 | 34 |
# Media cleanup helper (Apple artifacts, zero-byte MP4, duplicate suffix normalization) |
| 32 | 35 |
./cleanup_garmin_varia_media_folder.sh --dry-run ~/Autofs/xdev/autonas/ext01/@Camera/import |
| 33 | 36 |
``` |
@@ -49,6 +52,8 @@ manifest placeholder for a future FIT sync pipeline. |
||
| 49 | 52 |
Options: |
| 50 | 53 |
-s, --source, --input DIR Source directory or single file (default: current directory) |
| 51 | 54 |
-d, --destination, --output DIR Destination directory (default: current directory) |
| 55 |
+ --staging-dir DIR Temporary staging directory for intermediate output files |
|
| 56 |
+ Falls back to destination temp files if staging cannot be used |
|
| 52 | 57 |
--mode MODE hardware|auto|quality|compat (default: hardware) |
| 53 | 58 |
--crf N CRF for quality/compat modes (lower = better; default: 20/19) |
| 54 | 59 |
--no-overwrite Skip files that already exist at destination |
@@ -28,6 +28,7 @@ GARMIN_INFERRED_FOCAL_35MM_MM="7" |
||
| 28 | 28 |
|
| 29 | 29 |
SOURCE_DIR="$DEFAULT_SOURCE" |
| 30 | 30 |
DEST_DIR="$DEFAULT_SOURCE" |
| 31 |
+STAGING_DIR="" |
|
| 31 | 32 |
MODE="$DEFAULT_MODE" |
| 32 | 33 |
EXTENSIONS_CSV="$DEFAULT_EXTENSIONS" |
| 33 | 34 |
CRF_OVERRIDE="" |
@@ -39,6 +40,7 @@ VERBOSE=false |
||
| 39 | 40 |
MOVE_SOURCE=false |
| 40 | 41 |
SOURCE_PROVIDED=false |
| 41 | 42 |
DEST_PROVIDED=false |
| 43 |
+STAGING_PROVIDED=false |
|
| 42 | 44 |
FAIL_FAST=true |
| 43 | 45 |
SOURCE_READABLE_MODE="normal" |
| 44 | 46 |
APPLE_REPACK_FALLBACK=true |
@@ -81,6 +83,8 @@ Usage: |
||
| 81 | 83 |
Options: |
| 82 | 84 |
-s, --source, --input DIR Source directory or single file (default: current directory) |
| 83 | 85 |
-d, --destination, --output DIR Destination directory (default: current directory) |
| 86 |
+ --staging-dir DIR Temporary staging directory for intermediate output files |
|
| 87 |
+ (falls back to destination if staging cannot be used) |
|
| 84 | 88 |
--mode MODE Encoding mode (default: hardware); see Encoding Modes below |
| 85 | 89 |
--crf N CRF value for quality/compat modes (lower = better; default: 20/19) |
| 86 | 90 |
--no-overwrite Skip files that already exist at destination (default: overwrite) |
@@ -319,20 +323,39 @@ make_temp_log_file() {
|
||
| 319 | 323 |
|
| 320 | 324 |
make_temp_output_file() {
|
| 321 | 325 |
local target_file="$1" |
| 322 |
- local target_dir target_base target_noext temp_path seed_path |
|
| 326 |
+ local preferred_dir="${2:-}"
|
|
| 327 |
+ local target_dir target_base target_noext temp_path seed_path work_dir |
|
| 323 | 328 |
|
| 324 | 329 |
target_dir="$(dirname "$target_file")" |
| 325 | 330 |
target_base="$(basename "$target_file")" |
| 326 | 331 |
target_noext="${target_base%.*}"
|
| 332 |
+ work_dir="$target_dir" |
|
| 333 |
+ |
|
| 334 |
+ if [[ -n "$preferred_dir" ]]; then |
|
| 335 |
+ work_dir="$preferred_dir" |
|
| 336 |
+ fi |
|
| 337 |
+ |
|
| 338 |
+ seed_path="$(mktemp "$work_dir/.varia_tmp.${target_noext}.XXXXXX" 2>/dev/null || true)"
|
|
| 339 |
+ if [[ -z "$seed_path" && "$work_dir" != "$target_dir" ]]; then |
|
| 340 |
+ seed_path="$(mktemp "$target_dir/.varia_tmp.${target_noext}.XXXXXX" 2>/dev/null || true)"
|
|
| 341 |
+ fi |
|
| 327 | 342 |
|
| 328 |
- seed_path="$(mktemp "$target_dir/.varia_tmp.${target_noext}.XXXXXX" 2>/dev/null || true)"
|
|
| 329 | 343 |
if [[ -n "$seed_path" ]]; then |
| 330 | 344 |
rm -f -- "$seed_path" 2>/dev/null || true |
| 331 | 345 |
temp_path="${seed_path}.mp4"
|
| 332 | 346 |
else |
| 333 |
- temp_path="$target_dir/.varia_tmp.${target_noext}.$$.$RANDOM.mp4"
|
|
| 347 |
+ temp_path="$work_dir/.varia_tmp.${target_noext}.$$.$RANDOM.mp4"
|
|
| 334 | 348 |
if ! touch "$temp_path" >/dev/null 2>&1; then |
| 335 |
- temp_path="" |
|
| 349 |
+ if [[ "$work_dir" != "$target_dir" ]]; then |
|
| 350 |
+ temp_path="$target_dir/.varia_tmp.${target_noext}.$$.$RANDOM.mp4"
|
|
| 351 |
+ if ! touch "$temp_path" >/dev/null 2>&1; then |
|
| 352 |
+ temp_path="" |
|
| 353 |
+ else |
|
| 354 |
+ rm -f -- "$temp_path" 2>/dev/null || true |
|
| 355 |
+ fi |
|
| 356 |
+ else |
|
| 357 |
+ temp_path="" |
|
| 358 |
+ fi |
|
| 336 | 359 |
else |
| 337 | 360 |
rm -f -- "$temp_path" 2>/dev/null || true |
| 338 | 361 |
fi |
@@ -344,8 +367,11 @@ make_temp_output_file() {
|
||
| 344 | 367 |
cleanup_transcode_artifacts() {
|
| 345 | 368 |
local temp_output="$1" |
| 346 | 369 |
local final_output="$2" |
| 370 |
+ local exif_backup_default="${temp_output}_original"
|
|
| 371 |
+ local exif_backup_alt="${temp_output}.exif_original"
|
|
| 347 | 372 |
|
| 348 | 373 |
rm -f -- "$temp_output" 2>/dev/null || true |
| 374 |
+ rm -f -- "$exif_backup_default" "$exif_backup_alt" 2>/dev/null || true |
|
| 349 | 375 |
|
| 350 | 376 |
# Defensive cleanup for previously failed non-atomic runs. |
| 351 | 377 |
if [[ -f "$final_output" && ! -s "$final_output" ]]; then |
@@ -414,6 +440,12 @@ parse_args() {
|
||
| 414 | 440 |
DEST_PROVIDED=true |
| 415 | 441 |
shift 2 |
| 416 | 442 |
;; |
| 443 |
+ --staging-dir) |
|
| 444 |
+ require_value "$1" "${2:-}"
|
|
| 445 |
+ STAGING_DIR="$2" |
|
| 446 |
+ STAGING_PROVIDED=true |
|
| 447 |
+ shift 2 |
|
| 448 |
+ ;; |
|
| 417 | 449 |
--mode) |
| 418 | 450 |
require_value "$1" "${2:-}"
|
| 419 | 451 |
MODE="$2" |
@@ -925,6 +957,22 @@ normalize_dest_dir() {
|
||
| 925 | 957 |
DEST_DIR="$(to_abs_path "$DEST_DIR")" |
| 926 | 958 |
} |
| 927 | 959 |
|
| 960 |
+normalize_staging_dir() {
|
|
| 961 |
+ if [[ "$STAGING_PROVIDED" != true ]]; then |
|
| 962 |
+ STAGING_DIR="" |
|
| 963 |
+ return |
|
| 964 |
+ fi |
|
| 965 |
+ |
|
| 966 |
+ STAGING_DIR="$(to_abs_path "$STAGING_DIR")" |
|
| 967 |
+ [[ -d "$STAGING_DIR" ]] || die "Staging directory not found: $STAGING_DIR" |
|
| 968 |
+ STAGING_DIR="$(cd "$STAGING_DIR" && pwd)" |
|
| 969 |
+ [[ -w "$STAGING_DIR" ]] || die "Staging directory not writable: $STAGING_DIR" |
|
| 970 |
+ |
|
| 971 |
+ if path_is_within "$STAGING_DIR" "$SOURCE_DIR"; then |
|
| 972 |
+ die "Staging directory must not be inside source: staging=$STAGING_DIR source=$SOURCE_DIR" |
|
| 973 |
+ fi |
|
| 974 |
+} |
|
| 975 |
+ |
|
| 928 | 976 |
collect_extensions() {
|
| 929 | 977 |
local raw="$EXTENSIONS_CSV" |
| 930 | 978 |
local token |
@@ -1053,13 +1101,16 @@ process_video_file() {
|
||
| 1053 | 1101 |
|
| 1054 | 1102 |
build_video_args |
| 1055 | 1103 |
|
| 1056 |
- temp_output_file="$(make_temp_output_file "$output_file")" |
|
| 1104 |
+ temp_output_file="$(make_temp_output_file "$output_file" "$STAGING_DIR")" |
|
| 1057 | 1105 |
if [[ -z "$temp_output_file" ]]; then |
| 1058 | 1106 |
ERRORS=$((ERRORS + 1)) |
| 1059 | 1107 |
DESTINATION_FAILURES=$((DESTINATION_FAILURES + 1)) |
| 1060 | 1108 |
log_msg "ERROR" "Could not create temporary output file: $output_file" |
| 1061 | 1109 |
return 3 |
| 1062 | 1110 |
fi |
| 1111 |
+ if [[ "$STAGING_PROVIDED" == true ]] && ! path_is_within "$temp_output_file" "$STAGING_DIR"; then |
|
| 1112 |
+ log_msg "WARN" "Staging unavailable for temp output; using destination directory: $temp_output_file" |
|
| 1113 |
+ fi |
|
| 1063 | 1114 |
|
| 1064 | 1115 |
local cmd=(ffmpeg -hide_banner) |
| 1065 | 1116 |
if [[ "$SOURCE_READABLE_MODE" == "tolerant" ]]; then |
@@ -1397,6 +1448,7 @@ main() {
|
||
| 1397 | 1448 |
|
| 1398 | 1449 |
normalize_source_dir |
| 1399 | 1450 |
normalize_dest_dir |
| 1451 |
+ normalize_staging_dir |
|
| 1400 | 1452 |
collect_extensions |
| 1401 | 1453 |
|
| 1402 | 1454 |
if path_is_within "$DEST_DIR" "$SOURCE_DIR"; then |