1import re
2from .formatbase import FormatBase
3from .ssaevent import SSAEvent
4from .ssastyle import SSAStyle
5from .substation import parse_tags
6from .time import ms_to_times, make_time, tmptimestamp_to_ms
7
8#: Pattern that matches TMP timestamp
9TMPTIMESTAMP = re.compile(r"(\d{1,2}):(\d{2}):(\d{2})")
10#: Pattern that matches TMP line
11TMP_LINE = re.compile(r"(\d{1,2}:\d{2}:\d{2}):(.+)")
12
13#: Largest timestamp allowed in Tmp, ie. 99:59:59.
14MAX_REPRESENTABLE_TIME = make_time(h=100) - 1
15
16
17def ms_to_timestamp(ms):
18    """Convert ms to 'HH:MM:SS'"""
19    # XXX throw on overflow/underflow?
20    if ms < 0: ms = 0
21    if ms > MAX_REPRESENTABLE_TIME: ms = MAX_REPRESENTABLE_TIME
22    h, m, s, ms = ms_to_times(ms)
23    return "%02d:%02d:%02d" % (h, m, s)
24
25
26class TmpFormat(FormatBase):
27    """TMP subtitle format implementation"""
28    @classmethod
29    def guess_format(cls, text):
30        """See :meth:`pysubs2.formats.FormatBase.guess_format()`"""
31        if "[Script Info]" in text or "[V4+ Styles]" in text:
32            # disambiguation vs. SSA/ASS
33            return None
34
35        for line in text.splitlines():
36            if TMP_LINE.match(line) and len(TMP_LINE.findall(line)) == 1:
37                return "tmp"
38
39    @classmethod
40    def from_file(cls, subs, fp, format_, **kwargs):
41        """See :meth:`pysubs2.formats.FormatBase.from_file()`"""
42        events = []
43
44        def prepare_text(text):
45            text = text.replace("|", r"\N")  # convert newlines
46            text = re.sub(r"< *u *>", "{\\\\u1}", text) # not r" for Python 2.7 compat, triggers unicodeescape
47            text = re.sub(r"< */? *[a-zA-Z][^>]*>", "", text) # strip other HTML tags
48            return text
49
50        for line in fp:
51            match = TMP_LINE.match(line)
52            if not match:
53                continue
54
55            start, text = match.groups()
56            start = tmptimestamp_to_ms(TMPTIMESTAMP.match(start).groups())
57
58            # Unfortunately, end timestamp is not given; try to estimate something reasonable:
59            # start + 500 ms + 67 ms/character (15 chars per second)
60            end_guess = start + 500 + (len(line) * 67)
61
62            event = SSAEvent(start=start, end=end_guess, text=prepare_text(text))
63            events.append(event)
64
65        # correct any overlapping subtitles created by end_guess
66        for i in range(len(events) - 1):
67            events[i].end = min(events[i].end, events[i+1].start)
68
69        subs.events = events
70
71    @classmethod
72    def to_file(cls, subs, fp, format_, apply_styles=True, **kwargs):
73        """
74        See :meth:`pysubs2.formats.FormatBase.to_file()`
75
76        Italic, underline and strikeout styling is supported.
77
78        Keyword args:
79            apply_styles: If False, do not write any styling.
80
81        """
82        def prepare_text(text, style):
83            body = []
84            skip = False
85            for fragment, sty in parse_tags(text, style, subs.styles):
86                fragment = fragment.replace(r"\h", " ")
87                fragment = fragment.replace(r"\n", "\n")
88                fragment = fragment.replace(r"\N", "\n")
89                if apply_styles:
90                    if sty.italic: fragment = "<i>%s</i>" % fragment
91                    if sty.underline: fragment = "<u>%s</u>" % fragment
92                    if sty.strikeout: fragment = "<s>%s</s>" % fragment
93                if sty.drawing: skip = True
94                body.append(fragment)
95
96            if skip:
97                return ""
98            else:
99                return re.sub("\n+", "\n", "".join(body).strip())
100
101        visible_lines = (line for line in subs if not line.is_comment)
102
103        for line in visible_lines:
104            start = ms_to_timestamp(line.start)
105            text = prepare_text(line.text, subs.styles.get(line.style, SSAStyle.DEFAULT_STYLE))
106
107            print(start + ":" + text, end="\n", file=fp)
108