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

EsupPortail / Esup-Pod / 17798713985

17 Sep 2025 01:10PM UTC coverage: 70.327%. First build
17798713985

Pull #1342

github

web-flow
Merge 66fd448d6 into a22fd1a86
Pull Request #1342: Fixes the `sec_to_timestamp function to avoid generating invalid timestamps

1 of 9 new or added lines in 2 files covered. (11.11%)

12182 of 17322 relevant lines covered (70.33%)

0.7 hits per line

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

71.0
/pod/video_encode_transcript/encoding_utils.py
1
from collections import OrderedDict
1✔
2
from timeit import default_timer as timer
1✔
3

4
import json
1✔
5
import os
1✔
6
import shlex
1✔
7
import subprocess
1✔
8
import logging
1✔
9

10
try:
1✔
11
    from .encoding_settings import VIDEO_RENDITIONS
1✔
12
except (ImportError, ValueError):
×
13
    from encoding_settings import VIDEO_RENDITIONS
×
14

15
try:
1✔
16
    from django.conf import settings
1✔
17

18
    VIDEO_RENDITIONS = getattr(settings, "VIDEO_RENDITIONS", VIDEO_RENDITIONS)
1✔
19
    DEBUG = getattr(settings, "DEBUG", True)
1✔
20
except ImportError:  # pragma: no cover
21
    DEBUG = True
22
    pass
23

24
logger = logging.getLogger(__name__)
1✔
25
if DEBUG:
1✔
26
    logger.setLevel(logging.DEBUG)
1✔
27

28

29
def sec_to_timestamp(total_seconds) -> str:
1✔
30
    """Format time for webvtt caption."""
NEW
31
    if total_seconds < 0:
×
NEW
32
        total_seconds = 0
×
NEW
33
    hours = int(total_seconds // 3600)
×
NEW
34
    minutes = int((total_seconds % 3600) // 60)
×
NEW
35
    seconds = total_seconds % 60
×
36
    # Clamp seconds to just below 60 if needed (ex: 59.999)
NEW
37
    if seconds >= 60:
×
NEW
38
        seconds = 59.999
×
39
    return "{:02d}:{:02d}:{:06.3f}".format(hours, minutes, seconds)
×
40

41

42
def get_dressing_position_value(position: str, height: str) -> str:
1✔
43
    """
44
    Obtain dimensions proportional to the video format.
45

46
    Args:
47
        position (str): property "position" of the dressing object.
48
        height (str): height of the source video.
49

50
    Returns:
51
        str: params for the ffmpeg command.
52
    """
53
    height = str(float(height) * 0.05)
1✔
54
    if position == "top_right":
1✔
55
        return "overlay=main_w-overlay_w-" + height + ":" + height
1✔
56
    elif position == "top_left":
1✔
57
        return "overlay=" + height + ":" + height
1✔
58
    elif position == "bottom_right":
1✔
59
        return "overlay=main_w-overlay_w-" + height + ":main_h-overlay_h-" + height
1✔
60
    elif position == "bottom_left":
1✔
61
        return "overlay=" + height + ":main_h-overlay_h-" + height
1✔
62

63

64
def get_renditions():
1✔
65
    try:
1✔
66
        from .models import VideoRendition
1✔
67
        from django.core import serializers
1✔
68

69
        renditions = json.loads(
1✔
70
            serializers.serialize("json", VideoRendition.objects.all())
71
        )
72
        video_rendition = []
1✔
73
        for rend in renditions:
1✔
74
            video_rendition.append(rend["fields"])
1✔
75
        return video_rendition
1✔
76
    except ImportError:
×
77
        return VIDEO_RENDITIONS
×
78

79

80
def check_file(path_file) -> bool:
1✔
81
    if os.access(path_file, os.F_OK) and os.stat(path_file).st_size > 0:
1✔
82
        return True
1✔
83
    return False
1✔
84

85

86
def get_list_rendition():
1✔
87
    list_rendition = {}
1✔
88
    renditions = get_renditions()
1✔
89
    for rend in renditions:
1✔
90
        list_rendition[int(rend["resolution"].split("x")[1])] = rend
1✔
91
    list_rendition = OrderedDict(sorted(list_rendition.items(), key=lambda t: t[0]))
1✔
92
    return list_rendition
1✔
93

94

95
def get_info_from_video(probe_cmd):
1✔
96
    info = None
1✔
97
    msg = ""
1✔
98
    try:
1✔
99
        output = subprocess.check_output(shlex.split(probe_cmd), stderr=subprocess.PIPE)
1✔
100
        info = json.loads(output)
1✔
101
    except subprocess.CalledProcessError as e:
×
102
        # raise RuntimeError('ffprobe returned non-zero status: {}'.format(
103
        # e.stderr))
104
        msg += 20 * "////" + "\n"
×
105
        msg += "Runtime Error: {0}\n".format(e)
×
106
    except OSError as err:
×
107
        # raise OSError(e.errno, 'ffprobe not found: {}'.format(e.strerror))
108
        msg += 20 * "////" + "\n"
×
109
        msg += "OS error: {0}\n".format(err)
×
110
    return info, msg
1✔
111

112

113
def launch_cmd(cmd):
1✔
114
    if cmd == "":
1✔
115
        msg = "No cmd to launch"
×
116
        logger.warning(msg)
×
117
        return False, msg
×
118
    msg = ""
1✔
119
    encode_start = timer()
1✔
120
    return_value = False
1✔
121
    try:
1✔
122
        logger.debug("launch_cmd: %s" % cmd)
1✔
123
        output = subprocess.run(
1✔
124
            shlex.split(cmd),
125
            stdout=subprocess.PIPE,
126
            stderr=subprocess.STDOUT,
127
        )
128
        encode_end = timer() - encode_start
1✔
129
        msg += cmd + "\n"
1✔
130
        msg += "Encode file in {:.3}s.\n".format(encode_end)
1✔
131
        # msg += "\n".join(output.stdout.decode().split('\n'))
132
        try:
1✔
133
            msg += output.stdout.decode("utf-8")
1✔
134
        except UnicodeDecodeError:
×
135
            pass
×
136
        msg += "\n"
1✔
137
        if output.returncode != 0:
1✔
138
            msg += "ERROR RETURN CODE %s for command %s" % (output.returncode, cmd)
×
139
        else:
140
            return_value = True
1✔
141
    except (subprocess.CalledProcessError, OSError) as e:
×
142
        # raise RuntimeError('ffmpeg returned non-zero status: {}'.format(
143
        # e.stderr))
144
        msg += 20 * "////" + "\n"
×
145
        err_msg = "Runtime or OS Error: {0}\n".format(e)
×
146
        msg += err_msg
×
147
        logger.error(err_msg)
×
148
    return return_value, msg
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

© 2025 Coveralls, Inc