MediaImporter / INCIDENTS.md
Newer Older
206 lines | 6.937kb
Bogdan Timofte authored 2 weeks ago
1
# Incident Reports
2

            
3
## 2026-05-15 - GoPro Chapter Import Overwrite
4

            
5
### Summary
6

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

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

            
16
```text
17
/Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro/2026-05-15/2026-05-15_22-20-09.mp4
18
```
19

            
20
### User Command
21

            
22
```bash
23
media-importer.sh -s "/Volumes/NO NAME" -d ~/Autofs/xdev/autonas/ext01/@Camera/GoPro
24
```
25

            
26
Effective settings from the log:
27

            
28
```text
29
Organization pattern: ymd
30
Destination:         /Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro
31
Keep originals:      No
32
Verify mode:         size
33
Dry run:             No
34
Keep empty dirs:     No
35
Source patterns:
36
  - /Volumes/NO NAME
37
```
38

            
39
### Impact
40

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

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

            
51
### Evidence
52

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

            
55
```text
56
GX011621.MP4 -> 2026-05-15_22-20-09.mp4
57
GX021621.MP4 -> 2026-05-15_22-20-09.mp4
58
GX031621.MP4 -> 2026-05-15_22-20-09.mp4
59
GX041621.MP4 -> 2026-05-15_22-20-09.mp4
60
GX051621.MP4 -> 2026-05-15_22-20-09.mp4
61
GX061621.MP4 -> 2026-05-15_22-20-09.mp4
62
GX071621.MP4 -> 2026-05-15_22-20-09.mp4
63
GX081621.MP4 -> 2026-05-15_22-20-09.mp4
64
GX091621.MP4 -> 2026-05-15_22-20-09.mp4
65
```
66

            
67
The script emitted warnings like:
68

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

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

            
76
Post-incident inspection found:
77

            
78
```text
79
/Users/bogdan/Autofs/xdev/autonas/ext01/@Camera/GoPro/2026-05-15/2026-05-15_22-20-09.mp4
80
size: 586M
81
duration: 0:01:49
82
QuickTime CreateDate: 2026:05:15 19:20:09
83
```
84

            
85
The duration matched the surviving low-resolution proxy:
86

            
87
```text
88
GL091621.LRV duration: 108.58s
89
```
90

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

            
94
### Timeline Recovered From Card Artifacts
95

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

            
100
```text
101
GX011621.THM  2026-05-15 19:20:10
102
GX021621.THM  2026-05-15 19:31:58
103
GX031621.THM  2026-05-15 19:43:44
104
GX041621.THM  2026-05-15 19:55:32
105
GX051621.THM  2026-05-15 20:07:20
106
GX061621.THM  2026-05-15 20:19:08
107
GX071621.THM  2026-05-15 20:30:54
108
GX081621.THM  2026-05-15 20:42:42
109
GX091621.THM  2026-05-15 20:54:30
110
```
111

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

            
115
### Root Cause
116

            
117
There were two separate issues:
118

            
119
1. Filename collision handling was unsafe.
120
   - Multiple GoPro chapters had the same metadata timestamp.
121
   - `generate_destination_path` generated the same destination filename for all
122
     files.
123
   - `process_file` detected the existing destination but continued.
124
   - The copy operation overwrote the destination.
125
   - Verification then validated the newly overwritten destination.
126
   - The source file was deleted.
127

            
128
2. Timestamp interpretation was easy to misunderstand.
129
   - The script converts QuickTime `CreateDate` values from UTC to local time.
130
   - The accident log used `22:20:09`, while surviving `.THM/.LRV` filesystem
131
     timestamps showed `19:20`.
132
   - This looks like QuickTime UTC conversion rather than a GoPro firmware
133
     regression.
134
   - This was not the data-loss mechanism, but it affected the visible names.
135

            
136
### Corrective Actions
137

            
138
Implemented in `media-importer.sh`:
139

            
140
- Added explicit no-overwrite checks in `safe_cp` and `safe_mv`.
141
- Changed copy/move flow to copy into a temporary file, verify the temporary
142
  file, then move it into place only if the final destination does not exist.
143
- Added explicit destination conflict handling. Unattended runs append numeric
144
  suffixes; interactive runs ask the user and can apply the decision to all
145
  similar conflicts during long imports.
146

            
147
```text
148
2026-05-15_19-20-09.mp4
149
2026-05-15_19-20-09_1.mp4
150
2026-05-15_19-20-09_2.mp4
151
```
152

            
153
- Sorted discovered files before processing to make chapter order stable.
154
- Added `--date-source filesystem` for imports where filesystem timestamps are
155
  the source of truth.
156
- GoPro `GX/GH/GP*.MP4` files now prefer filesystem timestamps in default
157
  `auto` mode, using matching `THM`, matching `LRV`, then MP4 mtime as fallback.
158
- GoPro imports that use filesystem dates automatically write the corrected clip
159
  start time into destination metadata before deleting the source.
160
- Added `--sync-metadata` to force the same metadata write for non-GoPro cases.
161
- Prevented accidental execution when the script is sourced rather than run.
162

            
163
Implemented in `test_runner.sh`:
164

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

            
170
Verification command:
171

            
172
```bash
173
./test_runner.sh timestamp-collision
174
```
175

            
176
### Operational Guidance
177

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

            
180
```bash
181
./media-importer.sh -s "/Volumes/GOPRO" -d "$HOME/Autofs/xdev/autonas/ext01/@Camera/GoPro" --dry-run -v
182
```
183

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

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

            
194
After dry-run review, run the same command without `--dry-run`.
195

            
196
### Regression Rule
197

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

            
201
```bash
202
./test_runner.sh timestamp-collision
203
```
204

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