MediaImporter / INCIDENTS.md
1 contributor
206 lines | 6.937kb

Incident Reports

2026-05-15 - GoPro Chapter Import Overwrite

Summary

An import from /Volumes/NO NAME into ~/Autofs/xdev/autonas/ext01/@Camera/GoPro processed 9 GoPro MP4 files and reported success for all of them, but the import collapsed multiple chapter files onto one destination filename. Because the script continued after detecting the collision, each later copy overwrote the previous destination file and the source file was then deleted after verification.

The surviving destination file appears to be the last chapter (GX091621.MP4), stored as:

/Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro/2026-05-15/2026-05-15_22-20-09.mp4

User Command

media-importer.sh -s "/Volumes/NO NAME" -d ~/Autofs/xdev/autonas/ext01/@Camera/GoPro

Effective settings from the log:

Organization pattern: ymd
Destination:         /Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro
Keep originals:      No
Verify mode:         size
Dry run:             No
Keep empty dirs:     No
Source patterns:
  - /Volumes/NO NAME

Impact

  • Total files found: 9
  • Total size found: 30GB
  • Script reported: 9 successfully processed, 0 errors
  • Actual observed result: only one full-resolution MP4 remained in destination
  • Source MP4 files were removed from the card
  • Remaining card artifacts included .LRV proxy files and .THM thumbnails

The original full-resolution data for the first 8 chapters was not recoverable from the mounted filesystem at the time of inspection. No recovery tool was run.

Evidence

The import log repeatedly mapped each source chapter to the same destination:

GX011621.MP4 -> 2026-05-15_22-20-09.mp4
GX021621.MP4 -> 2026-05-15_22-20-09.mp4
GX031621.MP4 -> 2026-05-15_22-20-09.mp4
GX041621.MP4 -> 2026-05-15_22-20-09.mp4
GX051621.MP4 -> 2026-05-15_22-20-09.mp4
GX061621.MP4 -> 2026-05-15_22-20-09.mp4
GX071621.MP4 -> 2026-05-15_22-20-09.mp4
GX081621.MP4 -> 2026-05-15_22-20-09.mp4
GX091621.MP4 -> 2026-05-15_22-20-09.mp4

The script emitted warnings like:

Destination already exists: .../2026-05-15_22-20-09.mp4 - proceeding to move/copy and letting external tools handle conflicts

That warning was the bug signal. The script should have chosen a unique destination or failed closed. Instead it proceeded.

Post-incident inspection found:

/Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro/2026-05-15/2026-05-15_22-20-09.mp4
size: 586M
duration: 0:01:49
QuickTime CreateDate: 2026:05:15 19:20:09

The duration matched the surviving low-resolution proxy:

GL091621.LRV duration: 108.58s

This strongly indicates that the surviving full-resolution file was the last chapter (GX091621.MP4).

Timeline Recovered From Card Artifacts

The full-resolution MP4 files were gone from /Volumes/NO NAME/DCIM/100GOPRO, but GoPro sidecar/proxy files remained. The .THM timestamps preserved a useful chapter timeline:

GX011621.THM  2026-05-15 19:20:10
GX021621.THM  2026-05-15 19:31:58
GX031621.THM  2026-05-15 19:43:44
GX041621.THM  2026-05-15 19:55:32
GX051621.THM  2026-05-15 20:07:20
GX061621.THM  2026-05-15 20:19:08
GX071621.THM  2026-05-15 20:30:54
GX081621.THM  2026-05-15 20:42:42
GX091621.THM  2026-05-15 20:54:30

The .LRV proxy files also remained and can preserve low-resolution visual content when full-resolution MP4 recovery is not possible.

Root Cause

There were two separate issues:

  1. Filename collision handling was unsafe.

    • Multiple GoPro chapters had the same metadata timestamp.
    • generate_destination_path generated the same destination filename for all files.
    • process_file detected the existing destination but continued.
    • The copy operation overwrote the destination.
    • Verification then validated the newly overwritten destination.
    • The source file was deleted.
  2. Timestamp interpretation was easy to misunderstand.

    • The script converts QuickTime CreateDate values from UTC to local time.
    • The accident log used 22:20:09, while surviving .THM/.LRV filesystem timestamps showed 19:20.
    • This looks like QuickTime UTC conversion rather than a GoPro firmware regression.
    • This was not the data-loss mechanism, but it affected the visible names.

Corrective Actions

Implemented in media-importer.sh:

  • Added explicit no-overwrite checks in safe_cp and safe_mv.
  • Changed copy/move flow to copy into a temporary file, verify the temporary file, then move it into place only if the final destination does not exist.
  • Added explicit destination conflict handling. Unattended runs append numeric suffixes; interactive runs ask the user and can apply the decision to all similar conflicts during long imports.
2026-05-15_19-20-09.mp4
2026-05-15_19-20-09_1.mp4
2026-05-15_19-20-09_2.mp4
  • Sorted discovered files before processing to make chapter order stable.
  • Added --date-source filesystem for imports where filesystem timestamps are the source of truth.
  • GoPro GX/GH/GP*.MP4 files now prefer filesystem timestamps in default auto mode, using matching THM, matching LRV, then MP4 mtime as fallback.
  • GoPro imports that use filesystem dates automatically write the corrected clip start time into destination metadata before deleting the source.
  • Added --sync-metadata to force the same metadata write for non-GoPro cases.
  • Prevented accidental execution when the script is sourced rather than run.

Implemented in test_runner.sh:

  • Added timestamp-collision regression test.
  • The test creates multiple MP4 files with identical CreateDate values.
  • It runs move mode and verifies that all files arrive at unique destination paths, with no overwrite collapse.

Verification command:

./test_runner.sh timestamp-collision

Operational Guidance

For the next GoPro import, start with dry-run:

./media-importer.sh -s "/Volumes/GOPRO" -d "$HOME/Autofs/xdev/autonas/ext01/@Camera/GoPro" --dry-run -v

Review the dry-run output before removing --dry-run. Expected healthy output:

  • each GX*.MP4 maps to a distinct destination path;
  • date source says Filesystem:GX*.THM, Filesystem:GX*.LRV, or the MP4 basename depending on the best available GoPro filesystem source;
  • each GoPro line says metadata date would be synced;
  • no line says that an existing destination will be overwritten;
  • any unavoidable conflict is either a deliberate numeric suffix or an explicit user choice.

After dry-run review, run the same command without --dry-run.

Regression Rule

Any future change to date extraction, filename generation, verification, copy, move, conflict handling, or GoPro/Garmin/Varia media behavior must run:

./test_runner.sh timestamp-collision

The test must fail if two source media files can collapse into one destination file while the script still reports success.