This file is indexed.

/usr/lib/python3/dist-packages/pysrt/commands.py is in python3-pysrt 1.0.1-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable-all

import os
import re
import sys
import codecs
import shutil
import argparse
from textwrap import dedent

from chardet import detect
from pysrt import SubRipFile, SubRipTime, VERSION_STRING

def underline(string):
    return "\033[4m%s\033[0m" % string


class TimeAwareArgumentParser(argparse.ArgumentParser):

    RE_TIME_REPRESENTATION = re.compile(r'^\-?(\d+[hms]{0,2}){1,4}$')

    def parse_args(self, args=None, namespace=None):
        time_index = -1
        for index, arg in enumerate(args):
            match = self.RE_TIME_REPRESENTATION.match(arg)
            if match:
                time_index = index
                break

        if time_index >= 0:
            args.insert(time_index, '--')

        return super(TimeAwareArgumentParser, self).parse_args(args, namespace)


class SubRipShifter(object):

    BACKUP_EXTENSION = '.bak'
    RE_TIME_STRING = re.compile(r'(\d+)([hms]{0,2})')
    UNIT_RATIOS = {
        'ms': 1,
        '': SubRipTime.SECONDS_RATIO,
        's': SubRipTime.SECONDS_RATIO,
        'm': SubRipTime.MINUTES_RATIO,
        'h': SubRipTime.HOURS_RATIO,
    }
    DESCRIPTION = dedent("""\
        Srt subtitle editor

        It can either shift, split or change the frame rate.
    """)
    TIMESTAMP_HELP = "A timestamp in the form: [-][Hh][Mm]S[s][MSms]"
    SHIFT_EPILOG = dedent("""\

        Examples:
            1 minute and 12 seconds foreward (in place):
                $ srt -i shift 1m12s movie.srt

            half a second foreward:
                $ srt shift 500ms movie.srt > othername.srt

            1 second and half backward:
                $ srt -i shift -1s500ms movie.srt

            3 seconds backward:
                $ srt -i shift -3 movie.srt
    """)
    RATE_EPILOG = dedent("""\

        Examples:
            Convert 23.9fps subtitles to 25fps:
                $ srt -i rate 23.9 25 movie.srt
    """)
    LIMITS_HELP = "Each parts duration in the form: [Hh][Mm]S[s][MSms]"
    SPLIT_EPILOG = dedent("""\

        Examples:
            For a movie in 2 parts with the first part 48 minutes and 18 seconds long:
                $ srt split 48m18s movie.srt
                => creates movie.1.srt and movie.2.srt

            For a movie in 3 parts of 20 minutes each:
                $ srt split 20m 20m movie.srt
                => creates movie.1.srt, movie.2.srt and movie.3.srt
    """)
    FRAME_RATE_HELP = "A frame rate in fps (commonly 23.9 or 25)"
    ENCODING_HELP = dedent("""\
        Change file encoding. Useful for players accepting only latin1 subtitles.
        List of supported encodings: http://docs.python.org/library/codecs.html#standard-encodings
    """)
    BREAK_EPILOG = dedent("""\
        Break lines longer than defined length
    """)
    LENGTH_HELP = "Maximum number of characters per line"

    def __init__(self):
        self.output_file_path = None

    def build_parser(self):
        parser = TimeAwareArgumentParser(description=self.DESCRIPTION, formatter_class=argparse.RawTextHelpFormatter)
        parser.add_argument('-i', '--in-place', action='store_true', dest='in_place',
            help="Edit file in-place, saving a backup as file.bak (do not works for the split command)")
        parser.add_argument('-e', '--output-encoding', metavar=underline('encoding'), action='store', dest='output_encoding',
            type=self.parse_encoding, help=self.ENCODING_HELP)
        parser.add_argument('-v', '--version', action='version', version='%%(prog)s %s' % VERSION_STRING)
        subparsers = parser.add_subparsers(title='commands')

        shift_parser = subparsers.add_parser('shift', help="Shift subtitles by specified time offset", epilog=self.SHIFT_EPILOG, formatter_class=argparse.RawTextHelpFormatter)
        shift_parser.add_argument('time_offset', action='store', metavar=underline('offset'),
            type=self.parse_time, help=self.TIMESTAMP_HELP)
        shift_parser.set_defaults(action=self.shift)

        rate_parser = subparsers.add_parser('rate', help="Convert subtitles from a frame rate to another", epilog=self.RATE_EPILOG, formatter_class=argparse.RawTextHelpFormatter)
        rate_parser.add_argument('initial', action='store', type=float, help=self.FRAME_RATE_HELP)
        rate_parser.add_argument('final', action='store', type=float, help=self.FRAME_RATE_HELP)
        rate_parser.set_defaults(action=self.rate)

        split_parser = subparsers.add_parser('split', help="Split a file in multiple parts", epilog=self.SPLIT_EPILOG, formatter_class=argparse.RawTextHelpFormatter)
        split_parser.add_argument('limits', action='store', nargs='+', type=self.parse_time, help=self.LIMITS_HELP)
        split_parser.set_defaults(action=self.split)

        break_parser = subparsers.add_parser('break', help="Break long lines", epilog=self.BREAK_EPILOG, formatter_class=argparse.RawTextHelpFormatter)
        break_parser.add_argument('length', action='store', type=int, help=self.LENGTH_HELP)
        break_parser.set_defaults(action=self.break_lines)

        parser.add_argument('file', action='store')

        return parser

    def run(self, args):
        self.arguments = self.build_parser().parse_args(args)
        if self.arguments.in_place:
            self.create_backup()
        self.arguments.action()

    def parse_time(self, time_string):
        negative = time_string.startswith('-')
        if negative:
            time_string = time_string[1:]
        ordinal = sum(int(value) * self.UNIT_RATIOS[unit] for value, unit
                        in self.RE_TIME_STRING.findall(time_string))
        return -ordinal if negative else ordinal

    def parse_encoding(self, encoding_name):
        try:
            codecs.lookup(encoding_name)
        except LookupError as error:
            raise argparse.ArgumentTypeError(error.message)
        return encoding_name

    def shift(self):
        self.input_file.shift(milliseconds=self.arguments.time_offset)
        self.input_file.write_into(self.output_file)

    def rate(self):
        ratio = self.arguments.final / self.arguments.initial
        self.input_file.shift(ratio=ratio)
        self.input_file.write_into(self.output_file)

    def split(self):
        limits = [0] + self.arguments.limits + [self.input_file[-1].end.ordinal + 1]
        base_name, extension = os.path.splitext(self.arguments.file)
        for index, (start, end) in enumerate(zip(limits[:-1], limits[1:])):
            file_name = '%s.%s%s' % (base_name, index + 1, extension)
            part_file = self.input_file.slice(ends_after=start, starts_before=end)
            part_file.shift(milliseconds=-start)
            part_file.clean_indexes()
            part_file.save(path=file_name, encoding=self.output_encoding)

    def create_backup(self):
        backup_file = self.arguments.file + self.BACKUP_EXTENSION
        if not os.path.exists(backup_file):
            shutil.copy2(self.arguments.file, backup_file)
        self.output_file_path = self.arguments.file
        self.arguments.file = backup_file

    def break_lines(self):
        split_re = re.compile(r'(.{,%i})(?:\s+|$)' % self.arguments.length)
        for item in self.input_file:
            item.text = '\n'.join(split_re.split(item.text)[1::2])
        self.input_file.write_into(self.output_file)

    @property
    def output_encoding(self):
        return self.arguments.output_encoding or self.input_file.encoding

    @property
    def input_file(self):
        if not hasattr(self, '_source_file'):
            with open(self.arguments.file, 'rb') as f:
                content = f.read()
                encoding = detect(content).get('encoding')
                encoding = self.normalize_encoding(encoding)

            self._source_file = SubRipFile.open(self.arguments.file,
                encoding=encoding, error_handling=SubRipFile.ERROR_LOG)
        return self._source_file

    @property
    def output_file(self):
        if not hasattr(self, '_output_file'):
            if self.output_file_path:
                self._output_file = codecs.open(self.output_file_path, 'w+', encoding=self.output_encoding)
            else:
                self._output_file = sys.stdout
        return self._output_file

    def normalize_encoding(self, encoding):
        return encoding.lower().replace('-', '_')


def main():
    SubRipShifter().run(sys.argv[1:])

if __name__ == '__main__':
    main()