@@ -14,6 +14,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). |
||
| 14 | 14 |
- `--unattended` preset (`--delete-source` + `--keep-going`) for long unattended runs |
| 15 | 15 |
- `--staging-dir DIR` to place intermediate transcoding temp files on a fast local path, |
| 16 | 16 |
with automatic fallback to destination temp files when staging is unavailable |
| 17 |
+ or lacks sufficient free space for the current file |
|
| 18 |
+- automatic RAM disk creation on macOS when `--staging-dir` points to a missing |
|
| 19 |
+ `/Volumes/<Name>` path, configurable with `--staging-ramdisk-mb N` |
|
| 20 |
+- `--debug-timing N` to stop after N video files and print timing statistics for quick profiling |
|
| 17 | 21 |
|
| 18 | 22 |
- `cleanup_garmin_varia_media_folder.sh` helper for media-folder hygiene (import + transcoded output): |
| 19 | 23 |
removes AppleDouble artifacts (`._*`), removes zero-size MP4 files, |
@@ -46,8 +50,6 @@ Initial release. |
||
| 46 | 50 |
- Auto single-file mode when `--source` is a file path |
| 47 | 51 |
- Quiet default output: one progress line per file with elapsed seconds |
| 48 | 52 |
- Summary line with counts and timing after each run |
| 49 |
-- JSON sidecar copy (preserves relative paths) |
|
| 50 |
-- `telemetry_manifest.json` placeholder written to destination |
|
| 51 | 53 |
- Destination-inside-source guard (hard error) |
| 52 | 54 |
- At-least-one-of `--source`/`--destination` requirement |
| 53 | 55 |
- Fail-fast: encoding chain stops on first ffmpeg error |
@@ -1,8 +1,7 @@ |
||
| 1 | 1 |
# VariaReEncoder |
| 2 | 2 |
|
| 3 | 3 |
Batch transcoder for Garmin Varia dashcam footage. Converts H.264 source clips to HEVC MP4, |
| 4 |
-optimized for Apple Photos / QuickTime compatibility, with sidecar JSON copy and a telemetry |
|
| 5 |
-manifest placeholder for a future FIT sync pipeline. |
|
| 4 |
+optimized for Apple Photos / QuickTime compatibility. |
|
| 6 | 5 |
|
| 7 | 6 |
## Requirements |
| 8 | 7 |
|
@@ -31,6 +30,9 @@ manifest placeholder for a future FIT sync pipeline. |
||
| 31 | 30 |
# NFS destination with local staging for temp outputs |
| 32 | 31 |
./garmin_varia_transcode.sh -s SampleFootage -d ~/Autofs/xdev/is-baobab/nvme0n1/@backup/Garmin --staging-dir /tmp/varia_staging |
| 33 | 32 |
|
| 33 |
+# Timing debug: process first 20 files then stop and print final statistics |
|
| 34 |
+./garmin_varia_transcode.sh -s SampleFootage -d Output --debug-timing 20 |
|
| 35 |
+ |
|
| 34 | 36 |
# Media cleanup helper (Apple artifacts, zero-byte MP4, duplicate suffix normalization) |
| 35 | 37 |
./cleanup_garmin_varia_media_folder.sh --dry-run ~/Autofs/xdev/autonas/ext01/@Camera/import |
| 36 | 38 |
``` |
@@ -53,7 +55,11 @@ Options: |
||
| 53 | 55 |
-s, --source, --input DIR Source directory or single file (default: current directory) |
| 54 | 56 |
-d, --destination, --output DIR Destination directory (default: current directory) |
| 55 | 57 |
--staging-dir DIR Temporary staging directory for intermediate output files |
| 56 |
- Falls back to destination temp files if staging cannot be used |
|
| 58 |
+ Falls back to destination temp files if staging is unavailable |
|
| 59 |
+ or does not have enough free space for current file |
|
| 60 |
+ --staging-ramdisk-mb N RAM disk size in MB when auto-creating missing /Volumes staging |
|
| 61 |
+ directory on macOS (default: 8192) |
|
| 62 |
+ --debug-timing N Process at most N video files, then stop and print timing stats |
|
| 57 | 63 |
--mode MODE hardware|auto|quality|compat (default: hardware) |
| 58 | 64 |
--crf N CRF for quality/compat modes (lower = better; default: 20/19) |
| 59 | 65 |
--no-overwrite Skip files that already exist at destination |
@@ -75,8 +81,6 @@ Options: |
||
| 75 | 81 |
- HEVC outputs tagged `hvc1` (required for Apple Photos / QuickTime) |
| 76 | 82 |
- Directory structure under source is preserved at destination |
| 77 | 83 |
- File timestamps copied from source (`touch -r`) |
| 78 |
-- JSON sidecars copied 1:1 to destination |
|
| 79 |
-- `telemetry_manifest.json` written to destination as a placeholder for a future FIT sync pipeline |
|
| 80 | 84 |
|
| 81 | 85 |
## Output Format (quiet mode, default) |
| 82 | 86 |
|
@@ -71,8 +71,6 @@ VIDEO_FILES=() |
||
| 71 | 71 |
|
| 72 | 72 |
VIDEOS_PROCESSED=0 |
| 73 | 73 |
VIDEOS_SKIPPED=0 |
| 74 |
-JSON_COPIED=0 |
|
| 75 |
-JSON_SKIPPED=0 |
|
| 76 | 74 |
ERRORS=0 |
| 77 | 75 |
INVALID_SOURCES_SKIPPED=0 |
| 78 | 76 |
DESTINATION_FAILURES=0 |
@@ -143,8 +141,6 @@ Behavior: |
||
| 143 | 141 |
- Transcoding encoder/mode is written in metadata (Software) |
| 144 | 142 |
- Original directory structure is preserved under destination |
| 145 | 143 |
- When --source points to a file, only that file is processed |
| 146 |
- - JSON sidecar files found in source are copied 1:1 to destination |
|
| 147 |
- - telemetry_manifest.json is created in destination as a placeholder contract |
|
| 148 | 144 |
|
| 149 | 145 |
Examples: |
| 150 | 146 |
./garmin_varia_transcode.sh -s SampleFootage -d /Volumes/Archive |
@@ -321,8 +317,6 @@ print_final_report() {
|
||
| 321 | 317 |
printf '| %-25s | %5s |\n' "Videos skipped" "$VIDEOS_SKIPPED" |
| 322 | 318 |
printf '| %-25s | %5s |\n' "Invalid sources skipped" "$INVALID_SOURCES_SKIPPED" |
| 323 | 319 |
printf '| %-25s | %5s |\n' "Destination failures" "$DESTINATION_FAILURES" |
| 324 |
- printf '| %-25s | %5s |\n' "JSON copied" "$JSON_COPIED" |
|
| 325 |
- printf '| %-25s | %5s |\n' "JSON skipped" "$JSON_SKIPPED" |
|
| 326 | 320 |
printf '| %-25s | %5s |\n' "Errors" "$ERRORS" |
| 327 | 321 |
printf '+---------------------------+-------+\n' |
| 328 | 322 |
|
@@ -1575,113 +1569,6 @@ process_video_file() {
|
||
| 1575 | 1569 |
return 0 |
| 1576 | 1570 |
} |
| 1577 | 1571 |
|
| 1578 |
-copy_one_json() {
|
|
| 1579 |
- local json_file="$1" |
|
| 1580 |
- local rel_json dst_json dst_dir |
|
| 1581 |
- |
|
| 1582 |
- rel_json="$(rel_path_from_source "$json_file")" |
|
| 1583 |
- dst_json="$DEST_DIR/$rel_json" |
|
| 1584 |
- dst_dir="$(dirname "$dst_json")" |
|
| 1585 |
- |
|
| 1586 |
- mkdir -p "$dst_dir" |
|
| 1587 |
- |
|
| 1588 |
- if [[ -f "$dst_json" && "$OVERWRITE" != true ]]; then |
|
| 1589 |
- JSON_SKIPPED=$((JSON_SKIPPED + 1)) |
|
| 1590 |
- vlog_msg "SKIP" "JSON exists: $dst_json" |
|
| 1591 |
- return |
|
| 1592 |
- fi |
|
| 1593 |
- |
|
| 1594 |
- if [[ "$DRY_RUN" == true ]]; then |
|
| 1595 |
- JSON_COPIED=$((JSON_COPIED + 1)) |
|
| 1596 |
- log_msg "DRY-RUN" "Would copy JSON: $json_file -> $dst_json" |
|
| 1597 |
- return |
|
| 1598 |
- fi |
|
| 1599 |
- |
|
| 1600 |
- if cp -f "$json_file" "$dst_json"; then |
|
| 1601 |
- touch -r "$json_file" "$dst_json" || true |
|
| 1602 |
- JSON_COPIED=$((JSON_COPIED + 1)) |
|
| 1603 |
- vlog_msg "INFO" "Copied JSON: $dst_json" |
|
| 1604 |
- else |
|
| 1605 |
- ERRORS=$((ERRORS + 1)) |
|
| 1606 |
- log_msg "ERROR" "Failed to copy JSON: $json_file" |
|
| 1607 |
- fi |
|
| 1608 |
-} |
|
| 1609 |
- |
|
| 1610 |
-copy_sidecars_json() {
|
|
| 1611 |
- local json_files=() |
|
| 1612 |
- |
|
| 1613 |
- if [[ -n "$SINGLE_FILE" ]]; then |
|
| 1614 |
- local single_abs rel_path candidate |
|
| 1615 |
- single_abs="$(to_abs_path "$SINGLE_FILE")" |
|
| 1616 |
- rel_path="$(rel_path_from_source "$single_abs")" |
|
| 1617 |
- candidate="$SOURCE_DIR/${rel_path%.*}.json"
|
|
| 1618 |
- if [[ -f "$candidate" ]]; then |
|
| 1619 |
- json_files+=("$candidate")
|
|
| 1620 |
- fi |
|
| 1621 |
- else |
|
| 1622 |
- while IFS= read -r -d '' jf; do |
|
| 1623 |
- json_files+=("$jf")
|
|
| 1624 |
- done < <( |
|
| 1625 |
- if [[ "$RECURSIVE" == true ]]; then |
|
| 1626 |
- find "$SOURCE_DIR" -path "$DEST_DIR" -prune -o -type f -iname '*.json' -print0 |
|
| 1627 |
- else |
|
| 1628 |
- find "$SOURCE_DIR" -maxdepth 1 -type f -iname '*.json' -print0 |
|
| 1629 |
- fi |
|
| 1630 |
- ) |
|
| 1631 |
- fi |
|
| 1632 |
- |
|
| 1633 |
- if [[ ${#json_files[@]} -eq 0 ]]; then
|
|
| 1634 |
- vlog_msg "INFO" "No JSON sidecars found to copy" |
|
| 1635 |
- return |
|
| 1636 |
- fi |
|
| 1637 |
- |
|
| 1638 |
- local jf |
|
| 1639 |
- for jf in "${json_files[@]}"; do
|
|
| 1640 |
- if is_apple_noise_file "$jf"; then |
|
| 1641 |
- vlog_msg "SKIP" "Ignoring Apple artifact JSON: $jf" |
|
| 1642 |
- continue |
|
| 1643 |
- fi |
|
| 1644 |
- copy_one_json "$jf" |
|
| 1645 |
- done |
|
| 1646 |
-} |
|
| 1647 |
- |
|
| 1648 |
-write_manifest() {
|
|
| 1649 |
- local manifest_path="$DEST_DIR/telemetry_manifest.json" |
|
| 1650 |
- |
|
| 1651 |
- if [[ -f "$manifest_path" && "$OVERWRITE" != true ]]; then |
|
| 1652 |
- vlog_msg "SKIP" "Manifest exists: $manifest_path" |
|
| 1653 |
- return |
|
| 1654 |
- fi |
|
| 1655 |
- |
|
| 1656 |
- if [[ "$DRY_RUN" == true ]]; then |
|
| 1657 |
- log_msg "DRY-RUN" "Would write manifest: $manifest_path" |
|
| 1658 |
- return |
|
| 1659 |
- fi |
|
| 1660 |
- |
|
| 1661 |
- mkdir -p "$DEST_DIR" |
|
| 1662 |
- |
|
| 1663 |
- cat > "$manifest_path" <<EOF |
|
| 1664 |
-{
|
|
| 1665 |
- "schema_version": "0.1-draft", |
|
| 1666 |
- "purpose": "placeholder contract for future FIT-to-sidecar sync pipeline", |
|
| 1667 |
- "fields_target": [ |
|
| 1668 |
- "power_w", |
|
| 1669 |
- "speed_kmh", |
|
| 1670 |
- "heart_rate_bpm", |
|
| 1671 |
- "cadence_rpm", |
|
| 1672 |
- "gps" |
|
| 1673 |
- ], |
|
| 1674 |
- "sync_methods": [ |
|
| 1675 |
- "auto_timestamp_plus_offset", |
|
| 1676 |
- "manual_offset_ms" |
|
| 1677 |
- ], |
|
| 1678 |
- "notes": "Current release copies existing JSON sidecars only; FIT parsing is not implemented yet." |
|
| 1679 |
-} |
|
| 1680 |
-EOF |
|
| 1681 |
- |
|
| 1682 |
- vlog_msg "INFO" "Wrote manifest: $manifest_path" |
|
| 1683 |
-} |
|
| 1684 |
- |
|
| 1685 | 1572 |
main() {
|
| 1686 | 1573 |
local total_started_at total_ended_at total_elapsed_sec total_elapsed_fmt |
| 1687 | 1574 |
total_started_at="$(date +%s)" |
@@ -1774,14 +1661,13 @@ main() {
|
||
| 1774 | 1661 |
done |
| 1775 | 1662 |
|
| 1776 | 1663 |
if [[ "$DEBUG_TIMING_STOPPED" == true ]]; then |
| 1777 |
- log_msg "INFO" "Skipping sidecar copy and manifest because debug timing mode requested early stop" |
|
| 1664 |
+ log_msg "INFO" "Skipping sidecar handling because debug timing mode requested early stop" |
|
| 1778 | 1665 |
elif [[ "$STOP_AFTER_CURRENT" == true ]]; then |
| 1779 |
- log_msg "INFO" "Skipping sidecar copy and manifest because run was stopped by user" |
|
| 1666 |
+ log_msg "INFO" "Skipping sidecar handling because run was stopped by user" |
|
| 1780 | 1667 |
elif [[ "$ERRORS" -eq 0 ]]; then |
| 1781 |
- copy_sidecars_json |
|
| 1782 |
- write_manifest |
|
| 1668 |
+ log_msg "INFO" "Sidecar handling is disabled for this release" |
|
| 1783 | 1669 |
else |
| 1784 |
- log_msg "INFO" "Skipping sidecar copy and manifest because encoding ended with errors" |
|
| 1670 |
+ log_msg "INFO" "Skipping sidecar handling because encoding ended with errors" |
|
| 1785 | 1671 |
fi |
| 1786 | 1672 |
|
| 1787 | 1673 |
total_ended_at="$(date +%s)" |