77from .errors import MalformedFileError , MalformedCaptionError
88from .models import Caption , Style
99
10+ TIMESTAMP = r'(?:\d+:)?\d{1,2}:\d{1,2}\.\d{1,3}'
11+ STRICT_TIMESTAMP = r'(?:\d{2,}:)?\d{2}:\d{2}\.\d{3}'
12+
1013
1114@dataclass
1215class ParserOutput :
@@ -41,9 +44,10 @@ class WebVTTCueBlock:
4144 """Representation of a cue timing block."""
4245
4346 CUE_TIMINGS_PATTERN = re .compile (
44- r'\s*((?:\d+:)?\d{2}:\d{2}\.\d{3})'
45- r'\s*-->\s*'
46- r'((?:\d+:)?\d{2}:\d{2}\.\d{3})'
47+ rf'\s*({ TIMESTAMP } )\s*-->\s*({ TIMESTAMP } )'
48+ )
49+ STRICT_CUE_TIMINGS_PATTERN = re .compile (
50+ rf'\s*({ STRICT_TIMESTAMP } )\s*-->\s*({ STRICT_TIMESTAMP } )(?=\s|$)'
4751 )
4852
4953 def __init__ (
@@ -68,24 +72,33 @@ def __init__(
6872 @classmethod
6973 def is_valid (
7074 cls ,
71- lines : typing .Sequence [str ]
75+ lines : typing .Sequence [str ],
76+ strict : bool = False
7277 ) -> bool :
7378 """
7479 Validate the lines for a match of a cue time block.
7580
81+ Timestamps with fields shorter than the WebVTT format requires, as
82+ in ``0:0:13.540``, are accepted unless strict.
83+
7684 :param lines: the lines to be validated
85+ :param strict: only accept timestamps in the WebVTT format
7786 :returns: true for a matching cue time block
7887 """
88+ pattern = (
89+ cls .STRICT_CUE_TIMINGS_PATTERN if strict
90+ else cls .CUE_TIMINGS_PATTERN
91+ )
7992 return bool (
8093 (
8194 len (lines ) >= 1 and
82- re .match (cls . CUE_TIMINGS_PATTERN , lines [0 ]) and
95+ re .match (pattern , lines [0 ]) and
8396 (len (lines ) < 2 or "-->" not in lines [1 ])
8497 ) or
8598 (
8699 len (lines ) >= 2 and
87100 "-->" not in lines [0 ] and
88- re .match (cls . CUE_TIMINGS_PATTERN , lines [1 ]) and
101+ re .match (pattern , lines [1 ]) and
89102 (len (lines ) < 3 or "-->" not in lines [2 ])
90103 )
91104 )
@@ -356,7 +369,7 @@ def parse_items(
356369 comments : typing .List [WebVTTCommentBlock ] = []
357370
358371 for block_lines in iter_blocks_of_lines (lines , strict ):
359- item = parse_item (block_lines )
372+ item = parse_item (block_lines , strict )
360373 if item :
361374 item .comments = [comment .text for comment in comments ]
362375 comments = []
@@ -380,21 +393,32 @@ def parse_items(
380393
381394
382395def parse_item (
383- lines : typing .Sequence [str ]
396+ lines : typing .Sequence [str ],
397+ strict : bool = False
384398 ) -> typing .Union [Caption , Style , None ]:
385399 """
386400 Parse an item from lines of text.
387401
388402 :param lines: lines of text
403+ :param strict: only accept cue timestamps in the WebVTT format and
404+ raise an error for cues with an invalid timestamp
389405 :returns: An item (Caption or Style) if found, otherwise None
406+
407+ :raises MalformedCaptionError: if strict and a cue has an invalid
408+ timestamp
390409 """
391- if WebVTTCueBlock .is_valid (lines ):
410+ if WebVTTCueBlock .is_valid (lines , strict ):
392411 cue_block = WebVTTCueBlock .from_lines (lines )
393- return Caption (cue_block .start ,
394- cue_block .end ,
395- cue_block .payload ,
396- cue_block .identifier
397- )
412+ try :
413+ return Caption (cue_block .start ,
414+ cue_block .end ,
415+ cue_block .payload ,
416+ cue_block .identifier
417+ )
418+ except MalformedCaptionError :
419+ if strict :
420+ raise
421+ return None
398422
399423 if WebVTTStyleBlock .is_valid (lines ):
400424 return Style (WebVTTStyleBlock .from_lines (lines ).text )
0 commit comments