├── modules ├── __pycache__ │ ├── captions.cpython-310.pyc │ ├── videoSize.cpython-310.pyc │ └── writeText.cpython-310.pyc ├── videoSize.py ├── captions.py └── writeText.py ├── README.md ├── main.py └── srtFiles └── roganAndOldGuy.srt /modules/__pycache__/captions.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lernEarnBurn/TikTok-Caption-Generator/HEAD/modules/__pycache__/captions.cpython-310.pyc -------------------------------------------------------------------------------- /modules/__pycache__/videoSize.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lernEarnBurn/TikTok-Caption-Generator/HEAD/modules/__pycache__/videoSize.cpython-310.pyc -------------------------------------------------------------------------------- /modules/__pycache__/writeText.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lernEarnBurn/TikTok-Caption-Generator/HEAD/modules/__pycache__/writeText.cpython-310.pyc -------------------------------------------------------------------------------- /modules/videoSize.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | 3 | def get_video_dimensions(video_path): 4 | vid = cv2.VideoCapture(video_path) 5 | height = vid.get(cv2.CAP_PROP_FRAME_HEIGHT) 6 | width = vid.get(cv2.CAP_PROP_FRAME_WIDTH) 7 | 8 | return (width * 0.9), height 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tik Tok Caption Generator (python) 2 | 3 | ## What it does 4 | Takes a 6:19 video and puts captions on it 5 | 6 | ## How it works 7 | + Uses openai whisper api to create an srt file 8 | + Uses moviepy to put the captions on the video 9 | 10 | 11 | ## Link 12 | Intended for local use 13 | 14 | ## Technologies 15 | Python 16 | 17 | 18 | ## Introduction 19 | Project for personal use 20 | 21 | ## Accomplishments 22 | + It works 23 | 24 | 25 | ## Notes 26 | + In order to shorten each Token in Whisper I tweaked the source code (see https://github.com/openai/whisper/discussions/223 for details.) 27 | 28 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from modules.captions import transcribe_audio 2 | from modules.writeText import add_caption_overlay 3 | import tkinter as tk 4 | from tkinter import filedialog 5 | import os 6 | 7 | 8 | 9 | def main(): 10 | root = tk.Tk() 11 | root.withdraw() 12 | 13 | file_path = filedialog.askopenfilename(filetypes=[("Video Files", "*.mp4")]) 14 | file_name = os.path.splitext(os.path.basename(file_path))[0] 15 | 16 | transcribe_audio(file_path) 17 | add_caption_overlay(file_path, f'./srtFiles/{file_name}.srt') 18 | 19 | print('\n\nVideo Complete') 20 | 21 | if __name__ == "__main__" : 22 | main() 23 | #add_caption_overlay('./testVid.mp4', './srtFiles/testVid.srt') 24 | #transcribe_audio('./testVid.mp4') -------------------------------------------------------------------------------- /modules/captions.py: -------------------------------------------------------------------------------- 1 | from datetime import timedelta 2 | import os 3 | import whisper 4 | import datetime 5 | 6 | from datetime import datetime, timedelta 7 | 8 | def transcribe_audio(path): 9 | model = whisper.load_model("small") 10 | print("Whisper model loaded.") 11 | transcribe = model.transcribe(audio=path) 12 | segments = transcribe['segments'] 13 | 14 | video_filename = os.path.splitext(os.path.basename(path))[0] 15 | 16 | 17 | 18 | for segment in segments: 19 | if segment['text'] != '': 20 | 21 | start_time = timedelta(seconds=float(segment['start'])) 22 | end_time = timedelta(seconds=float(segment['end'])) 23 | 24 | start_datetime = datetime(1, 1, 1) + start_time 25 | end_datetime = datetime(1, 1, 1) + end_time 26 | 27 | start_time_formatted = start_datetime.strftime("%H:%M:%S,%f")[:-3] 28 | end_time_formatted = end_datetime.strftime("%H:%M:%S,%f")[:-3] 29 | 30 | text = segment['text'] 31 | segment_id = segment['id'] + 1 32 | segment_content = f"{segment_id}\n{start_time_formatted} --> {end_time_formatted}\n{text[1:] if text[0] == ' ' else text}\n\n" 33 | 34 | srt_filename = os.path.join("./SrtFiles", f"{video_filename}.srt") 35 | with open(srt_filename, 'a', encoding='utf-8') as srt_file: 36 | srt_file.write(segment_content) 37 | 38 | print('\n\nSRT generated') 39 | 40 | return srt_filename -------------------------------------------------------------------------------- /modules/writeText.py: -------------------------------------------------------------------------------- 1 | from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip 2 | import os 3 | import pysrt 4 | from datetime import datetime as dt 5 | from .videoSize import get_video_dimensions 6 | 7 | os.environ["IMAGEIO_FFMPEG_EXE"] = r"C:\Program Files\ImageMagick-7.1.1-Q16-HDRI\ffmpeg.exe" 8 | 9 | def add_caption_overlay(video_path, srt_path): 10 | video_filename = os.path.splitext(os.path.basename(video_path))[0] 11 | 12 | video = VideoFileClip(video_path) 13 | 14 | subs = pysrt.open(srt_path) 15 | 16 | reference_date = dt(1900, 1, 1) 17 | 18 | clips = [] 19 | 20 | for caption in subs: 21 | start_time = dt.combine(reference_date, caption.start.to_time()) 22 | start_time_float = (start_time - reference_date).total_seconds() 23 | end_time = dt.combine(reference_date, caption.end.to_time()) 24 | caption_text = caption.text_without_tags 25 | 26 | duration_seconds = (end_time - start_time).total_seconds() 27 | 28 | screensize = get_video_dimensions(video_path) 29 | 30 | 31 | text = TextClip(caption_text, fontsize=75, color='white', font='Georgia-Bold', method='caption', stroke_color='white', stroke_width=2.5, size=screensize) 32 | 33 | text = text.set_position(lambda t: ('center', 175-t) ).set_start(start_time_float).set_duration(duration_seconds) 34 | 35 | clips.append(text) 36 | 37 | final_clip = CompositeVideoClip([video] + clips) 38 | final_clip = final_clip.set_duration(video.duration) 39 | final_clip.write_videofile(f'./output/{video_filename}.mp4', codec='libx264', audio_codec='aac') 40 | 41 | -------------------------------------------------------------------------------- /srtFiles/roganAndOldGuy.srt: -------------------------------------------------------------------------------- 1 | 1 2 | 00:00:00,000 --> 00:00:02,720 3 | People can develop these patterns of behavior 4 | 5 | 2 6 | 00:00:02,720 --> 00:00:04,280 7 | that are destructive with anything 8 | 9 | 3 10 | 00:00:04,280 --> 00:00:05,920 11 | whether it's with alcohol or cannabis 12 | 13 | 4 14 | 00:00:05,920 --> 00:00:08,000 15 | or sex or anything 16 | 17 | 5 18 | 00:00:08,000 --> 00:00:09,080 19 | people get 20 | 21 | 6 22 | 00:00:09,080 --> 00:00:09,920 23 | they get in ruts 24 | 25 | 7 26 | 00:00:09,920 --> 00:00:10,800 27 | and 28 | 29 | 8 30 | 00:00:10,800 --> 00:00:11,640 31 | it doesn't mean 32 | 33 | 9 34 | 00:00:11,640 --> 00:00:12,840 35 | that the cannabis is bad 36 | 37 | 10 38 | 00:00:12,840 --> 00:00:15,040 39 | it means that you are on a bad mental path 40 | 41 | 11 42 | 00:00:15,040 --> 00:00:15,880 43 | Yes 44 | 45 | 12 46 | 00:00:15,880 --> 00:00:16,720 47 | Exactly 48 | 49 | 13 50 | 00:00:16,720 --> 00:00:17,560 51 | I mean 52 | 53 | 14 54 | 00:00:17,560 --> 00:00:18,520 55 | I'm not encouraging it for everybody 56 | 57 | 15 58 | 00:00:18,520 --> 00:00:19,280 59 | because some people 60 | 61 | 16 62 | 00:00:19,280 --> 00:00:20,120 63 | generally 64 | 65 | 17 66 | 00:00:20,120 --> 00:00:20,800 67 | biologically 68 | 69 | 18 70 | 00:00:20,800 --> 00:00:21,160 71 | doesn't 72 | 73 | 19 74 | 00:00:21,160 --> 00:00:21,920 75 | jive with them 76 | 77 | 20 78 | 00:00:21,920 --> 00:00:22,760 79 | Yeah 80 | 81 | 21 82 | 00:00:22,760 --> 00:00:23,360 83 | But 84 | 85 | 22 86 | 00:00:23,360 --> 00:00:24,560 87 | the fundamental thing is 88 | 89 | 23 90 | 00:00:24,560 --> 00:00:26,000 91 | we as adult human beings 92 | 93 | 24 94 | 00:00:26,000 --> 00:00:27,200 95 | need to take responsibility 96 | 97 | 25 98 | 00:00:27,200 --> 00:00:28,200 99 | for our own lives 100 | 101 | 26 102 | 00:00:28,200 --> 00:00:29,640 103 | and our own decisions 104 | 105 | 27 106 | 00:00:29,960 --> 00:00:30,800 107 | and we need not 108 | 109 | 28 110 | 00:00:30,800 --> 00:00:31,080 111 | hand 112 | 113 | 29 114 | 00:00:31,080 --> 00:00:31,960 115 | that responsibility 116 | 117 | 30 118 | 00:00:31,960 --> 00:00:32,240 119 | over 120 | 121 | 31 122 | 00:00:32,240 --> 00:00:33,240 123 | to governmental 124 | 125 | 32 126 | 00:00:33,240 --> 00:00:34,000 127 | institutions 128 | 129 | 33 130 | 00:00:34,000 --> 00:00:34,480 131 | especially 132 | 133 | 34 134 | 00:00:34,480 --> 00:00:35,200 135 | when it concerns 136 | 137 | 35 138 | 00:00:35,200 --> 00:00:35,720 139 | something as 140 | 141 | 36 142 | 00:00:35,720 --> 00:00:36,360 143 | intimate 144 | 145 | 37 146 | 00:00:36,360 --> 00:00:37,080 147 | and 148 | 149 | 38 150 | 00:00:37,080 --> 00:00:38,000 151 | personal 152 | 153 | 39 154 | 00:00:38,000 --> 00:00:39,200 155 | as our consciousness 156 | 157 | 1 158 | 00:00:00,000 --> 00:00:02,720 159 | People can develop these patterns of behavior 160 | 161 | 2 162 | 00:00:02,720 --> 00:00:04,280 163 | that are destructive with anything 164 | 165 | 3 166 | 00:00:04,280 --> 00:00:05,920 167 | whether it's with alcohol or cannabis 168 | 169 | 4 170 | 00:00:05,920 --> 00:00:08,000 171 | or sex or anything 172 | 173 | 5 174 | 00:00:08,000 --> 00:00:09,080 175 | people get 176 | 177 | 6 178 | 00:00:09,080 --> 00:00:09,920 179 | they get in ruts 180 | 181 | 7 182 | 00:00:09,920 --> 00:00:10,800 183 | and 184 | 185 | 8 186 | 00:00:10,800 --> 00:00:11,640 187 | it doesn't mean 188 | 189 | 9 190 | 00:00:11,640 --> 00:00:12,840 191 | that the cannabis is bad 192 | 193 | 10 194 | 00:00:12,840 --> 00:00:15,040 195 | it means that you are on a bad mental path 196 | 197 | 11 198 | 00:00:15,040 --> 00:00:15,880 199 | Yes 200 | 201 | 12 202 | 00:00:15,880 --> 00:00:16,720 203 | Exactly 204 | 205 | 13 206 | 00:00:16,720 --> 00:00:17,560 207 | I mean 208 | 209 | 14 210 | 00:00:17,560 --> 00:00:18,520 211 | I'm not encouraging it for everybody 212 | 213 | 15 214 | 00:00:18,520 --> 00:00:19,280 215 | because some people 216 | 217 | 16 218 | 00:00:19,280 --> 00:00:20,120 219 | generally 220 | 221 | 17 222 | 00:00:20,120 --> 00:00:20,800 223 | biologically 224 | 225 | 18 226 | 00:00:20,800 --> 00:00:21,160 227 | doesn't 228 | 229 | 19 230 | 00:00:21,160 --> 00:00:21,920 231 | jive with them 232 | 233 | 20 234 | 00:00:21,920 --> 00:00:22,760 235 | Yeah 236 | 237 | 21 238 | 00:00:22,760 --> 00:00:23,360 239 | But 240 | 241 | 22 242 | 00:00:23,360 --> 00:00:24,560 243 | the fundamental thing is 244 | 245 | 23 246 | 00:00:24,560 --> 00:00:26,000 247 | we as adult human beings 248 | 249 | 24 250 | 00:00:26,000 --> 00:00:27,200 251 | need to take responsibility 252 | 253 | 25 254 | 00:00:27,200 --> 00:00:28,200 255 | for our own lives 256 | 257 | 26 258 | 00:00:28,200 --> 00:00:29,640 259 | and our own decisions 260 | 261 | 27 262 | 00:00:29,960 --> 00:00:30,800 263 | and we need not 264 | 265 | 28 266 | 00:00:30,800 --> 00:00:31,080 267 | hand 268 | 269 | 29 270 | 00:00:31,080 --> 00:00:31,960 271 | that responsibility 272 | 273 | 30 274 | 00:00:31,960 --> 00:00:32,240 275 | over 276 | 277 | 31 278 | 00:00:32,240 --> 00:00:33,240 279 | to governmental 280 | 281 | 32 282 | 00:00:33,240 --> 00:00:34,000 283 | institutions 284 | 285 | 33 286 | 00:00:34,000 --> 00:00:34,480 287 | especially 288 | 289 | 34 290 | 00:00:34,480 --> 00:00:35,200 291 | when it concerns 292 | 293 | 35 294 | 00:00:35,200 --> 00:00:35,720 295 | something as 296 | 297 | 36 298 | 00:00:35,720 --> 00:00:36,360 299 | intimate 300 | 301 | 37 302 | 00:00:36,360 --> 00:00:37,080 303 | and 304 | 305 | 38 306 | 00:00:37,080 --> 00:00:38,000 307 | personal 308 | 309 | 39 310 | 00:00:38,000 --> 00:00:39,200 311 | as our consciousness 312 | 313 | --------------------------------------------------------------------------------