Repository: jdepoix/youtube-transcript-api
File: youtube_transcript_api/formatters.py (Python)

Code before:
        :return: a string formatted as a cue timestamp, 'HH:MM:SS,MS'
        :rtype str
        :example:
        >>> self._seconds_to_timestamp(6.93)
        '00:00:06,930'
        """
        time = float(time)
        hours, remainder = divmod(time, 3600)
        mins, secs = divmod(remainder, 60)
        ms = int(round((time - int(time))*1000, 2))
        return "{:02.0f}:{:02.0f}:{:02.0f},{:03d}".format(hours, mins, secs, ms)

    def format_transcript(self, transcript, **kwargs):
        """Converts a transcript into SRT formatting.

        :param transcript:
        :reference: https://www.3playmedia.com/blog/create-srt-file/
        """
        lines = []
        for i, line in enumerate(transcript):
            if i < len(transcript) - 1:
                # Looks ahead, use next start time since duration value
                # would create an overlap between start times.
                time_text = "{} --> {}".format(
                    self._seconds_to_timestamp(line['start']),
                    self._seconds_to_timestamp(transcript[i + 1]['start'])
                )
            else:
                # Reached the end, cannot look ahead, use duration now.
                duration = line['start'] + line['duration']
                time_text = "{} --> {}".format(
                    self._seconds_to_timestamp(line['start']),
                    self._seconds_to_timestamp(duration)
                )
            lines.append("{}\n{}\n{}".format(i + 1, time_text, line['text']))

        return "\n\n".join(lines) + "\n"

    def format_transcripts(self, transcripts, **kwargs):
        """Converts a list of transcripts into SRT formatting.

        :param transcript:
        :reference: https://www.3playmedia.com/blog/create-srt-file/
        """
        return '\n\n\n'.join([self.format_transcript(transcript, **kwargs) for transcript in transcripts])


class FormatterLoader(object):
    TYPES = {
        'json': JSONFormatter,
        'pretty': PrettyPrintFormatter,

Review comment (suggestion):
I didn't realize this when approving the initial PR for WebVTT, but it doesn't seem to make sense to me to always use the end time of the next start time, even if `transcript[i + 1]['start'] > line['start'] + line['duration']`. I guess this whole if-else could be replaced by
```python
end = line['start'] + line['duration']
time_text = "{} --> {}".format(
  self._seconds_to_timestamp(line['start']),
  self._seconds_to_timestamp(
    transcript[i + 1]['start'] 
       if i < len(transcript) - 1 and transcript[i + 1]['start'] < end else end
  )
)
```

Code after:
        return "\n\n".join(lines) + "\n"

    def _format_transcript_helper(self, i, time_text, line):
        return "{}\n{}\n{}".format(i + 1, time_text, line['text'])


class WebVTTFormatter(_TextBasedFormatter):
    def _format_timestamp(self, hours, mins, secs, ms):
        return "{:02d}:{:02d}:{:02d}.{:03d}".format(hours, mins, secs, ms)

    def _format_transcript_header(self, lines):
        return "WEBVTT\n\n" + "\n\n".join(lines) + "\n"

    def _format_transcript_helper(self, i, time_text, line):
        return "{}\n{}".format(time_text, line['text'])


class FormatterLoader(object):
    TYPES = {
        'json': JSONFormatter,
        'pretty': PrettyPrintFormatter,
        'text': TextFormatter,
        'webvtt': WebVTTFormatter,
        'srt' : SRTFormatter,
    }

    class UnknownFormatterType(Exception):
        def __init__(self, formatter_type):
            super(FormatterLoader.UnknownFormatterType, self).__init__(
                'The format \'{formatter_type}\' is not supported. '
                'Choose one of the following formats: {supported_formatter_types}'.format(
                    formatter_type=formatter_type,
                    supported_formatter_types=', '.join(FormatterLoader.TYPES.keys()),
                )
            )

    def load(self, formatter_type='pretty'):
        """
        Loads the Formatter for the given formatter type.

        :param formatter_type:
        :return: Formatter object
        """
        if formatter_type not in FormatterLoader.TYPES.keys():
            raise FormatterLoader.UnknownFormatterType(formatter_type)
        return FormatterLoader.TYPES[formatter_type]()