• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

popstas / talks-reducer / 28130984099

24 Jun 2026 09:32PM UTC coverage: 74.925% (+0.9%) from 74.044%
28130984099

Pull #140

github

web-flow
Merge 1bb5ff3e4 into 8888c2237
Pull Request #140: feat: Add video trim (cut begin/end) before converting

286 of 332 new or added lines in 10 files covered. (86.14%)

611 existing lines in 10 files now uncovered.

8480 of 11318 relevant lines covered (74.92%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.74
/talks_reducer/timecode.py
1
"""Helpers for parsing and formatting trim timecodes.
2

3
Timecodes describe positions within a video and are accepted in several
4
forms by the CLI and GUIs: a bare number of seconds (``12.5``) or a
5
colon-separated clock string (``SS``, ``MM:SS`` or ``HH:MM:SS`` with an
6
optional fractional ``.ms`` suffix). All helpers operate on non-negative
7
values and reject malformed input with :class:`ValueError`.
8
"""
9

10
from __future__ import annotations
1✔
11

12
import math
1✔
13
from numbers import Real
1✔
14

15
__all__ = ["parse_timecode", "format_timecode"]
1✔
16

17

18
def _validate_seconds(seconds: float, value) -> float:
1✔
19
    """Return ``seconds`` after rejecting negative or non-finite values."""
20

21
    if not math.isfinite(seconds):
1✔
NEW
22
        raise ValueError(f"Invalid timecode: {value!r}")
×
23
    if seconds < 0:
1✔
24
        raise ValueError(f"Timecode cannot be negative: {value!r}")
1✔
25
    return seconds
1✔
26

27

28
def parse_timecode(value) -> float:
1✔
29
    """Return the number of seconds described by ``value``.
30

31
    ``value`` may be a numeric seconds value (``int``/``float``) or a string
32
    holding either a decimal number of seconds or a ``SS`` / ``MM:SS`` /
33
    ``HH:MM:SS`` clock with an optional ``.ms`` fractional part. Negative or
34
    malformed values raise :class:`ValueError`.
35
    """
36

37
    if isinstance(value, bool):  # bool is a subclass of int; reject explicitly
1✔
NEW
38
        raise ValueError(f"Invalid timecode: {value!r}")
×
39

40
    if isinstance(value, Real):
1✔
41
        return _validate_seconds(float(value), value)
1✔
42

43
    if not isinstance(value, str):
1✔
44
        raise ValueError(f"Invalid timecode: {value!r}")
1✔
45

46
    text = value.strip()
1✔
47
    if not text:
1✔
48
        raise ValueError("Timecode cannot be empty")
1✔
49

50
    if ":" in text:
1✔
51
        parts = text.split(":")
1✔
52
        if len(parts) > 3:
1✔
53
            raise ValueError(f"Invalid timecode: {value!r}")
1✔
54
        try:
1✔
55
            numbers = [float(part) for part in parts]
1✔
56
        except ValueError as exc:
1✔
57
            raise ValueError(f"Invalid timecode: {value!r}") from exc
1✔
58
        if any(number < 0 for number in numbers):
1✔
NEW
59
            raise ValueError(f"Timecode cannot be negative: {value!r}")
×
60
        seconds = 0.0
1✔
61
        for number in numbers:
1✔
62
            seconds = seconds * 60 + number
1✔
63
        return _validate_seconds(seconds, value)
1✔
64

65
    try:
1✔
66
        seconds = float(text)
1✔
67
    except ValueError as exc:
1✔
68
        raise ValueError(f"Invalid timecode: {value!r}") from exc
1✔
69
    return _validate_seconds(seconds, value)
1✔
70

71

72
def format_timecode(seconds, *, milliseconds: bool = False) -> str:
1✔
73
    """Return ``seconds`` formatted as a ``HH:MM:SS`` clock string.
74

75
    When ``milliseconds`` is true the fractional part is appended as a
76
    three-digit ``.mmm`` suffix (``HH:MM:SS.mmm``) so the value can round-trip
77
    through :func:`parse_timecode` without losing sub-second precision.
78
    """
79

80
    if isinstance(seconds, bool) or not isinstance(seconds, Real):
1✔
NEW
81
        raise ValueError(f"Invalid seconds value: {seconds!r}")
×
82
    if seconds < 0:
1✔
83
        raise ValueError(f"Seconds cannot be negative: {seconds!r}")
1✔
84

85
    total = int(seconds)
1✔
86
    hours, remainder = divmod(total, 3600)
1✔
87
    minutes, secs = divmod(remainder, 60)
1✔
88
    clock = f"{hours:02d}:{minutes:02d}:{secs:02d}"
1✔
89
    if not milliseconds:
1✔
90
        return clock
1✔
91
    millis = int(round((float(seconds) - total) * 1000))
1✔
92
    if millis >= 1000:  # rounding can spill into the next second
1✔
NEW
93
        millis = 999
×
94
    return f"{clock}.{millis:03d}"
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc