def subtitles( self, comments: Comments) -> Generator[Tuple[str, Comment], None, None]: """ Subtitle generator :param comments: Comments to turn into subtitles :return: Generator with subtitles and subtitle data """ for index, comment in enumerate(comments): # Stat and stop timestamps. Add a millisecond for timedelta to include millisecond digits start = datetime.timedelta(seconds=comment.content_offset_seconds) stop: datetime.timedelta = start + datetime.timedelta( milliseconds=self.format_dictionary['duration']) # Format message message: str = Pipe(self.format_dictionary['comments']).comment( comment.data) # Subtitle variables # Subtract the last three milliseconds form timestamp (required by SRT) subtitle: dict = { 'index': index + 1, 'start': SRT.format_timestamp(start), 'stop': SRT.format_timestamp(stop), 'message': message } yield '{index}\n{start} --> {stop}\n{message}\n'.format_map( SafeDict(subtitle)), comment
def prefix(self) -> Generator[Tuple[str, None], None, None]: """ SSA file header :return: Generator for header lines """ lines: List[str] = list() # Script info lines.append('[Script Info]') lines.append('Title: {title}'.format_map(SafeDict(self.video.data))) lines.append('ScriptType: v4.00') lines.append('Collisions: Normal') lines.append('PlayResX: {resolution[x]}'.format_map( SafeDict(self.format_dictionary))) lines.append('PlayResY: {resolution[y]}'.format_map( SafeDict(self.format_dictionary))) lines.append('PlayDepth: 0') lines.append('Timer: 100,0000') # V4 Styles lines.append('\n[V4 Styles]') lines.append(self.format_dictionary['styles']['format']) lines.append(self.format_dictionary['styles']['values']) # Fonts lines.append('\n[Fonts]') lines.append(self.format_dictionary['fonts']) # Graphics lines.append('\n[Graphics]') lines.append(self.format_dictionary['graphics']) # Events lines.append('\n[Events]') lines.append(self.format_dictionary['events']['format']) for line in lines: yield line, None
def dialogues( self, comments: Comments) -> Generator[Tuple[str, Comments], None, None]: """ Format comments as SSA dialogues :param comments: Comment to format :return: tuple(formatted comment, comment) """ for comment in comments: start: datetime.timedelta = datetime.timedelta( seconds=comment.content_offset_seconds) end: datetime.timedelta = start + datetime.timedelta( milliseconds=self.format_dictionary['duration']) # Avoid SSA variable conflicts with Python string formatting # This is done by temporarily removing opening and closing curly brackets used by SSA. # # The main problem is detecting these curly brackets. We want to differentiate brackets that # should be used by the Python string formatter, and those used by SSA. # # Opening curly brackets for SSA can easily be found by looking for "{\", however, # closing curly brackets are used in the same way (just a "}") for both and requires a bit more effort. # # By incrementing a counter for opening brackets meant for Python formatting and decrementing for every # closing bracket meant for Python formatting, we can define every closing bracket to belong to SSA whenever # the counter is at zero. ssa_closing_brackets_indices: list = [] open_bracket_counter: int = 0 # Loop through every character in formatting string for index in range( len(self.format_dictionary['comments']['format'])): letter: str = self.format_dictionary['comments']['format'][ index] # Check if SSA bracket first, before altering the counter. if letter is '}' and open_bracket_counter is 0: ssa_closing_brackets_indices.append(index) continue # Update counter open_bracket_counter += { '{': 1, # Bracket is opened '\\': -1, # Bracket was meant for SSA, not for Python '}': -1 # Closing bracket }.get(letter, 0) # Multiple SSA commands within a curly brackets could make it negative # Example: {\\c�&\\b1} will count 1, 0, -1, -2 open_bracket_counter = max(0, open_bracket_counter) # Add a temporary special character for SSA closing curly brackets for index in ssa_closing_brackets_indices: self.format_dictionary['comments']['format'] = self.format_dictionary['comments']['format'][ :index] + SSA.SPECIAL + \ self.format_dictionary['comments']['format'][index + 1:] self.format_dictionary['comments'][ 'format'] = self.format_dictionary['comments'][ 'format'].replace('{\\', SSA.OPEN).replace( SSA.SPECIAL, SSA.CLOSE) # Format comment comment_text = Pipe(self.format_dictionary['comments']).comment( comment.data) # Insert opening and closing curly brackets for SSA comment_text = comment_text.replace(SSA.OPEN, '{\\').replace(SSA.CLOSE, '}') # Convert color code into SSA color code. comment_text = comment_text.replace('\\c&#', '\\c&H').replace( '\\c&H#', '\\c&H') dialogue: dict = { 'start': SSA.format_timestamp(start), 'end': SSA.format_timestamp(end), 'comment': comment_text } dialogue.update(comment.data) yield self.format_dictionary['events']['dialogue'].format_map( SafeDict(dialogue)), comment