Skip to content

Commit ef9929c

Browse files
authored
Fix for ALEs without blank lines between sections (#3)
* Handle files where sections aren't separated with line breaks and add explicit error if FPS is missing in header rather than letting an implicit NameError come up later * Added handling for extra blank lines after the Column statement.\nCo-authored-by: Sean Wallitsch <[email protected]> Signed-off-by: Eric Reinecke <[email protected]> --------- Signed-off-by: Eric Reinecke <[email protected]> Signed-off-by: Eric Reinecke <[email protected]>
1 parent cc9e7a1 commit ef9929c

File tree

4 files changed

+153
-31
lines changed

4 files changed

+153
-31
lines changed

src/otio_ale_adapter/ale.py

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
DEFAULT_VIDEO_FORMAT = '1080'
1111
ASC_SOP_REGEX = re.compile(r'(-*\d+\.\d+)')
12+
ALE_SECTION_DESIGNATORS = {"Heading", "Column", "Data"}
1213

1314

1415
def AVID_VIDEO_FORMAT_FROM_WIDTH_HEIGHT(width, height):
@@ -182,6 +183,70 @@ def _video_format_from_metadata(clips):
182183
return AVID_VIDEO_FORMAT_FROM_WIDTH_HEIGHT(max_width, max_height)
183184

184185

186+
def _read_heading_lines(lines):
187+
"""
188+
Consumes all the Header information from lines returning a dictionary
189+
mapping header keys to header values.
190+
"""
191+
header = {}
192+
# read until we run out of lines or the next section starts, not consuming
193+
# the next section designator
194+
while len(lines) and lines[0] not in ALE_SECTION_DESIGNATORS:
195+
line = lines.pop(0)
196+
if line.strip() == "":
197+
continue
198+
199+
if "\t" not in line:
200+
raise ALEParseError("Invalid Heading line: " + line)
201+
202+
segments = line.split("\t")
203+
while len(segments) >= 2:
204+
key, val = segments.pop(0), segments.pop(0)
205+
header[key] = val
206+
if len(segments) != 0:
207+
raise ALEParseError("Invalid Heading line: " + line)
208+
209+
return header
210+
211+
212+
def _pop_columns(lines):
213+
"""
214+
Consumes the Column information from the ALE and returns the list of
215+
columns.
216+
"""
217+
try:
218+
line = lines.pop(0)
219+
# skip blank lines
220+
while not line.strip():
221+
line = lines.pop(0)
222+
except IndexError:
223+
raise ALEParseError("Unexpected end of file after 'Column'")
224+
225+
columns = line.split("\t")
226+
227+
return columns
228+
229+
230+
def _read_data(lines, columns, fps, ale_name_column_key):
231+
"""
232+
Generator consuming the Data section of the ALE yielding Clips.
233+
"""
234+
while len(lines):
235+
line = lines.pop(0)
236+
237+
if line.strip() == "":
238+
continue
239+
240+
clip = _parse_data_line(
241+
line,
242+
columns,
243+
fps,
244+
ale_name_column_key=ale_name_column_key
245+
)
246+
247+
yield clip
248+
249+
185250
def read_from_string(input_str, fps=24, **adapter_argument_map):
186251
ale_name_column_key = adapter_argument_map.get(
187252
"ale_name_column_key", "Name"
@@ -203,21 +268,7 @@ def nextline(lines):
203268
continue
204269

205270
if line.strip() == "Heading":
206-
while len(lines):
207-
line = nextline(lines)
208-
209-
if line.strip() == "":
210-
break
211-
212-
if "\t" not in line:
213-
raise ALEParseError("Invalid Heading line: " + line)
214-
215-
segments = line.split("\t")
216-
while len(segments) >= 2:
217-
key, val = segments.pop(0), segments.pop(0)
218-
header[key] = val
219-
if len(segments) != 0:
220-
raise ALEParseError("Invalid Heading line: " + line)
271+
header.update(_read_heading_lines(lines))
221272

222273
if "FPS" in header:
223274
read_fps = float(header["FPS"])
@@ -234,24 +285,13 @@ def nextline(lines):
234285
if len(lines) == 0:
235286
raise ALEParseError("Unexpected end of file after: " + line)
236287

237-
line = nextline(lines)
238-
columns = line.split("\t")
288+
columns = _pop_columns(lines)
239289

240290
if line.strip() == "Data":
241-
while len(lines):
242-
line = nextline(lines)
243-
244-
if line.strip() == "":
245-
continue
246-
247-
clip = _parse_data_line(
248-
line,
249-
columns,
250-
fps,
251-
ale_name_column_key=ale_name_column_key
252-
)
253-
254-
collection.append(clip)
291+
clip_generator = _read_data(
292+
lines, columns, fps, ale_name_column_key
293+
)
294+
collection.extend(clip_generator)
255295

256296
collection.metadata["ALE"] = {
257297
"header": header,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Heading
2+
3+
FIELD_DELIM TABS
4+
5+
VIDEO_FORMAT 1080
6+
7+
AUDIO_FORMAT 48khz
8+
9+
FPS 25
10+
11+
12+
13+
Column
14+
15+
Name Tracks Start End Take Tape UNC FPS Reel Scene Shoot date Manufacturer Source Resolution Source Bit Depth DESCRIPT Comments Audio SR Audio Bit Depth Auxiliary TC1 KN Start Source File Path Filter Camera ID ISO Lens Number Camera Serial # Camera Type White Point (Kelvin) Angle Camera Clip Number Stereoscopic Eye Gamma Space LUT 1 ASC_SOP ASC_SAT RESOLVE_SIZING
16+
17+
18+
19+
Data
20+
21+
A020C003_150905_E2XZ.mov V 05:42:12:20 05:42:30:12 A020E2XZ Y:\Pennrand_CompWorkflow_XXXXXX\04_Compositing\11_Render_Compositing\PennComp_Za250\PennComp_Za250_Raw\A020C003_150905_E2XZ.mov 25 A020E2XZ 20150905 DaVinci Resolve 2048x1152 16 Y:\Pennrand_CompWorkflow_XXXXXX\04_Compositing\11_Render_Compositing\PennComp_Za250\PennComp_Za250_Raw\A020C003_150905_E2XZ.mov 0 E2XZ 800 0 3120 Alexa Plus 4700 1800 A A020C003_150905_E2XZ SINGLE LOG-C None (1.1822 1.2183 1.2284)(-0.2429 -0.2823 -0.2849)(0.7283 0.7096 0.7054) 1.0680 (0.0000 0.0000 1.0000 0.0000 0.0000 0 0)
22+

0 commit comments

Comments
 (0)