def test_roundtrip_disk2mem2disk(self): # somefile.xml -> OTIO timeline = adapters.read_from_file(FCP7_XML_EXAMPLE_PATH) tmp_path = tempfile.mkstemp(suffix=".xml", text=True)[1] # somefile.xml -> OTIO -> tempfile.xml adapters.write_to_file(timeline, tmp_path) # somefile.xml -> OTIO -> tempfile.xml -> OTIO result = adapters.read_from_file(tmp_path) # TODO: OTIO doesn't support linking items for the moment, so the # adapter reads links to the metadata, but doesn't write them. # See _dict_to_xml_tree for more information. def scrub_md_dicts(timeline): def scrub_displayformat(md_dict): for ignore_key in {"link"}: try: del(md_dict[ignore_key]) except KeyError: pass for value in list(md_dict.values()): try: value.items() scrub_displayformat(value) except AttributeError: pass for child in timeline.tracks.each_child(): scrub_displayformat(child.metadata) try: scrub_displayformat(child.media_reference.metadata) except AttributeError: pass # media reference bug, ensure that these match self.assertJsonEqual( result.tracks[0][1].media_reference, timeline.tracks[0][1].media_reference ) scrub_md_dicts(result) scrub_md_dicts(timeline) self.assertJsonEqual(result, timeline) self.assertIsOTIOEquivalentTo(result, timeline) # But the xml text on disk is not identical because otio has a subset # of features to xml and we drop all the nle specific preferences. with open(FCP7_XML_EXAMPLE_PATH, "r") as original_file: with open(tmp_path, "r") as output_file: self.assertNotEqual(original_file.read(), output_file.read())
def test_read_generators(self): timeline = adapters.read_from_file(GENERATOR_XML_EXAMPLE_PATH) video_track = timeline.tracks[0] audio_track = timeline.tracks[3] self.assertEqual(len(video_track), 6) self.assertEqual(len(audio_track), 3) # Check all video items are generators self.assertTrue( all( isinstance(item.media_reference, schema.GeneratorReference) for item in video_track ) ) # Check the video generator kinds self.assertEqual( [clip.media_reference.generator_kind for clip in video_track], ["Slug", "Slug", "Color", "Slug", "Slug", "GraphicAndType"], ) # Check all non-gap audio items are generators self.assertTrue( all( isinstance(item.media_reference, schema.GeneratorReference) for item in video_track if not isinstance(item, schema.Gap) ) )
def test_hiero_flavored_xml(self): timeline = adapters.read_from_file(HIERO_XML_PATH) self.assertTrue(len(timeline.tracks), 1) self.assertTrue(timeline.tracks[0].name == 'Video 1') clips = [c for c in timeline.tracks[0].each_clip()] self.assertTrue(len(clips), 2) self.assertTrue(clips[0].name == 'A160C005_171213_R0MN') self.assertTrue(clips[1].name == '/') self.assertTrue( isinstance( clips[0].media_reference, schema.ExternalReference ) ) self.assertTrue( isinstance( clips[1].media_reference, schema.MissingReference ) ) source_range = opentime.TimeRange( start_time=opentime.RationalTime(1101071, 24), duration=opentime.RationalTime(1055, 24) ) self.assertTrue(clips[0].source_range == source_range) available_range = opentime.TimeRange( start_time=opentime.RationalTime(1101071, 24), duration=opentime.RationalTime(1055, 24) ) self.assertTrue(clips[0].available_range() == available_range) clip_1_range = clips[1].available_range() self.assertEqual( clip_1_range, opentime.TimeRange( opentime.RationalTime(), opentime.RationalTime(1, 24), ) ) # Test serialization tmp_path = tempfile.mkstemp(suffix=".xml", text=True)[1] adapters.write_to_file(timeline, tmp_path) # Similar to the test_roundtrip_disk2mem2disk above # the track name element among others will not be present in a new xml. with open(HIERO_XML_PATH, "r") as original_file: with open(tmp_path, "r") as output_file: self.assertNotEqual(original_file.read(), output_file.read())
def test_read(self): timeline = adapters.read_from_file(FCP7_XML_EXAMPLE_PATH) self.assertTrue(timeline is not None) self.assertEqual(len(timeline.tracks), 8) video_tracks = [ t for t in timeline.tracks if t.kind == schema.TrackKind.Video ] audio_tracks = [ t for t in timeline.tracks if t.kind == schema.TrackKind.Audio ] self.assertEqual(len(video_tracks), 4) self.assertEqual(len(audio_tracks), 4) video_clip_names = (("", 'sc01_sh010_anim.mov'), ("", 'sc01_sh010_anim.mov', "", 'sc01_sh020_anim.mov', 'sc01_sh030_anim.mov', 'Cross Dissolve', "", 'sc01_sh010_anim'), ("", 'test_title'), ("", 'sc01_master_layerA_sh030_temp.mov', 'Cross Dissolve', 'sc01_sh010_anim.mov')) for n, track in enumerate(video_tracks): self.assertTupleEqual(tuple(c.name for c in track), video_clip_names[n]) audio_clip_names = (("", 'sc01_sh010_anim.mov', "", 'sc01_sh010_anim.mov'), ("", 'sc01_placeholder.wav', "", 'sc01_sh010_anim'), ("", 'track_08.wav'), ("", 'sc01_master_layerA_sh030_temp.mov', 'sc01_sh010_anim.mov')) for n, track in enumerate(audio_tracks): self.assertTupleEqual(tuple(c.name for c in track), audio_clip_names[n]) video_clip_durations = (((536, 30.0), (100, 30.0)), ((13, 30.0), (100, 30.0), (52, 30.0), (157, 30.0), (235, 30.0), ((19, 30.0), (0, 30.0)), (79, 30.0), (320, 30.0)), ((15, 30.0), (941, 30.0)), ((956, 30.0), (208, 30.0), ((12, 30.0), (13, 30.0)), (82, 30.0))) for t, track in enumerate(video_tracks): for c, clip in enumerate(track): if isinstance(clip, schema.Transition): self.assertEqual( clip.in_offset, opentime.RationalTime(*video_clip_durations[t][c][0])) self.assertEqual( clip.out_offset, opentime.RationalTime(*video_clip_durations[t][c][1])) else: self.assertEqual( clip.source_range.duration, opentime.RationalTime(*video_clip_durations[t][c])) audio_clip_durations = (((13, 30.0), (100, 30.0), (423, 30.0), (100, 30.0), (423, 30.0)), ((335, 30.0), (170, 30.0), (131, 30.0), (294, 30.0), (34, 30.0), (124, 30.0)), ((153, 30.0), (198, 30.0)), ((956, 30.0), (221, 30.0), (94, 30.0))) for t, track in enumerate(audio_tracks): for c, clip in enumerate(track): self.assertEqual( clip.source_range.duration, opentime.RationalTime(*audio_clip_durations[t][c])) timeline_marker_names = ('My MArker 1', 'dsf', "") for n, marker in enumerate(timeline.tracks.markers): self.assertEqual(marker.name, timeline_marker_names[n]) timeline_marker_start_times = ((113, 30.0), (492, 30.0), (298, 30.0)) for n, marker in enumerate(timeline.tracks.markers): self.assertEqual( marker.marked_range.start_time, opentime.RationalTime(*timeline_marker_start_times[n])) timeline_marker_comments = ('so, this happened', 'fsfsfs', None) for n, marker in enumerate(timeline.tracks.markers): self.assertEqual( marker.metadata.get('fcp_xml', {}).get('comment'), timeline_marker_comments[n]) clip_with_marker = video_tracks[1][4] clip_marker = clip_with_marker.markers[0] self.assertEqual(clip_marker.name, "") self.assertEqual(clip_marker.marked_range.start_time, opentime.RationalTime(73, 30.0)) self.assertEqual( clip_marker.metadata.get('fcp_xml', {}).get('comment'), None)
def test_xml_with_empty_elements(self): timeline = adapters.read_from_file(EMPTY_ELEMENT_XML_PATH) # Spot-check the EDL, this one would throw exception on load before self.assertEqual(len(timeline.video_tracks()), 12) self.assertEqual(len(timeline.video_tracks()[0]), 34)