Spaces:
Sleeping
Sleeping
add and disable rumble option, webscraper is buggin
Browse files- data/rumble/audio.mp3 +0 -0
- data/rumble/subtitles.srt +404 -0
- endpoints/__init__.py +1 -0
- endpoints/rumble.py +85 -0
- endpoints/utils.py +41 -1
- generate_rumble_clips.py +101 -0
- server.py +2 -1
- static/index.html +62 -7
data/rumble/audio.mp3
ADDED
Binary file (481 kB). View file
|
|
data/rumble/subtitles.srt
ADDED
@@ -0,0 +1,404 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
1.1
|
2 |
+
00:00:00,320 --> 00:00:00,380
|
3 |
+
and
|
4 |
+
|
5 |
+
1.2
|
6 |
+
00:00:00,380 --> 00:00:00,600
|
7 |
+
get
|
8 |
+
|
9 |
+
1.3
|
10 |
+
00:00:00,600 --> 00:00:00,780
|
11 |
+
to
|
12 |
+
|
13 |
+
1.4
|
14 |
+
00:00:00,780 --> 00:00:00,880
|
15 |
+
the
|
16 |
+
|
17 |
+
1.5
|
18 |
+
00:00:00,880 --> 00:00:01,340
|
19 |
+
core
|
20 |
+
|
21 |
+
1.6
|
22 |
+
00:00:01,340 --> 00:00:01,580
|
23 |
+
of
|
24 |
+
|
25 |
+
1.7
|
26 |
+
00:00:01,580 --> 00:00:01,760
|
27 |
+
the
|
28 |
+
|
29 |
+
1.8
|
30 |
+
00:00:01,760 --> 00:00:02,140
|
31 |
+
issue,
|
32 |
+
|
33 |
+
1.9
|
34 |
+
00:00:02,379 --> 00:00:02,480
|
35 |
+
to
|
36 |
+
|
37 |
+
1.10
|
38 |
+
00:00:02,480 --> 00:00:02,799
|
39 |
+
get
|
40 |
+
|
41 |
+
1.11
|
42 |
+
00:00:02,799 --> 00:00:02,980
|
43 |
+
to
|
44 |
+
|
45 |
+
1.12
|
46 |
+
00:00:02,980 --> 00:00:03,080
|
47 |
+
the
|
48 |
+
|
49 |
+
1.13
|
50 |
+
00:00:03,080 --> 00:00:03,740
|
51 |
+
fundamental
|
52 |
+
|
53 |
+
1.14
|
54 |
+
00:00:03,740 --> 00:00:04,320
|
55 |
+
point,
|
56 |
+
|
57 |
+
1.15
|
58 |
+
00:00:05,059 --> 00:00:05,799
|
59 |
+
which
|
60 |
+
|
61 |
+
1.16
|
62 |
+
00:00:05,799 --> 00:00:05,960
|
63 |
+
is
|
64 |
+
|
65 |
+
1.17
|
66 |
+
00:00:05,960 --> 00:00:06,240
|
67 |
+
why
|
68 |
+
|
69 |
+
1.18
|
70 |
+
00:00:06,240 --> 00:00:06,419
|
71 |
+
a
|
72 |
+
|
73 |
+
1.19
|
74 |
+
00:00:06,419 --> 00:00:06,559
|
75 |
+
lot
|
76 |
+
|
77 |
+
1.20
|
78 |
+
00:00:06,559 --> 00:00:06,679
|
79 |
+
of
|
80 |
+
|
81 |
+
1.21
|
82 |
+
00:00:06,679 --> 00:00:06,839
|
83 |
+
people
|
84 |
+
|
85 |
+
1.22
|
86 |
+
00:00:06,839 --> 00:00:07,099
|
87 |
+
don't
|
88 |
+
|
89 |
+
1.23
|
90 |
+
00:00:07,099 --> 00:00:07,299
|
91 |
+
like
|
92 |
+
|
93 |
+
1.24
|
94 |
+
00:00:07,299 --> 00:00:07,519
|
95 |
+
me
|
96 |
+
|
97 |
+
1.25
|
98 |
+
00:00:07,519 --> 00:00:07,780
|
99 |
+
because
|
100 |
+
|
101 |
+
1.26
|
102 |
+
00:00:07,780 --> 00:00:07,980
|
103 |
+
it's
|
104 |
+
|
105 |
+
2.1
|
106 |
+
00:00:07,980 --> 00:00:08,679
|
107 |
+
brash
|
108 |
+
|
109 |
+
2.2
|
110 |
+
00:00:08,679 --> 00:00:08,939
|
111 |
+
and
|
112 |
+
|
113 |
+
2.3
|
114 |
+
00:00:08,939 --> 00:00:09,099
|
115 |
+
you
|
116 |
+
|
117 |
+
2.4
|
118 |
+
00:00:09,099 --> 00:00:09,220
|
119 |
+
know,
|
120 |
+
|
121 |
+
2.5
|
122 |
+
00:00:09,339 --> 00:00:09,359
|
123 |
+
when
|
124 |
+
|
125 |
+
2.6
|
126 |
+
00:00:09,359 --> 00:00:09,460
|
127 |
+
you
|
128 |
+
|
129 |
+
2.7
|
130 |
+
00:00:09,460 --> 00:00:09,939
|
131 |
+
go
|
132 |
+
|
133 |
+
2.8
|
134 |
+
00:00:09,939 --> 00:00:10,199
|
135 |
+
right
|
136 |
+
|
137 |
+
2.9
|
138 |
+
00:00:10,199 --> 00:00:10,380
|
139 |
+
with
|
140 |
+
|
141 |
+
2.10
|
142 |
+
00:00:10,380 --> 00:00:10,519
|
143 |
+
the
|
144 |
+
|
145 |
+
2.11
|
146 |
+
00:00:10,519 --> 00:00:10,919
|
147 |
+
fundamental
|
148 |
+
|
149 |
+
2.12
|
150 |
+
00:00:10,919 --> 00:00:11,380
|
151 |
+
point,
|
152 |
+
|
153 |
+
2.13
|
154 |
+
00:00:11,759 --> 00:00:12,439
|
155 |
+
I
|
156 |
+
|
157 |
+
2.14
|
158 |
+
00:00:12,439 --> 00:00:12,640
|
159 |
+
think
|
160 |
+
|
161 |
+
2.15
|
162 |
+
00:00:12,640 --> 00:00:13,000
|
163 |
+
a
|
164 |
+
|
165 |
+
2.16
|
166 |
+
00:00:13,000 --> 00:00:13,160
|
167 |
+
lot
|
168 |
+
|
169 |
+
2.17
|
170 |
+
00:00:13,160 --> 00:00:13,300
|
171 |
+
of
|
172 |
+
|
173 |
+
2.18
|
174 |
+
00:00:13,300 --> 00:00:13,539
|
175 |
+
people
|
176 |
+
|
177 |
+
2.19
|
178 |
+
00:00:13,539 --> 00:00:13,779
|
179 |
+
are
|
180 |
+
|
181 |
+
2.20
|
182 |
+
00:00:13,779 --> 00:00:14,080
|
183 |
+
ready
|
184 |
+
|
185 |
+
2.21
|
186 |
+
00:00:14,080 --> 00:00:14,320
|
187 |
+
for
|
188 |
+
|
189 |
+
2.22
|
190 |
+
00:00:14,320 --> 00:00:14,580
|
191 |
+
that,
|
192 |
+
|
193 |
+
2.23
|
194 |
+
00:00:14,699 --> 00:00:14,820
|
195 |
+
you
|
196 |
+
|
197 |
+
2.24
|
198 |
+
00:00:14,820 --> 00:00:15,000
|
199 |
+
know,
|
200 |
+
|
201 |
+
2.25
|
202 |
+
00:00:15,220 --> 00:00:15,339
|
203 |
+
if
|
204 |
+
|
205 |
+
2.26
|
206 |
+
00:00:15,339 --> 00:00:15,740
|
207 |
+
they're
|
208 |
+
|
209 |
+
2.27
|
210 |
+
00:00:15,740 --> 00:00:15,859
|
211 |
+
in
|
212 |
+
|
213 |
+
2.28
|
214 |
+
00:00:15,859 --> 00:00:16,000
|
215 |
+
a
|
216 |
+
|
217 |
+
2.29
|
218 |
+
00:00:16,000 --> 00:00:16,300
|
219 |
+
totally
|
220 |
+
|
221 |
+
3.1
|
222 |
+
00:00:16,300 --> 00:00:16,699
|
223 |
+
if
|
224 |
+
|
225 |
+
3.2
|
226 |
+
00:00:16,699 --> 00:00:16,879
|
227 |
+
they
|
228 |
+
|
229 |
+
3.3
|
230 |
+
00:00:16,879 --> 00:00:17,000
|
231 |
+
have
|
232 |
+
|
233 |
+
3.4
|
234 |
+
00:00:17,000 --> 00:00:17,079
|
235 |
+
a
|
236 |
+
|
237 |
+
3.5
|
238 |
+
00:00:17,079 --> 00:00:17,320
|
239 |
+
totally
|
240 |
+
|
241 |
+
3.6
|
242 |
+
00:00:17,320 --> 00:00:17,640
|
243 |
+
different
|
244 |
+
|
245 |
+
3.7
|
246 |
+
00:00:17,640 --> 00:00:18,239
|
247 |
+
perspective.
|
248 |
+
|
249 |
+
3.8
|
250 |
+
00:00:18,679 --> 00:00:18,739
|
251 |
+
What
|
252 |
+
|
253 |
+
3.9
|
254 |
+
00:00:18,739 --> 00:00:18,839
|
255 |
+
I
|
256 |
+
|
257 |
+
3.10
|
258 |
+
00:00:18,839 --> 00:00:18,980
|
259 |
+
mean
|
260 |
+
|
261 |
+
3.11
|
262 |
+
00:00:18,980 --> 00:00:19,160
|
263 |
+
by
|
264 |
+
|
265 |
+
3.12
|
266 |
+
00:00:19,160 --> 00:00:19,339
|
267 |
+
that
|
268 |
+
|
269 |
+
3.13
|
270 |
+
00:00:19,339 --> 00:00:19,460
|
271 |
+
is
|
272 |
+
|
273 |
+
3.14
|
274 |
+
00:00:19,460 --> 00:00:19,600
|
275 |
+
when
|
276 |
+
|
277 |
+
3.15
|
278 |
+
00:00:19,600 --> 00:00:19,719
|
279 |
+
I
|
280 |
+
|
281 |
+
3.16
|
282 |
+
00:00:19,719 --> 00:00:19,899
|
283 |
+
go
|
284 |
+
|
285 |
+
3.17
|
286 |
+
00:00:19,899 --> 00:00:20,079
|
287 |
+
out
|
288 |
+
|
289 |
+
3.18
|
290 |
+
00:00:20,079 --> 00:00:20,280
|
291 |
+
there
|
292 |
+
|
293 |
+
3.19
|
294 |
+
00:00:20,280 --> 00:00:20,399
|
295 |
+
and
|
296 |
+
|
297 |
+
3.20
|
298 |
+
00:00:20,399 --> 00:00:20,559
|
299 |
+
say,
|
300 |
+
|
301 |
+
3.21
|
302 |
+
00:00:20,660 --> 00:00:20,820
|
303 |
+
hey,
|
304 |
+
|
305 |
+
3.22
|
306 |
+
00:00:21,100 --> 00:00:21,480
|
307 |
+
Christ
|
308 |
+
|
309 |
+
3.23
|
310 |
+
00:00:21,480 --> 00:00:21,739
|
311 |
+
is
|
312 |
+
|
313 |
+
3.24
|
314 |
+
00:00:21,739 --> 00:00:22,059
|
315 |
+
king
|
316 |
+
|
317 |
+
3.25
|
318 |
+
00:00:22,059 --> 00:00:22,620
|
319 |
+
and
|
320 |
+
|
321 |
+
3.26
|
322 |
+
00:00:22,620 --> 00:00:22,820
|
323 |
+
that
|
324 |
+
|
325 |
+
3.27
|
326 |
+
00:00:22,820 --> 00:00:23,059
|
327 |
+
means
|
328 |
+
|
329 |
+
4.1
|
330 |
+
00:00:23,300 --> 00:00:23,519
|
331 |
+
Jews
|
332 |
+
|
333 |
+
4.2
|
334 |
+
00:00:23,519 --> 00:00:23,899
|
335 |
+
can't
|
336 |
+
|
337 |
+
4.3
|
338 |
+
00:00:23,899 --> 00:00:24,059
|
339 |
+
be
|
340 |
+
|
341 |
+
4.4
|
342 |
+
00:00:24,059 --> 00:00:24,199
|
343 |
+
in
|
344 |
+
|
345 |
+
4.5
|
346 |
+
00:00:24,199 --> 00:00:24,660
|
347 |
+
power.
|
348 |
+
|
349 |
+
4.6
|
350 |
+
00:00:25,059 --> 00:00:25,739
|
351 |
+
People
|
352 |
+
|
353 |
+
4.7
|
354 |
+
00:00:25,739 --> 00:00:25,980
|
355 |
+
go,
|
356 |
+
|
357 |
+
4.8
|
358 |
+
00:00:26,120 --> 00:00:26,280
|
359 |
+
whoa,
|
360 |
+
|
361 |
+
4.9
|
362 |
+
00:00:26,440 --> 00:00:26,500
|
363 |
+
I
|
364 |
+
|
365 |
+
4.10
|
366 |
+
00:00:26,500 --> 00:00:26,660
|
367 |
+
don't
|
368 |
+
|
369 |
+
4.11
|
370 |
+
00:00:26,660 --> 00:00:26,820
|
371 |
+
hear
|
372 |
+
|
373 |
+
4.12
|
374 |
+
00:00:26,820 --> 00:00:27,079
|
375 |
+
people
|
376 |
+
|
377 |
+
4.13
|
378 |
+
00:00:27,079 --> 00:00:27,399
|
379 |
+
talk
|
380 |
+
|
381 |
+
4.14
|
382 |
+
00:00:27,399 --> 00:00:27,640
|
383 |
+
like
|
384 |
+
|
385 |
+
4.15
|
386 |
+
00:00:27,640 --> 00:00:27,960
|
387 |
+
that.
|
388 |
+
|
389 |
+
4.16
|
390 |
+
00:00:28,320 --> 00:00:28,839
|
391 |
+
What
|
392 |
+
|
393 |
+
4.17
|
394 |
+
00:00:28,839 --> 00:00:28,920
|
395 |
+
do
|
396 |
+
|
397 |
+
4.18
|
398 |
+
00:00:28,920 --> 00:00:29,000
|
399 |
+
you
|
400 |
+
|
401 |
+
4.19
|
402 |
+
00:00:29,000 --> 00:00:29,199
|
403 |
+
mean?
|
404 |
+
|
endpoints/__init__.py
CHANGED
@@ -4,3 +4,4 @@ from .subway import router as subway_router
|
|
4 |
from .minecraft import router as minecraft_router
|
5 |
from .basic import router as basic_router
|
6 |
from .video_editor_cut import router as video_editor
|
|
|
|
4 |
from .minecraft import router as minecraft_router
|
5 |
from .basic import router as basic_router
|
6 |
from .video_editor_cut import router as video_editor
|
7 |
+
from .rumble import router as rumble_router
|
endpoints/rumble.py
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import Body, HTTPException
|
2 |
+
from fastapi.responses import StreamingResponse
|
3 |
+
from typing import Optional
|
4 |
+
from fastapi import APIRouter
|
5 |
+
from generate_rumble_clips import generate_rumble_clips
|
6 |
+
from endpoints.utils import remove_content_from_dir
|
7 |
+
from endpoints.utils import rumble_link_scraper
|
8 |
+
import io
|
9 |
+
import zipfile
|
10 |
+
import pydantic
|
11 |
+
import os
|
12 |
+
|
13 |
+
router = APIRouter()
|
14 |
+
|
15 |
+
class ClipData(pydantic.BaseModel):
|
16 |
+
start: str
|
17 |
+
end: str
|
18 |
+
name: str
|
19 |
+
clip_link: Optional[str] = None
|
20 |
+
|
21 |
+
class RumbleData(pydantic.BaseModel):
|
22 |
+
video_url: str
|
23 |
+
color: str
|
24 |
+
size: int
|
25 |
+
font: str
|
26 |
+
credit: str
|
27 |
+
credit_size: int
|
28 |
+
clips: list[ClipData]
|
29 |
+
|
30 |
+
|
31 |
+
@router.post("/rumble")
|
32 |
+
async def create_rumble_video(request_data: RumbleData = Body(...)):
|
33 |
+
|
34 |
+
# Extract values from the request_data dictionary
|
35 |
+
video_url = request_data.video_url
|
36 |
+
color = request_data.color
|
37 |
+
size = request_data.size
|
38 |
+
font = request_data.font
|
39 |
+
credit = request_data.credit
|
40 |
+
credit_size = request_data.credit_size
|
41 |
+
clips = request_data.clips
|
42 |
+
|
43 |
+
options = {
|
44 |
+
"video_url": video_url,
|
45 |
+
"clips": clips,
|
46 |
+
"font_color": color,
|
47 |
+
"font_size": size,
|
48 |
+
"font_family": font,
|
49 |
+
"border_size": 1,
|
50 |
+
"credit": credit,
|
51 |
+
"credit_size": credit_size,
|
52 |
+
}
|
53 |
+
|
54 |
+
try:
|
55 |
+
# scrape the web for the rumble link
|
56 |
+
options["video_url"] = None
|
57 |
+
|
58 |
+
print(options["video_url"])
|
59 |
+
|
60 |
+
# Create the directory if it doesn't exist
|
61 |
+
if not os.path.exists("./data/rumble/clips"):
|
62 |
+
os.makedirs("./data/rumble/clips")
|
63 |
+
|
64 |
+
# Generate the clips
|
65 |
+
clips = generate_rumble_clips(options)
|
66 |
+
|
67 |
+
# Create a zip archive in memory
|
68 |
+
zip_buffer = io.BytesIO()
|
69 |
+
with zipfile.ZipFile(zip_buffer, "w") as zip_file:
|
70 |
+
for clip in clips:
|
71 |
+
zip_file.write(clip.clip_link, arcname=f"clips/{clip.name}.mp4")
|
72 |
+
|
73 |
+
# Reset the buffer position to the beginning
|
74 |
+
zip_buffer.seek(0)
|
75 |
+
|
76 |
+
# remove the clips directory content
|
77 |
+
remove_content_from_dir("./data/rumble/clips")
|
78 |
+
|
79 |
+
# Return the zip archive as a StreamingResponse
|
80 |
+
return StreamingResponse(io.BytesIO(zip_buffer.read()), media_type="application/zip", headers={
|
81 |
+
'Content-Disposition': 'attachment; filename="clips.zip"'
|
82 |
+
})
|
83 |
+
except Exception as e:
|
84 |
+
# Handle exceptions appropriately
|
85 |
+
raise HTTPException(status_code=500, detail=str(e))
|
endpoints/utils.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1 |
import os
|
2 |
import shutil
|
3 |
import time
|
|
|
|
|
|
|
4 |
|
5 |
def get_file_name(prefix: str) -> str:
|
6 |
return f"{prefix}_{time.strftime('%Y%m%d-%H%M%S')}.mp4"
|
7 |
|
|
|
8 |
def remove_content_from_dir(folder):
|
9 |
for filename in os.listdir(folder):
|
10 |
file_path = os.path.join(folder, filename)
|
@@ -14,4 +18,40 @@ def remove_content_from_dir(folder):
|
|
14 |
elif os.path.isdir(file_path):
|
15 |
shutil.rmtree(file_path)
|
16 |
except Exception as e:
|
17 |
-
print('Failed to delete %s. Reason: %s' % (file_path, e))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
import shutil
|
3 |
import time
|
4 |
+
import httpx
|
5 |
+
from bs4 import BeautifulSoup
|
6 |
+
|
7 |
|
8 |
def get_file_name(prefix: str) -> str:
|
9 |
return f"{prefix}_{time.strftime('%Y%m%d-%H%M%S')}.mp4"
|
10 |
|
11 |
+
|
12 |
def remove_content_from_dir(folder):
|
13 |
for filename in os.listdir(folder):
|
14 |
file_path = os.path.join(folder, filename)
|
|
|
18 |
elif os.path.isdir(file_path):
|
19 |
shutil.rmtree(file_path)
|
20 |
except Exception as e:
|
21 |
+
print('Failed to delete %s. Reason: %s' % (file_path, e))
|
22 |
+
|
23 |
+
|
24 |
+
async def rumble_link_scraper(rumble_link):
|
25 |
+
url = "https://imgpanda.com/wp-admin/admin-ajax.php"
|
26 |
+
downloader_type = "Rumble"
|
27 |
+
server_name = "Server2"
|
28 |
+
|
29 |
+
headers = {
|
30 |
+
"Content-Type": "application/x-www-form-urlencoded",
|
31 |
+
}
|
32 |
+
|
33 |
+
data = {
|
34 |
+
"action": "ajax_call_custom",
|
35 |
+
"SocialDownUrl": rumble_link,
|
36 |
+
"downloaderType": downloader_type,
|
37 |
+
"serverName": server_name,
|
38 |
+
}
|
39 |
+
|
40 |
+
async with httpx.AsyncClient() as client:
|
41 |
+
response = await client.post(url, headers=headers, data=data)
|
42 |
+
|
43 |
+
if response.status_code == 200:
|
44 |
+
soup = BeautifulSoup(response.text, 'html.parser')
|
45 |
+
inner_html_string = ' '.join(response.text.split(' ')[1:-1])
|
46 |
+
inner_soup = BeautifulSoup(inner_html_string, 'html.parser')
|
47 |
+
|
48 |
+
# all a-tags within the class "social-download-result"
|
49 |
+
a_tags = inner_soup.select('a')
|
50 |
+
last_a_tag = a_tags[-1]
|
51 |
+
|
52 |
+
if last_a_tag:
|
53 |
+
return last_a_tag.get('href').replace('\\', '').replace('"', '')
|
54 |
+
else:
|
55 |
+
print('No matching <a> tag found within the class "social-download-result"')
|
56 |
+
else:
|
57 |
+
print(f"Error: {response.status_code}")
|
generate_rumble_clips.py
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from utils.create_subtitles import hex_to_ffmpeg_color, create_subtitles
|
3 |
+
from utils.transcribe_audio import transcribe_audio
|
4 |
+
from utils.video_to_audio import extract_audio
|
5 |
+
import subprocess
|
6 |
+
|
7 |
+
|
8 |
+
def generate_rumble_clips(customization_options, output_directory="./data/rumble/clips"):
|
9 |
+
customization_options['font_color'] = hex_to_ffmpeg_color(customization_options['font_color'])
|
10 |
+
audio_file_path = "./data/rumble/audio.mp3"
|
11 |
+
subtitle_file = "./data/rumble/subtitles.srt"
|
12 |
+
|
13 |
+
clips = []
|
14 |
+
|
15 |
+
for idx, clip in enumerate(customization_options['clips']):
|
16 |
+
# Generate the clip
|
17 |
+
video_clip_path = cut_video(customization_options['video_url'], clip.start, clip.end, output_directory, idx)
|
18 |
+
clip.clip_link = video_clip_path
|
19 |
+
clips.append(clip)
|
20 |
+
|
21 |
+
# Generate the subtitles
|
22 |
+
extract_audio(video_clip_path, audio_file_path)
|
23 |
+
transcription = transcribe_audio(audio_file_path)
|
24 |
+
create_subtitles(transcription, subtitle_file)
|
25 |
+
|
26 |
+
# Add the subtitle to the video
|
27 |
+
subtitle_video_clip = add_subtitle(video_clip_path, subtitle_file, customization_options)
|
28 |
+
|
29 |
+
# Add the text to the video
|
30 |
+
video_with_text = add_text_to_video(subtitle_video_clip, customization_options)
|
31 |
+
|
32 |
+
video_sped_up = speed_up_video(video_with_text)
|
33 |
+
clip.clip_link = video_sped_up
|
34 |
+
|
35 |
+
return clips
|
36 |
+
|
37 |
+
|
38 |
+
def cut_video(input_url, start_time, end_time, output_directory, idx):
|
39 |
+
output_filepath = os.path.join(output_directory, f'output_clip_{idx}.mp4')
|
40 |
+
|
41 |
+
command = [
|
42 |
+
'ffmpeg',
|
43 |
+
'-i', input_url,
|
44 |
+
'-ss', start_time,
|
45 |
+
'-to', end_time,
|
46 |
+
'-y', output_filepath
|
47 |
+
]
|
48 |
+
|
49 |
+
subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
50 |
+
|
51 |
+
return output_filepath
|
52 |
+
|
53 |
+
|
54 |
+
def add_subtitle(video_path, subtitle_file, customization_options):
|
55 |
+
output_filepath = video_path.replace('.mp4', '_with_subtitle.mp4')
|
56 |
+
|
57 |
+
subtitle_cmd = [
|
58 |
+
"ffmpeg",
|
59 |
+
"-i", video_path,
|
60 |
+
"-vf", f"subtitles={subtitle_file}:force_style='Fontsize={customization_options['font_size']},PrimaryColour={customization_options['font_color']},Fontname={customization_options['font_family']},MarginV=50'",
|
61 |
+
"-c:a", "copy",
|
62 |
+
'-y', output_filepath
|
63 |
+
]
|
64 |
+
|
65 |
+
subprocess.run(subtitle_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
66 |
+
|
67 |
+
return output_filepath
|
68 |
+
|
69 |
+
|
70 |
+
def add_text_to_video(video_path, customization_options):
|
71 |
+
output_filepath = video_path.replace('.mp4', '_with_text.mp4')
|
72 |
+
|
73 |
+
text_cmd = [
|
74 |
+
"ffmpeg",
|
75 |
+
"-i", video_path,
|
76 |
+
'-vf', f'drawtext=text=\'{customization_options["credit"]}\':fontcolor=white:fontsize={customization_options["credit_size"]}:box=1:[email protected]:boxborderw=5:x=(w-text_w)/2:y=h-text_h-10',
|
77 |
+
"-c:a", "copy",
|
78 |
+
'-y', output_filepath
|
79 |
+
]
|
80 |
+
|
81 |
+
subprocess.run(text_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
82 |
+
|
83 |
+
return output_filepath
|
84 |
+
|
85 |
+
|
86 |
+
def speed_up_video(video_path):
|
87 |
+
output_filepath = video_path.replace('.mp4', '_sped_up.mp4')
|
88 |
+
|
89 |
+
speed_up_cmd = [
|
90 |
+
"ffmpeg",
|
91 |
+
"-i", video_path,
|
92 |
+
"-filter:v", "setpts=0.66*PTS", # Speed up by 1.5x (1/1.5 = 0.6666)
|
93 |
+
"-filter:a", "atempo=1.5", # Speed up audio as well
|
94 |
+
"-c:v", "libx264", "-c:a", "aac",
|
95 |
+
"-strict", "experimental",
|
96 |
+
"-y", output_filepath
|
97 |
+
]
|
98 |
+
|
99 |
+
subprocess.run(speed_up_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
100 |
+
|
101 |
+
return output_filepath
|
server.py
CHANGED
@@ -3,7 +3,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
3 |
from fastapi.staticfiles import StaticFiles
|
4 |
from fastapi.responses import FileResponse
|
5 |
import uvicorn
|
6 |
-
from endpoints import subway_router, minecraft_router, basic_router, video_editor
|
7 |
|
8 |
app = FastAPI()
|
9 |
|
@@ -29,6 +29,7 @@ app.include_router(subway_router, prefix="/generate-subtitles")
|
|
29 |
app.include_router(minecraft_router, prefix="/generate-subtitles")
|
30 |
app.include_router(basic_router, prefix="/generate-subtitles")
|
31 |
app.include_router(video_editor, prefix="/video-editor")
|
|
|
32 |
|
33 |
if __name__ == "__main__":
|
34 |
uvicorn.run("server:app", host="localhost", port=8000, reload=True)
|
|
|
3 |
from fastapi.staticfiles import StaticFiles
|
4 |
from fastapi.responses import FileResponse
|
5 |
import uvicorn
|
6 |
+
from endpoints import subway_router, minecraft_router, basic_router, video_editor, rumble_router
|
7 |
|
8 |
app = FastAPI()
|
9 |
|
|
|
29 |
app.include_router(minecraft_router, prefix="/generate-subtitles")
|
30 |
app.include_router(basic_router, prefix="/generate-subtitles")
|
31 |
app.include_router(video_editor, prefix="/video-editor")
|
32 |
+
app.include_router(rumble_router, prefix="/generate-subtitles")
|
33 |
|
34 |
if __name__ == "__main__":
|
35 |
uvicorn.run("server:app", host="localhost", port=8000, reload=True)
|
static/index.html
CHANGED
@@ -11,15 +11,40 @@
|
|
11 |
<h1>BrainRotTok</h1>
|
12 |
<div class="form-buttons">
|
13 |
<ul>
|
14 |
-
<li><button class="show-form" data-form-id="basic-form">BASIC</button></li>
|
15 |
-
<li><button class="show-form" data-form-id="subway-form">SUBWAY</button></li>
|
16 |
<li>
|
17 |
-
<button class="show-form" data-form-id="
|
|
|
|
|
18 |
</li>
|
19 |
<li>
|
20 |
-
<button class="show-form" data-form-id="
|
|
|
|
|
21 |
</li>
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
</div>
|
24 |
<div id="forms-container">
|
25 |
<form
|
@@ -53,7 +78,11 @@
|
|
53 |
enctype="multipart/form-data"
|
54 |
>
|
55 |
<h2 class="form-title">SUBWAY</h2>
|
56 |
-
<p>
|
|
|
|
|
|
|
|
|
57 |
<label for="top_video">Top Video:</label>
|
58 |
<input type="file" id="top_video" name="top_video" />
|
59 |
<label for="bottom_video">Bottom Video:</label>
|
@@ -78,7 +107,10 @@
|
|
78 |
enctype="multipart/form-data"
|
79 |
>
|
80 |
<h2 class="form-title">MINECRAFT</h2>
|
81 |
-
<p>
|
|
|
|
|
|
|
82 |
<label for="video">Video:</label>
|
83 |
<input type="file" id="video" name="video" />
|
84 |
<label for="subtitles">Subtitles:</label>
|
@@ -95,6 +127,29 @@
|
|
95 |
<input type="number" id="credit_size" name="credit_size" />
|
96 |
<button type="submit">Submit</button>
|
97 |
</form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
<form
|
99 |
id="video-cutter-form"
|
100 |
class="hidden"
|
|
|
11 |
<h1>BrainRotTok</h1>
|
12 |
<div class="form-buttons">
|
13 |
<ul>
|
|
|
|
|
14 |
<li>
|
15 |
+
<button class="show-form" data-form-id="basic-form">
|
16 |
+
BASIC
|
17 |
+
</button>
|
18 |
</li>
|
19 |
<li>
|
20 |
+
<button class="show-form" data-form-id="subway-form">
|
21 |
+
SUBWAY
|
22 |
+
</button>
|
23 |
</li>
|
24 |
+
<li>
|
25 |
+
<button class="show-form" data-form-id="minecraft-form">
|
26 |
+
MINECRAFT
|
27 |
+
</button>
|
28 |
+
</li>
|
29 |
+
<li>
|
30 |
+
<button
|
31 |
+
class="show-form"
|
32 |
+
data-form-id="video-cutter-form"
|
33 |
+
disabled
|
34 |
+
>
|
35 |
+
VIDEO CUTTER
|
36 |
+
</button>
|
37 |
+
</li>
|
38 |
+
<li>
|
39 |
+
<button
|
40 |
+
class="show-form"
|
41 |
+
data-form-id="rumble-form"
|
42 |
+
disabled
|
43 |
+
>
|
44 |
+
RUMBLE
|
45 |
+
</button>
|
46 |
+
</li>
|
47 |
+
</ul>
|
48 |
</div>
|
49 |
<div id="forms-container">
|
50 |
<form
|
|
|
78 |
enctype="multipart/form-data"
|
79 |
>
|
80 |
<h2 class="form-title">SUBWAY</h2>
|
81 |
+
<p>
|
82 |
+
Merge a top and bottom video together with subtitles. The
|
83 |
+
top video will have no sound, while the bottom video will
|
84 |
+
have its subtitles generated.
|
85 |
+
</p>
|
86 |
<label for="top_video">Top Video:</label>
|
87 |
<input type="file" id="top_video" name="top_video" />
|
88 |
<label for="bottom_video">Bottom Video:</label>
|
|
|
107 |
enctype="multipart/form-data"
|
108 |
>
|
109 |
<h2 class="form-title">MINECRAFT</h2>
|
110 |
+
<p>
|
111 |
+
A background video playing while a script is being read by
|
112 |
+
an AI voice with subtitles.
|
113 |
+
</p>
|
114 |
<label for="video">Video:</label>
|
115 |
<input type="file" id="video" name="video" />
|
116 |
<label for="subtitles">Subtitles:</label>
|
|
|
127 |
<input type="number" id="credit_size" name="credit_size" />
|
128 |
<button type="submit">Submit</button>
|
129 |
</form>
|
130 |
+
<form
|
131 |
+
id="rumble-form"
|
132 |
+
class="hidden"
|
133 |
+
action="/generate-subtitles/rumble"
|
134 |
+
method="post"
|
135 |
+
enctype="multipart/form-data"
|
136 |
+
>
|
137 |
+
<h2 class="form-title">RUMBLE</h2>
|
138 |
+
<p>Generates subtitles for Rumble videos.</p>
|
139 |
+
<label for="video_url">Video Url:</label>
|
140 |
+
<input type="text" id="video_url" name="video_url" />
|
141 |
+
<label for="color">Color:</label>
|
142 |
+
<input type="text" id="color" name="color" />
|
143 |
+
<label for="size">Size:</label>
|
144 |
+
<input type="number" id="size" name="size" />
|
145 |
+
<label for="font">Font:</label>
|
146 |
+
<input type="text" id="font" name="font" />
|
147 |
+
<label for="credit">Credit:</label>
|
148 |
+
<input type="text" id="credit" name="credit" />
|
149 |
+
<label for="credit_size">Credit Size:</label>
|
150 |
+
<input type="number" id="credit_size" name="credit_size" />
|
151 |
+
<button type="submit">Submit</button>
|
152 |
+
</form>
|
153 |
<form
|
154 |
id="video-cutter-form"
|
155 |
class="hidden"
|