fix: FCPXML honors embedded source timecode (DJI import, follow-up to #34) - #35
Merged
Merged
Conversation
The 3.5.6 fix corrected the frame grid but still hard-coded asset start="0/1s" with 0-based asset-clip starts. Final Cut keys an asset's source timecode off the media's *real* timecode, so for footage that carries time-of-day timecode (DJI, Sony, etc.) every exported edit fell outside the asset's [start, start+duration] range and FCP rejected it with "Invalid edit with no respective media" — the same symptom 3.5.6 was meant to fix, reproduced on a real DJI 29.97 clip. Per the FCPXML spec, asset.start must equal the media's embedded source timecode and each asset-clip.start must fall within the asset's range. Fix: - exporters/media_probe.py: get_video_start_timecode_frames() reads the `timecode` tag (format or stream / tmcd) via ffprobe; timecode_to_frames() parses HH:MM:SS:FF (and ;FF drop-frame) to a whole-frame count on the nominal grid. - fcpxml_export.py: both generators take start_tc_frames; asset.start = TC, and every source-side start (asset-clip + keyword + chapter-marker) = TC + in-point. Timeline offsets stay 0-based. TC=0 media is unchanged (asset start="0/1s"), so non-timecoded footage is byte-identical. - Threaded start_tc_frames through the exporter interface (base + FCPXML; Premiere/EDL accept it for parity, source-TC handling there is a TODO) and probed it at both app.py export routes. Tests: tests/test_fcpxml_timecode.py — timecode parsing (NDF/DF), asset start == embedded TC, every clip within the asset's TC range, story export, and TC=0 back-compat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #34 / v3.5.6. A user on DJI footage reported the same "Invalid edit with no respective media" error after updating. Their exported FCPXML (29.97, 4K) was structurally clean and frame-aligned — so 3.5.6's grid fix was working — but the asset still declared
start="0/1s".Root cause
Per the FCPXML spec, an
<asset>'sstartis its source timecode, and every<asset-clip start>must fall within[asset.start, asset.start+duration]. DJI/Sony cameras stamp time-of-day timecode (the user's clip:2026-04-10 14:09:42). Doza hard-codedstart="0/1s"with 0-based clip starts, so when FCP resolved the real media (TC ≈ 14:09:42), every edit landed outside the asset's timecode range → rejected. This also explains why importing into a project that already had the media failed.Fix
exporters/media_probe.py:get_video_start_timecode_frames()reads thetimecodetag (format ortmcdstream) via ffprobe;timecode_to_frames()parsesHH:MM:SS:FF(+;drop-frame) to a whole-frame count.fcpxml_export.py: both generators takestart_tc_frames;asset.start = TCand every source-sidestart(asset-clip + keyword + chapter-marker) =TC + in-point. Timeline offsets stay 0-based. TC=0 media is byte-identical to before — non-timecoded footage is unaffected.start_tc_framesthrough the exporter interface and probed it at bothapp.pyexport routes. (Premiere/EDL accept the kwarg for parity; source-TC handling there is a follow-up.)Tests
tests/test_fcpxml_timecode.py— timecode parsing (NDF/DF/invalid),asset.start == embedded TC, every clip within the asset's TC range, story export, and TC=0 back-compat. 182 FCPXML/exporter tests pass.Version → 3.5.7.
🤖 Generated with Claude Code