1""" Experimental module for subtitles support. """ 2 3import re 4 5import numpy as np 6 7from moviepy.tools import cvsecs 8from moviepy.video.VideoClip import TextClip, VideoClip 9 10 11class SubtitlesClip(VideoClip): 12 """ A Clip that serves as "subtitle track" in videos. 13 14 One particularity of this class is that the images of the 15 subtitle texts are not generated beforehand, but only if 16 needed. 17 18 Parameters 19 ========== 20 21 subtitles 22 Either the name of a file, or a list 23 24 Examples 25 ========= 26 27 >>> from moviepy.video.tools.subtitles import SubtitlesClip 28 >>> from moviepy.video.io.VideoFileClip import VideoFileClip 29 >>> generator = lambda txt: TextClip(txt, font='Georgia-Regular', fontsize=24, color='white') 30 >>> sub = SubtitlesClip("subtitles.srt", generator) 31 >>> myvideo = VideoFileClip("myvideo.avi") 32 >>> final = CompositeVideoClip([clip, subtitles]) 33 >>> final.write_videofile("final.mp4", fps=myvideo.fps) 34 35 """ 36 37 def __init__(self, subtitles, make_textclip=None): 38 39 VideoClip.__init__(self, has_constant_size=False) 40 41 if isinstance(subtitles, str): 42 subtitles = file_to_subtitles(subtitles) 43 44 #subtitles = [(map(cvsecs, tt),txt) for tt, txt in subtitles] 45 self.subtitles = subtitles 46 self.textclips = dict() 47 48 if make_textclip is None: 49 make_textclip = lambda txt: TextClip(txt, font='Georgia-Bold', 50 fontsize=24, color='white', 51 stroke_color='black', stroke_width=0.5) 52 53 self.make_textclip = make_textclip 54 self.start=0 55 self.duration = max([tb for ((ta,tb), txt) in self.subtitles]) 56 self.end=self.duration 57 58 def add_textclip_if_none(t): 59 """ Will generate a textclip if it hasn't been generated asked 60 to generate it yet. If there is no subtitle to show at t, return 61 false. """ 62 sub =[((ta,tb),txt) for ((ta,tb),txt) in self.textclips.keys() 63 if (ta<=t<tb)] 64 if not sub: 65 sub = [((ta,tb),txt) for ((ta,tb),txt) in self.subtitles if 66 (ta<=t<tb)] 67 if not sub: 68 return False 69 sub = sub[0] 70 if sub not in self.textclips.keys(): 71 self.textclips[sub] = self.make_textclip(sub[1]) 72 73 return sub 74 75 def make_frame(t): 76 sub = add_textclip_if_none(t) 77 return (self.textclips[sub].get_frame(t) if sub 78 else np.array([[[0,0,0]]])) 79 80 def make_mask_frame(t): 81 sub = add_textclip_if_none(t) 82 return (self.textclips[sub].mask.get_frame(t) if sub 83 else np.array([[0]])) 84 85 self.make_frame = make_frame 86 hasmask = bool(self.make_textclip('T').mask) 87 self.mask = VideoClip(make_mask_frame, ismask=True) if hasmask else None 88 89 def in_subclip(self, t_start= None, t_end= None): 90 """ Returns a sequence of [(t1,t2), txt] covering all the given subclip 91 from t_start to t_end. The first and last times will be cropped so as 92 to be exactly t_start and t_end if possible. """ 93 94 def is_in_subclip(t1,t2): 95 try: 96 return (t_start<=t1<t_end) or (t_start< t2 <=t_end) 97 except: 98 return False 99 def try_cropping(t1,t2): 100 try: 101 return (max(t1, t_start), min(t2, t_end)) 102 except: 103 return (t1, t2) 104 return [(try_cropping(t1,t2), txt) for ((t1,t2), txt) in self.subtitles 105 if is_in_subclip(t1,t2)] 106 107 108 109 def __iter__(self): 110 return iter(self.subtitles) 111 112 113 114 def __getitem__(self, k): 115 return self.subtitles[k] 116 117 118 119 def __str__(self): 120 121 def to_srt(sub_element): 122 (ta, tb), txt = sub_element 123 fta = cvsecs(ta) 124 ftb = cvsecs(tb) 125 return "%s - %s\n%s"%(fta, ftb, txt) 126 127 return "\n\n".join(to_srt(s) for s in self.subtitles) 128 129 130 131 def match_expr(self, expr): 132 133 return SubtitlesClip([e for e in self.subtitles 134 if re.findall(expr, e[1]) != []]) 135 136 137 def write_srt(self, filename): 138 with open(filename, 'w+') as f: 139 f.write(str(self)) 140 141 142def file_to_subtitles(filename): 143 """ Converts a srt file into subtitles. 144 145 The returned list is of the form ``[((ta,tb),'some text'),...]`` 146 and can be fed to SubtitlesClip. 147 148 Only works for '.srt' format for the moment. 149 """ 150 times_texts = [] 151 current_times = None 152 current_text = "" 153 with open(filename,'r') as f: 154 for line in f: 155 times = re.findall("([0-9]*:[0-9]*:[0-9]*,[0-9]*)", line) 156 if times: 157 current_times = [cvsecs(t) for t in times] 158 elif line.strip() == '': 159 times_texts.append((current_times, current_text.strip('\n'))) 160 current_times, current_text = None, "" 161 elif current_times: 162 current_text += line 163 return times_texts 164