RafaG commited on
Commit
80b326d
·
verified ·
1 Parent(s): f111b6c

Upload 85 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +11 -0
  2. LICENSE +674 -0
  3. README.md +154 -13
  4. README_en.md +155 -0
  5. ViralCutter.ipynb +253 -0
  6. abrir_cmd.bat +16 -0
  7. api_config.json +12 -0
  8. changelog.md +94 -0
  9. check_landmarks.py +49 -0
  10. i18n/__pycache__/i18n.cpython-310.pyc +0 -0
  11. i18n/__pycache__/i18n.cpython-311.pyc +0 -0
  12. i18n/i18n.py +27 -0
  13. i18n/locale/en_US.json +211 -0
  14. i18n/locale/pt_BR.json +211 -0
  15. i18n/locale_diff.py +47 -0
  16. i18n/scan_i18n.py +75 -0
  17. install_dependencies.bat +27 -0
  18. main_improved.py +695 -0
  19. models/.local-model-here +0 -0
  20. prompt.txt +42 -0
  21. requirements.txt +23 -0
  22. run.bat +9 -0
  23. run_webui.bat +4 -0
  24. scripts/__pycache__/adjust_subtitles.cpython-310.pyc +0 -0
  25. scripts/__pycache__/adjust_subtitles.cpython-311.pyc +0 -0
  26. scripts/__pycache__/burn_subtitles.cpython-310.pyc +0 -0
  27. scripts/__pycache__/burn_subtitles.cpython-311.pyc +0 -0
  28. scripts/__pycache__/create_viral_segments.cpython-310.pyc +0 -0
  29. scripts/__pycache__/create_viral_segments.cpython-311.pyc +0 -0
  30. scripts/__pycache__/cut_json.cpython-310.pyc +0 -0
  31. scripts/__pycache__/cut_json.cpython-311.pyc +0 -0
  32. scripts/__pycache__/cut_segments.cpython-310.pyc +0 -0
  33. scripts/__pycache__/cut_segments.cpython-311.pyc +0 -0
  34. scripts/__pycache__/download_video.cpython-310.pyc +0 -0
  35. scripts/__pycache__/download_video.cpython-311.pyc +0 -0
  36. scripts/__pycache__/edit_video.cpython-310.pyc +0 -0
  37. scripts/__pycache__/edit_video.cpython-311.pyc +0 -0
  38. scripts/__pycache__/face_detection_insightface.cpython-310.pyc +0 -0
  39. scripts/__pycache__/face_detection_insightface.cpython-311.pyc +0 -0
  40. scripts/__pycache__/generate_premiere_xml.cpython-311.pyc +0 -0
  41. scripts/__pycache__/one_face.cpython-310.pyc +0 -0
  42. scripts/__pycache__/one_face.cpython-311.pyc +0 -0
  43. scripts/__pycache__/organize_output.cpython-310.pyc +0 -0
  44. scripts/__pycache__/organize_output.cpython-311.pyc +0 -0
  45. scripts/__pycache__/save_json.cpython-310.pyc +0 -0
  46. scripts/__pycache__/save_json.cpython-311.pyc +0 -0
  47. scripts/__pycache__/transcribe_cuts.cpython-310.pyc +0 -0
  48. scripts/__pycache__/transcribe_cuts.cpython-311.pyc +0 -0
  49. scripts/__pycache__/transcribe_video.cpython-310.pyc +0 -0
  50. scripts/__pycache__/transcribe_video.cpython-311.pyc +0 -0
.gitignore ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ env
2
+ .gitignore
3
+ scripts\__pycache__\*
4
+ i18n\__pycache__\*
5
+ *.pyc
6
+ VIRALS
7
+ *.7z
8
+ *.zip
9
+ api_config.json
10
+ versions
11
+ *.gguf
LICENSE ADDED
@@ -0,0 +1,674 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 3, 29 June 2007
3
+
4
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
5
+ Everyone is permitted to copy and distribute verbatim copies
6
+ of this license document, but changing it is not allowed.
7
+
8
+ Preamble
9
+
10
+ The GNU General Public License is a free, copyleft license for
11
+ software and other kinds of works.
12
+
13
+ The licenses for most software and other practical works are designed
14
+ to take away your freedom to share and change the works. By contrast,
15
+ the GNU General Public License is intended to guarantee your freedom to
16
+ share and change all versions of a program--to make sure it remains free
17
+ software for all its users. We, the Free Software Foundation, use the
18
+ GNU General Public License for most of our software; it applies also to
19
+ any other work released this way by its authors. You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ them if you wish), that you receive source code or can get it if you
26
+ want it, that you can change the software or use pieces of it in new
27
+ free programs, and that you know you can do these things.
28
+
29
+ To protect your rights, we need to prevent others from denying you
30
+ these rights or asking you to surrender the rights. Therefore, you have
31
+ certain responsibilities if you distribute copies of the software, or if
32
+ you modify it: responsibilities to respect the freedom of others.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must pass on to the recipients the same
36
+ freedoms that you received. You must make sure that they, too, receive
37
+ or can get the source code. And you must show them these terms so they
38
+ know their rights.
39
+
40
+ Developers that use the GNU GPL protect your rights with two steps:
41
+ (1) assert copyright on the software, and (2) offer you this License
42
+ giving you legal permission to copy, distribute and/or modify it.
43
+
44
+ For the developers' and authors' protection, the GPL clearly explains
45
+ that there is no warranty for this free software. For both users' and
46
+ authors' sake, the GPL requires that modified versions be marked as
47
+ changed, so that their problems will not be attributed erroneously to
48
+ authors of previous versions.
49
+
50
+ Some devices are designed to deny users access to install or run
51
+ modified versions of the software inside them, although the manufacturer
52
+ can do so. This is fundamentally incompatible with the aim of
53
+ protecting users' freedom to change the software. The systematic
54
+ pattern of such abuse occurs in the area of products for individuals to
55
+ use, which is precisely where it is most unacceptable. Therefore, we
56
+ have designed this version of the GPL to prohibit the practice for those
57
+ products. If such problems arise substantially in other domains, we
58
+ stand ready to extend this provision to those domains in future versions
59
+ of the GPL, as needed to protect the freedom of users.
60
+
61
+ Finally, every program is threatened constantly by software patents.
62
+ States should not allow patents to restrict development and use of
63
+ software on general-purpose computers, but in those that do, we wish to
64
+ avoid the special danger that patents applied to a free program could
65
+ make it effectively proprietary. To prevent this, the GPL assures that
66
+ patents cannot be used to render the program non-free.
67
+
68
+ The precise terms and conditions for copying, distribution and
69
+ modification follow.
70
+
71
+ TERMS AND CONDITIONS
72
+
73
+ 0. Definitions.
74
+
75
+ "This License" refers to version 3 of the GNU General Public License.
76
+
77
+ "Copyright" also means copyright-like laws that apply to other kinds of
78
+ works, such as semiconductor masks.
79
+
80
+ "The Program" refers to any copyrightable work licensed under this
81
+ License. Each licensee is addressed as "you". "Licensees" and
82
+ "recipients" may be individuals or organizations.
83
+
84
+ To "modify" a work means to copy from or adapt all or part of the work
85
+ in a fashion requiring copyright permission, other than the making of an
86
+ exact copy. The resulting work is called a "modified version" of the
87
+ earlier work or a work "based on" the earlier work.
88
+
89
+ A "covered work" means either the unmodified Program or a work based
90
+ on the Program.
91
+
92
+ To "propagate" a work means to do anything with it that, without
93
+ permission, would make you directly or secondarily liable for
94
+ infringement under applicable copyright law, except executing it on a
95
+ computer or modifying a private copy. Propagation includes copying,
96
+ distribution (with or without modification), making available to the
97
+ public, and in some countries other activities as well.
98
+
99
+ To "convey" a work means any kind of propagation that enables other
100
+ parties to make or receive copies. Mere interaction with a user through
101
+ a computer network, with no transfer of a copy, is not conveying.
102
+
103
+ An interactive user interface displays "Appropriate Legal Notices"
104
+ to the extent that it includes a convenient and prominently visible
105
+ feature that (1) displays an appropriate copyright notice, and (2)
106
+ tells the user that there is no warranty for the work (except to the
107
+ extent that warranties are provided), that licensees may convey the
108
+ work under this License, and how to view a copy of this License. If
109
+ the interface presents a list of user commands or options, such as a
110
+ menu, a prominent item in the list meets this criterion.
111
+
112
+ 1. Source Code.
113
+
114
+ The "source code" for a work means the preferred form of the work
115
+ for making modifications to it. "Object code" means any non-source
116
+ form of a work.
117
+
118
+ A "Standard Interface" means an interface that either is an official
119
+ standard defined by a recognized standards body, or, in the case of
120
+ interfaces specified for a particular programming language, one that
121
+ is widely used among developers working in that language.
122
+
123
+ The "System Libraries" of an executable work include anything, other
124
+ than the work as a whole, that (a) is included in the normal form of
125
+ packaging a Major Component, but which is not part of that Major
126
+ Component, and (b) serves only to enable use of the work with that
127
+ Major Component, or to implement a Standard Interface for which an
128
+ implementation is available to the public in source code form. A
129
+ "Major Component", in this context, means a major essential component
130
+ (kernel, window system, and so on) of the specific operating system
131
+ (if any) on which the executable work runs, or a compiler used to
132
+ produce the work, or an object code interpreter used to run it.
133
+
134
+ The "Corresponding Source" for a work in object code form means all
135
+ the source code needed to generate, install, and (for an executable
136
+ work) run the object code and to modify the work, including scripts to
137
+ control those activities. However, it does not include the work's
138
+ System Libraries, or general-purpose tools or generally available free
139
+ programs which are used unmodified in performing those activities but
140
+ which are not part of the work. For example, Corresponding Source
141
+ includes interface definition files associated with source files for
142
+ the work, and the source code for shared libraries and dynamically
143
+ linked subprograms that the work is specifically designed to require,
144
+ such as by intimate data communication or control flow between those
145
+ subprograms and other parts of the work.
146
+
147
+ The Corresponding Source need not include anything that users
148
+ can regenerate automatically from other parts of the Corresponding
149
+ Source.
150
+
151
+ The Corresponding Source for a work in source code form is that
152
+ same work.
153
+
154
+ 2. Basic Permissions.
155
+
156
+ All rights granted under this License are granted for the term of
157
+ copyright on the Program, and are irrevocable provided the stated
158
+ conditions are met. This License explicitly affirms your unlimited
159
+ permission to run the unmodified Program. The output from running a
160
+ covered work is covered by this License only if the output, given its
161
+ content, constitutes a covered work. This License acknowledges your
162
+ rights of fair use or other equivalent, as provided by copyright law.
163
+
164
+ You may make, run and propagate covered works that you do not
165
+ convey, without conditions so long as your license otherwise remains
166
+ in force. You may convey covered works to others for the sole purpose
167
+ of having them make modifications exclusively for you, or provide you
168
+ with facilities for running those works, provided that you comply with
169
+ the terms of this License in conveying all material for which you do
170
+ not control copyright. Those thus making or running the covered works
171
+ for you must do so exclusively on your behalf, under your direction
172
+ and control, on terms that prohibit them from making any copies of
173
+ your copyrighted material outside their relationship with you.
174
+
175
+ Conveying under any other circumstances is permitted solely under
176
+ the conditions stated below. Sublicensing is not allowed; section 10
177
+ makes it unnecessary.
178
+
179
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180
+
181
+ No covered work shall be deemed part of an effective technological
182
+ measure under any applicable law fulfilling obligations under article
183
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184
+ similar laws prohibiting or restricting circumvention of such
185
+ measures.
186
+
187
+ When you convey a covered work, you waive any legal power to forbid
188
+ circumvention of technological measures to the extent such circumvention
189
+ is effected by exercising rights under this License with respect to
190
+ the covered work, and you disclaim any intention to limit operation or
191
+ modification of the work as a means of enforcing, against the work's
192
+ users, your or third parties' legal rights to forbid circumvention of
193
+ technological measures.
194
+
195
+ 4. Conveying Verbatim Copies.
196
+
197
+ You may convey verbatim copies of the Program's source code as you
198
+ receive it, in any medium, provided that you conspicuously and
199
+ appropriately publish on each copy an appropriate copyright notice;
200
+ keep intact all notices stating that this License and any
201
+ non-permissive terms added in accord with section 7 apply to the code;
202
+ keep intact all notices of the absence of any warranty; and give all
203
+ recipients a copy of this License along with the Program.
204
+
205
+ You may charge any price or no price for each copy that you convey,
206
+ and you may offer support or warranty protection for a fee.
207
+
208
+ 5. Conveying Modified Source Versions.
209
+
210
+ You may convey a work based on the Program, or the modifications to
211
+ produce it from the Program, in the form of source code under the
212
+ terms of section 4, provided that you also meet all of these conditions:
213
+
214
+ a) The work must carry prominent notices stating that you modified
215
+ it, and giving a relevant date.
216
+
217
+ b) The work must carry prominent notices stating that it is
218
+ released under this License and any conditions added under section
219
+ 7. This requirement modifies the requirement in section 4 to
220
+ "keep intact all notices".
221
+
222
+ c) You must license the entire work, as a whole, under this
223
+ License to anyone who comes into possession of a copy. This
224
+ License will therefore apply, along with any applicable section 7
225
+ additional terms, to the whole of the work, and all its parts,
226
+ regardless of how they are packaged. This License gives no
227
+ permission to license the work in any other way, but it does not
228
+ invalidate such permission if you have separately received it.
229
+
230
+ d) If the work has interactive user interfaces, each must display
231
+ Appropriate Legal Notices; however, if the Program has interactive
232
+ interfaces that do not display Appropriate Legal Notices, your
233
+ work need not make them do so.
234
+
235
+ A compilation of a covered work with other separate and independent
236
+ works, which are not by their nature extensions of the covered work,
237
+ and which are not combined with it such as to form a larger program,
238
+ in or on a volume of a storage or distribution medium, is called an
239
+ "aggregate" if the compilation and its resulting copyright are not
240
+ used to limit the access or legal rights of the compilation's users
241
+ beyond what the individual works permit. Inclusion of a covered work
242
+ in an aggregate does not cause this License to apply to the other
243
+ parts of the aggregate.
244
+
245
+ 6. Conveying Non-Source Forms.
246
+
247
+ You may convey a covered work in object code form under the terms
248
+ of sections 4 and 5, provided that you also convey the
249
+ machine-readable Corresponding Source under the terms of this License,
250
+ in one of these ways:
251
+
252
+ a) Convey the object code in, or embodied in, a physical product
253
+ (including a physical distribution medium), accompanied by the
254
+ Corresponding Source fixed on a durable physical medium
255
+ customarily used for software interchange.
256
+
257
+ b) Convey the object code in, or embodied in, a physical product
258
+ (including a physical distribution medium), accompanied by a
259
+ written offer, valid for at least three years and valid for as
260
+ long as you offer spare parts or customer support for that product
261
+ model, to give anyone who possesses the object code either (1) a
262
+ copy of the Corresponding Source for all the software in the
263
+ product that is covered by this License, on a durable physical
264
+ medium customarily used for software interchange, for a price no
265
+ more than your reasonable cost of physically performing this
266
+ conveying of source, or (2) access to copy the
267
+ Corresponding Source from a network server at no charge.
268
+
269
+ c) Convey individual copies of the object code with a copy of the
270
+ written offer to provide the Corresponding Source. This
271
+ alternative is allowed only occasionally and noncommercially, and
272
+ only if you received the object code with such an offer, in accord
273
+ with subsection 6b.
274
+
275
+ d) Convey the object code by offering access from a designated
276
+ place (gratis or for a charge), and offer equivalent access to the
277
+ Corresponding Source in the same way through the same place at no
278
+ further charge. You need not require recipients to copy the
279
+ Corresponding Source along with the object code. If the place to
280
+ copy the object code is a network server, the Corresponding Source
281
+ may be on a different server (operated by you or a third party)
282
+ that supports equivalent copying facilities, provided you maintain
283
+ clear directions next to the object code saying where to find the
284
+ Corresponding Source. Regardless of what server hosts the
285
+ Corresponding Source, you remain obligated to ensure that it is
286
+ available for as long as needed to satisfy these requirements.
287
+
288
+ e) Convey the object code using peer-to-peer transmission, provided
289
+ you inform other peers where the object code and Corresponding
290
+ Source of the work are being offered to the general public at no
291
+ charge under subsection 6d.
292
+
293
+ A separable portion of the object code, whose source code is excluded
294
+ from the Corresponding Source as a System Library, need not be
295
+ included in conveying the object code work.
296
+
297
+ A "User Product" is either (1) a "consumer product", which means any
298
+ tangible personal property which is normally used for personal, family,
299
+ or household purposes, or (2) anything designed or sold for incorporation
300
+ into a dwelling. In determining whether a product is a consumer product,
301
+ doubtful cases shall be resolved in favor of coverage. For a particular
302
+ product received by a particular user, "normally used" refers to a
303
+ typical or common use of that class of product, regardless of the status
304
+ of the particular user or of the way in which the particular user
305
+ actually uses, or expects or is expected to use, the product. A product
306
+ is a consumer product regardless of whether the product has substantial
307
+ commercial, industrial or non-consumer uses, unless such uses represent
308
+ the only significant mode of use of the product.
309
+
310
+ "Installation Information" for a User Product means any methods,
311
+ procedures, authorization keys, or other information required to install
312
+ and execute modified versions of a covered work in that User Product from
313
+ a modified version of its Corresponding Source. The information must
314
+ suffice to ensure that the continued functioning of the modified object
315
+ code is in no case prevented or interfered with solely because
316
+ modification has been made.
317
+
318
+ If you convey an object code work under this section in, or with, or
319
+ specifically for use in, a User Product, and the conveying occurs as
320
+ part of a transaction in which the right of possession and use of the
321
+ User Product is transferred to the recipient in perpetuity or for a
322
+ fixed term (regardless of how the transaction is characterized), the
323
+ Corresponding Source conveyed under this section must be accompanied
324
+ by the Installation Information. But this requirement does not apply
325
+ if neither you nor any third party retains the ability to install
326
+ modified object code on the User Product (for example, the work has
327
+ been installed in ROM).
328
+
329
+ The requirement to provide Installation Information does not include a
330
+ requirement to continue to provide support service, warranty, or updates
331
+ for a work that has been modified or installed by the recipient, or for
332
+ the User Product in which it has been modified or installed. Access to a
333
+ network may be denied when the modification itself materially and
334
+ adversely affects the operation of the network or violates the rules and
335
+ protocols for communication across the network.
336
+
337
+ Corresponding Source conveyed, and Installation Information provided,
338
+ in accord with this section must be in a format that is publicly
339
+ documented (and with an implementation available to the public in
340
+ source code form), and must require no special password or key for
341
+ unpacking, reading or copying.
342
+
343
+ 7. Additional Terms.
344
+
345
+ "Additional permissions" are terms that supplement the terms of this
346
+ License by making exceptions from one or more of its conditions.
347
+ Additional permissions that are applicable to the entire Program shall
348
+ be treated as though they were included in this License, to the extent
349
+ that they are valid under applicable law. If additional permissions
350
+ apply only to part of the Program, that part may be used separately
351
+ under those permissions, but the entire Program remains governed by
352
+ this License without regard to the additional permissions.
353
+
354
+ When you convey a copy of a covered work, you may at your option
355
+ remove any additional permissions from that copy, or from any part of
356
+ it. (Additional permissions may be written to require their own
357
+ removal in certain cases when you modify the work.) You may place
358
+ additional permissions on material, added by you to a covered work,
359
+ for which you have or can give appropriate copyright permission.
360
+
361
+ Notwithstanding any other provision of this License, for material you
362
+ add to a covered work, you may (if authorized by the copyright holders of
363
+ that material) supplement the terms of this License with terms:
364
+
365
+ a) Disclaiming warranty or limiting liability differently from the
366
+ terms of sections 15 and 16 of this License; or
367
+
368
+ b) Requiring preservation of specified reasonable legal notices or
369
+ author attributions in that material or in the Appropriate Legal
370
+ Notices displayed by works containing it; or
371
+
372
+ c) Prohibiting misrepresentation of the origin of that material, or
373
+ requiring that modified versions of such material be marked in
374
+ reasonable ways as different from the original version; or
375
+
376
+ d) Limiting the use for publicity purposes of names of licensors or
377
+ authors of the material; or
378
+
379
+ e) Declining to grant rights under trademark law for use of some
380
+ trade names, trademarks, or service marks; or
381
+
382
+ f) Requiring indemnification of licensors and authors of that
383
+ material by anyone who conveys the material (or modified versions of
384
+ it) with contractual assumptions of liability to the recipient, for
385
+ any liability that these contractual assumptions directly impose on
386
+ those licensors and authors.
387
+
388
+ All other non-permissive additional terms are considered "further
389
+ restrictions" within the meaning of section 10. If the Program as you
390
+ received it, or any part of it, contains a notice stating that it is
391
+ governed by this License along with a term that is a further
392
+ restriction, you may remove that term. If a license document contains
393
+ a further restriction but permits relicensing or conveying under this
394
+ License, you may add to a covered work material governed by the terms
395
+ of that license document, provided that the further restriction does
396
+ not survive such relicensing or conveying.
397
+
398
+ If you add terms to a covered work in accord with this section, you
399
+ must place, in the relevant source files, a statement of the
400
+ additional terms that apply to those files, or a notice indicating
401
+ where to find the applicable terms.
402
+
403
+ Additional terms, permissive or non-permissive, may be stated in the
404
+ form of a separately written license, or stated as exceptions;
405
+ the above requirements apply either way.
406
+
407
+ 8. Termination.
408
+
409
+ You may not propagate or modify a covered work except as expressly
410
+ provided under this License. Any attempt otherwise to propagate or
411
+ modify it is void, and will automatically terminate your rights under
412
+ this License (including any patent licenses granted under the third
413
+ paragraph of section 11).
414
+
415
+ However, if you cease all violation of this License, then your
416
+ license from a particular copyright holder is reinstated (a)
417
+ provisionally, unless and until the copyright holder explicitly and
418
+ finally terminates your license, and (b) permanently, if the copyright
419
+ holder fails to notify you of the violation by some reasonable means
420
+ prior to 60 days after the cessation.
421
+
422
+ Moreover, your license from a particular copyright holder is
423
+ reinstated permanently if the copyright holder notifies you of the
424
+ violation by some reasonable means, this is the first time you have
425
+ received notice of violation of this License (for any work) from that
426
+ copyright holder, and you cure the violation prior to 30 days after
427
+ your receipt of the notice.
428
+
429
+ Termination of your rights under this section does not terminate the
430
+ licenses of parties who have received copies or rights from you under
431
+ this License. If your rights have been terminated and not permanently
432
+ reinstated, you do not qualify to receive new licenses for the same
433
+ material under section 10.
434
+
435
+ 9. Acceptance Not Required for Having Copies.
436
+
437
+ You are not required to accept this License in order to receive or
438
+ run a copy of the Program. Ancillary propagation of a covered work
439
+ occurring solely as a consequence of using peer-to-peer transmission
440
+ to receive a copy likewise does not require acceptance. However,
441
+ nothing other than this License grants you permission to propagate or
442
+ modify any covered work. These actions infringe copyright if you do
443
+ not accept this License. Therefore, by modifying or propagating a
444
+ covered work, you indicate your acceptance of this License to do so.
445
+
446
+ 10. Automatic Licensing of Downstream Recipients.
447
+
448
+ Each time you convey a covered work, the recipient automatically
449
+ receives a license from the original licensors, to run, modify and
450
+ propagate that work, subject to this License. You are not responsible
451
+ for enforcing compliance by third parties with this License.
452
+
453
+ An "entity transaction" is a transaction transferring control of an
454
+ organization, or substantially all assets of one, or subdividing an
455
+ organization, or merging organizations. If propagation of a covered
456
+ work results from an entity transaction, each party to that
457
+ transaction who receives a copy of the work also receives whatever
458
+ licenses to the work the party's predecessor in interest had or could
459
+ give under the previous paragraph, plus a right to possession of the
460
+ Corresponding Source of the work from the predecessor in interest, if
461
+ the predecessor has it or can get it with reasonable efforts.
462
+
463
+ You may not impose any further restrictions on the exercise of the
464
+ rights granted or affirmed under this License. For example, you may
465
+ not impose a license fee, royalty, or other charge for exercise of
466
+ rights granted under this License, and you may not initiate litigation
467
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
468
+ any patent claim is infringed by making, using, selling, offering for
469
+ sale, or importing the Program or any portion of it.
470
+
471
+ 11. Patents.
472
+
473
+ A "contributor" is a copyright holder who authorizes use under this
474
+ License of the Program or a work on which the Program is based. The
475
+ work thus licensed is called the contributor's "contributor version".
476
+
477
+ A contributor's "essential patent claims" are all patent claims
478
+ owned or controlled by the contributor, whether already acquired or
479
+ hereafter acquired, that would be infringed by some manner, permitted
480
+ by this License, of making, using, or selling its contributor version,
481
+ but do not include claims that would be infringed only as a
482
+ consequence of further modification of the contributor version. For
483
+ purposes of this definition, "control" includes the right to grant
484
+ patent sublicenses in a manner consistent with the requirements of
485
+ this License.
486
+
487
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
488
+ patent license under the contributor's essential patent claims, to
489
+ make, use, sell, offer for sale, import and otherwise run, modify and
490
+ propagate the contents of its contributor version.
491
+
492
+ In the following three paragraphs, a "patent license" is any express
493
+ agreement or commitment, however denominated, not to enforce a patent
494
+ (such as an express permission to practice a patent or covenant not to
495
+ sue for patent infringement). To "grant" such a patent license to a
496
+ party means to make such an agreement or commitment not to enforce a
497
+ patent against the party.
498
+
499
+ If you convey a covered work, knowingly relying on a patent license,
500
+ and the Corresponding Source of the work is not available for anyone
501
+ to copy, free of charge and under the terms of this License, through a
502
+ publicly available network server or other readily accessible means,
503
+ then you must either (1) cause the Corresponding Source to be so
504
+ available, or (2) arrange to deprive yourself of the benefit of the
505
+ patent license for this particular work, or (3) arrange, in a manner
506
+ consistent with the requirements of this License, to extend the patent
507
+ license to downstream recipients. "Knowingly relying" means you have
508
+ actual knowledge that, but for the patent license, your conveying the
509
+ covered work in a country, or your recipient's use of the covered work
510
+ in a country, would infringe one or more identifiable patents in that
511
+ country that you have reason to believe are valid.
512
+
513
+ If, pursuant to or in connection with a single transaction or
514
+ arrangement, you convey, or propagate by procuring conveyance of, a
515
+ covered work, and grant a patent license to some of the parties
516
+ receiving the covered work authorizing them to use, propagate, modify
517
+ or convey a specific copy of the covered work, then the patent license
518
+ you grant is automatically extended to all recipients of the covered
519
+ work and works based on it.
520
+
521
+ A patent license is "discriminatory" if it does not include within
522
+ the scope of its coverage, prohibits the exercise of, or is
523
+ conditioned on the non-exercise of one or more of the rights that are
524
+ specifically granted under this License. You may not convey a covered
525
+ work if you are a party to an arrangement with a third party that is
526
+ in the business of distributing software, under which you make payment
527
+ to the third party based on the extent of your activity of conveying
528
+ the work, and under which the third party grants, to any of the
529
+ parties who would receive the covered work from you, a discriminatory
530
+ patent license (a) in connection with copies of the covered work
531
+ conveyed by you (or copies made from those copies), or (b) primarily
532
+ for and in connection with specific products or compilations that
533
+ contain the covered work, unless you entered into that arrangement,
534
+ or that patent license was granted, prior to 28 March 2007.
535
+
536
+ Nothing in this License shall be construed as excluding or limiting
537
+ any implied license or other defenses to infringement that may
538
+ otherwise be available to you under applicable patent law.
539
+
540
+ 12. No Surrender of Others' Freedom.
541
+
542
+ If conditions are imposed on you (whether by court order, agreement or
543
+ otherwise) that contradict the conditions of this License, they do not
544
+ excuse you from the conditions of this License. If you cannot convey a
545
+ covered work so as to satisfy simultaneously your obligations under this
546
+ License and any other pertinent obligations, then as a consequence you may
547
+ not convey it at all. For example, if you agree to terms that obligate you
548
+ to collect a royalty for further conveying from those to whom you convey
549
+ the Program, the only way you could satisfy both those terms and this
550
+ License would be to refrain entirely from conveying the Program.
551
+
552
+ 13. Use with the GNU Affero General Public License.
553
+
554
+ Notwithstanding any other provision of this License, you have
555
+ permission to link or combine any covered work with a work licensed
556
+ under version 3 of the GNU Affero General Public License into a single
557
+ combined work, and to convey the resulting work. The terms of this
558
+ License will continue to apply to the part which is the covered work,
559
+ but the special requirements of the GNU Affero General Public License,
560
+ section 13, concerning interaction through a network will apply to the
561
+ combination as such.
562
+
563
+ 14. Revised Versions of this License.
564
+
565
+ The Free Software Foundation may publish revised and/or new versions of
566
+ the GNU General Public License from time to time. Such new versions will
567
+ be similar in spirit to the present version, but may differ in detail to
568
+ address new problems or concerns.
569
+
570
+ Each version is given a distinguishing version number. If the
571
+ Program specifies that a certain numbered version of the GNU General
572
+ Public License "or any later version" applies to it, you have the
573
+ option of following the terms and conditions either of that numbered
574
+ version or of any later version published by the Free Software
575
+ Foundation. If the Program does not specify a version number of the
576
+ GNU General Public License, you may choose any version ever published
577
+ by the Free Software Foundation.
578
+
579
+ If the Program specifies that a proxy can decide which future
580
+ versions of the GNU General Public License can be used, that proxy's
581
+ public statement of acceptance of a version permanently authorizes you
582
+ to choose that version for the Program.
583
+
584
+ Later license versions may give you additional or different
585
+ permissions. However, no additional obligations are imposed on any
586
+ author or copyright holder as a result of your choosing to follow a
587
+ later version.
588
+
589
+ 15. Disclaimer of Warranty.
590
+
591
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599
+
600
+ 16. Limitation of Liability.
601
+
602
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610
+ SUCH DAMAGES.
611
+
612
+ 17. Interpretation of Sections 15 and 16.
613
+
614
+ If the disclaimer of warranty and limitation of liability provided
615
+ above cannot be given local legal effect according to their terms,
616
+ reviewing courts shall apply local law that most closely approximates
617
+ an absolute waiver of all civil liability in connection with the
618
+ Program, unless a warranty or assumption of liability accompanies a
619
+ copy of the Program in return for a fee.
620
+
621
+ END OF TERMS AND CONDITIONS
622
+
623
+ How to Apply These Terms to Your New Programs
624
+
625
+ If you develop a new program, and you want it to be of the greatest
626
+ possible use to the public, the best way to achieve this is to make it
627
+ free software which everyone can redistribute and change under these terms.
628
+
629
+ To do so, attach the following notices to the program. It is safest
630
+ to attach them to the start of each source file to most effectively
631
+ state the exclusion of warranty; and each file should have at least
632
+ the "copyright" line and a pointer to where the full notice is found.
633
+
634
+ <one line to give the program's name and a brief idea of what it does.>
635
+ Copyright (C) <year> <name of author>
636
+
637
+ This program is free software: you can redistribute it and/or modify
638
+ it under the terms of the GNU General Public License as published by
639
+ the Free Software Foundation, either version 3 of the License, or
640
+ (at your option) any later version.
641
+
642
+ This program is distributed in the hope that it will be useful,
643
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
644
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645
+ GNU General Public License for more details.
646
+
647
+ You should have received a copy of the GNU General Public License
648
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
649
+
650
+ Also add information on how to contact you by electronic and paper mail.
651
+
652
+ If the program does terminal interaction, make it output a short
653
+ notice like this when it starts in an interactive mode:
654
+
655
+ <program> Copyright (C) <year> <name of author>
656
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657
+ This is free software, and you are welcome to redistribute it
658
+ under certain conditions; type `show c' for details.
659
+
660
+ The hypothetical commands `show w' and `show c' should show the appropriate
661
+ parts of the General Public License. Of course, your program's commands
662
+ might be different; for a GUI interface, you would use an "about box".
663
+
664
+ You should also get your employer (if you work as a programmer) or school,
665
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
666
+ For more information on this, and how to apply and follow the GNU GPL, see
667
+ <https://www.gnu.org/licenses/>.
668
+
669
+ The GNU General Public License does not permit incorporating your program
670
+ into proprietary programs. If your program is a subroutine library, you
671
+ may consider it more useful to permit linking proprietary applications with
672
+ the library. If this is what you want to do, use the GNU Lesser General
673
+ Public License instead of this License. But first, please read
674
+ <https://www.gnu.org/licenses/why-not-lgpl.html>.
README.md CHANGED
@@ -1,13 +1,154 @@
1
- ---
2
- title: ViralCutterPRO
3
- emoji: 📊
4
- colorFrom: indigo
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 6.2.0
8
- app_file: app.py
9
- pinned: false
10
- license: gpl-3.0
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ViralCutter: Gerador de Vídeos Virais
2
+ [ ![](https://dcbadge.vercel.app/api/server/aihubbrasil) ](https://discord.gg/aihubbrasil) <br>
3
+ [English](https://github.com/RafaelGodoyEbert/ViralCutter/blob/main/README_en.md) | [Português](https://github.com/RafaelGodoyEbert/ViralCutter/blob/main/README.md)
4
+
5
+ ## **Descrição**
6
+ ViralCutter é uma ferramenta inovadora para gerar vídeos virais a partir de conteúdo existente. Com técnicas avançadas de processamento de vídeo e áudio, o ViralCutter corta e edita segmentos de vídeo que são perfeitos para compartilhamento em redes sociais. Utilizando o modelo WhisperX para transcrição e geração de legendas automáticas, ele adapta os vídeos para o formato 9:16 (vertical), ideal para plataformas como TikTok e Instagram com Reels e Youtube com Shorts.
7
+
8
+ ## **Novidades e Atualizações (Changelog)**
9
+
10
+ Confira as melhorias mais recentes:
11
+
12
+ - **Nova WebUI (Gradio)**: Interface gráfica moderna inspirada no OpusClip, facilitando o uso de todas as ferramentas.
13
+ - **Instalação Rápida (UV)**: Novo script `.bat` que utiliza o `uv` para instalar dependências de forma muito mais rápida.
14
+ - **Otimização de Performance**: "Slicing" de transcrição implementado. O vídeo é transcrito apenas uma vez, e os cortes reutilizam os dados, eliminando reprocessamento.
15
+ - **Suporte a IA Flexível**: Integração nativa com **Gemini API** e suporte experimental ao **G4F** (GPT-4 Free), além de modo Manual.
16
+ - **Configuração Externa**: Arquivos `api_config.json` e `prompt.txt` para fácil personalização sem mexer no código.
17
+ - **Correção de Rostos**: Fix no MediaPipe para rastreamento de rostos preciso sem depender de "Center Crop".
18
+ - **Melhorias em Legendas**: Posicionamento inteligente para vídeos com 2 faces (split screen) e correções de estilo.
19
+
20
+ *(Veja o [changelog.md](changelog.md) para detalhes completos)*
21
+
22
+ ## **Interface**
23
+ ![WebUi Home](https://github.com/user-attachments/assets/923f260d-222b-40cc-9ab6-7114d6cefd1c)
24
+ *Tela Principal: Ajustes de IA, legendas e visualização de logs*
25
+
26
+ ![WebUI Library](https://github.com/user-attachments/assets/3d9fd063-7e81-4e14-bba6-c66618a1635f)
27
+ *Biblioteca: Galeria estilo OpusClip e controles intuitivos*
28
+
29
+ ## **Funcionalidades**
30
+
31
+ - **Download de Vídeos**: Baixa vídeos do YouTube através de uma URL fornecida.
32
+ - **Transcrição de Áudio**: Converte áudio em texto utilizando o modelo WhisperX.
33
+ - **Identificação de Segmentos Virais**: Utiliza IA para detectar partes do vídeo com alto potencial de viralização.
34
+ - **Corte e Ajuste de Formato**: Corta os segmentos selecionados e ajusta a proporção para 9:16.
35
+ - **Recorte Inteligente**: Mantém o falante em foco (Face Tracking) ou utiliza modo Split Screen (2-Faces) automático.
36
+ - **Mesclagem de Áudio e Vídeo**: Combina o áudio transcrito com os clipes de vídeo processados.
37
+ - **Exportação em Lote**: Gera um arquivo ZIP com todos os vídeos virais criados, facilitando o download e compartilhamento.
38
+ - **Legenda personalizada**: Você cria uma legenda personalizada com cores, highlight, sem highlight ou palavra por palavra, tendo uma ampla possibilidade de edição.
39
+
40
+
41
+ ## **Como Usar**
42
+ <!--
43
+ Entre no link e siga os passos na ordem:<br> [![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/drive/1gcxImzBt0ObWLfW3ThEcwqKhasB4WpgX?usp=sharing)
44
+ HF [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)]()
45
+ -->
46
+
47
+ - Entre no link e siga os passos na ordem: [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1UZKzeqjIeEyvq9nPx7s_4mU6xlkZQn_R?usp=sharing#scrollTo=pa36OeArowme) <br>
48
+
49
+ ## **Limitações**
50
+
51
+ - A qualidade dos vídeos gerados pode variar com base na qualidade do vídeo original.
52
+ - O tempo de processamento depende fortemente da sua GPU.
53
+ - O modelo **G4F** pode ser instável ou ter limites de requisição. Use **Gemini** para maior estabilidade mas precisa de api_key.
54
+
55
+ ## Inspiração:
56
+ Este projeto foi inspirado nos seguintes repositórios:
57
+
58
+ * [Reels Clips Automator](https://github.com/eddieoz/reels-clips-automator)
59
+ * [YoutubeVideoToAIPoweredShorts](https://github.com/Fitsbit/YoutubeVideoToAIPoweredShorts)
60
+
61
+ ## TODO📝
62
+ - [x] Release code
63
+ - [ ] Huggingface SpaceDemo
64
+ - [x] Two face in the cut
65
+ - [x] Custom caption and burn
66
+ - [x] Make the code faster
67
+ - [ ] More types of framing beyond 9:16
68
+ - [x] The cut follows the face as it moves
69
+ - [ ] Automatic translation
70
+ - [ ] Satisfactory video on the side
71
+ - [ ] Background music
72
+ - [ ] watermark at user's choice
73
+ - [ ] Upload directly to YouTube channel
74
+
75
+ ## Exemplos
76
+ ### Exmplo de vídeo viral ``com highlight ativo`` [comprimido pra caber no github]
77
+ https://github.com/user-attachments/assets/dd9a7039-e0f3-427a-a6e1-f50ab5029082
78
+
79
+ ### Exemplo Opus Clip vs ViralCutter [comprimido pra caber no github]
80
+ https://github.com/user-attachments/assets/12916792-dc0e-4f63-a76b-5698946f50f4
81
+
82
+ ### Exemplo 2 faces [comprimido pra caber no github]
83
+ https://github.com/user-attachments/assets/ca7ebb9c-52ba-4171-a513-625bef690a2b
84
+
85
+ ## **Instalação e Uso Local**
86
+
87
+ ### Pré-requisitos
88
+ - Python 3.10+
89
+ - FFmpeg instalado e no PATH do sistema.
90
+ - GPU NVIDIA recomendada (com CUDA instalado) para o WhisperX.
91
+
92
+ ### Configuração
93
+ 1. **Instale as dependências**:
94
+
95
+ **Opção A (Recomendada - Rápida)**:
96
+ Execute o arquivo `install_dependencies.bat`. Ele usará o `uv` para instalar tudo rapidamente.
97
+
98
+ **Opção B (Manual)**:
99
+ ```bash
100
+ pip install -r requirements.txt
101
+ ```
102
+ *(Nota: WhisperX e Torch podem exigir instalação específica para sua versão de CUDA)*.
103
+
104
+ 2. **Configure a API (Opcional mas Recomendado)**:
105
+ Edite o arquivo `api_config.json` na pasta raiz:
106
+ ```json
107
+ {
108
+ "selected_api": "gemini",
109
+ "gemini": {
110
+ "api_key": "SUA_CHAVE_AQUI"
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### Executando
116
+
117
+ #### Interface Gráfica (WebUI)
118
+ Para usar a nova interface visual:
119
+ Dê um duplo clique em `run_webui.bat` ou execute:
120
+ ```bash
121
+ .\run_webui.bat
122
+ ```
123
+
124
+ #### Modo Interativo (Simples)
125
+ Basta rodar o script e seguir as instruções na tela:
126
+ ```bash
127
+ python main_improved.py
128
+ ```
129
+
130
+ #### Modo CLI (Avançado)
131
+ Você pode passar todos os argumentos via linha de comando para automação:
132
+
133
+ ```bash
134
+ python main_improved.py --url "https://youtu.be/EXEMPLO" --segments 3 --ai-backend gemini --model large-v3-turbo
135
+ ```
136
+
137
+ **Argumentos Principais:**
138
+ - `--url`: URL do vídeo do YouTube.
139
+ - `--segments`: Número de cortes a gerar.
140
+ - `--ai-backend`: `gemini` (Recomendado), `g4f` ou `manual`.
141
+ - `--viral`: Ativa modo de busca viral automática.
142
+ - `--face-mode`: `auto`, `1` (uma cara), ou `2` (duas caras/split).
143
+ - `--workflow`: `1` (Completo) ou `2` (Apenas Corte, sem legendas/crop).
144
+
145
+ ---
146
+
147
+ ## **Contribuições**
148
+ Quer ajudar a tornar o ViralCutter ainda melhor? Se você tiver sugestões ou quiser contribuir com o código, fique à vontade para abrir uma issue ou enviar um pull request no nosso repositório do GitHub.
149
+
150
+ ## **Versão**
151
+ `0.7v Alpha`
152
+ Uma alternativa gratuita ao `opus.pro` e ao `vidyo.ai`.
153
+
154
+ ---
README_en.md ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ViralCutter: Viral Video Generator
2
+ [ ![](https://dcbadge.vercel.app/api/server/aihubbrasil) ](https://discord.gg/aihubbrasil) <br>
3
+ [English](https://github.com/RafaelGodoyEbert/ViralCutter/blob/main/README_en.md) | [Português](https://github.com/RafaelGodoyEbert/ViralCutter/blob/main/README.md)
4
+
5
+ ## **Description**
6
+ ViralCutter is an innovative tool designed to generate viral videos from existing content. With advanced video and audio processing techniques, ViralCutter cuts and edits video segments that are perfect for sharing on social media. Using the WhisperX model for transcription and automatic caption generation, it adapts videos to the 9:16 (vertical) format, ideal for platforms like TikTok, Instagram Reels, and YouTube Shorts.
7
+
8
+ ## **What's New & Updates (Changelog)**
9
+
10
+ Check out the latest improvements:
11
+
12
+ - **New WebUI (Gradio)**: Modern graphical interface inspired by OpusClip, making it easier to use all tools.
13
+ - **Fast Installation (UV)**: New `.bat` script that uses `uv` to install dependencies much faster.
14
+ - **Performance Optimization**: Transcription "slicing" implemented. The video is transcribed only once, and cuts reuse the data, eliminating reprocessing.
15
+ - **Flexible AI Support**: Native integration with **Gemini API** and experimental support for **G4F** (GPT-4 Free), plus a Manual mode.
16
+ - **External Configuration**: `api_config.json` and `prompt.txt` files for easy customization without touching the code.
17
+ - **Face Fix**: MediaPipe fix for precise face tracking without relying on "Center Crop".
18
+ - **Subtitle Improvements**: Smart positioning for 2-face videos (split screen) and style corrections.
19
+
20
+ *(See [changelog.md](changelog.md) for full details)*
21
+
22
+ ## **Interface**
23
+ ![WebUi Home](https://github.com/user-attachments/assets/923f260d-222b-40cc-9ab6-7114d6cefd1c)
24
+ *Main Screen: OpusClip-style gallery and intuitive controls*
25
+
26
+ ![WebUI Library](https://github.com/user-attachments/assets/3d9fd063-7e81-4e14-bba6-c66618a1635f)
27
+ *Settings: AI adjustment, captions, and log viewer*
28
+
29
+ ## **Features**
30
+
31
+ - **Video Download**: Downloads YouTube videos via a provided URL.
32
+ - **Audio Transcription**: Converts audio to text using the WhisperX model.
33
+ - **Viral Segment Identification**: Uses AI to detect parts of the video with high viral potential.
34
+ - **Cutting & Formatting**: Cuts selected segments and adjusts the aspect ratio to 9:16.
35
+ - **Smart Cropping**: Keeps the speaker in focus (Face Tracking) or uses automatic Split Screen (2-Faces) mode.
36
+ - **Audio/Video Merging**: Combines transcribed audio with processed video clips.
37
+ - **Batch Export**: Generates a ZIP file with all created viral videos, facilitating download and sharing.
38
+ - **Custom Captions**: Create custom captions with colors, highlights, no highlights, or word-by-word styles, offering extensive editing possibilities.
39
+
40
+
41
+ ## **How to Use**
42
+ <!--
43
+ Open the link and follow the steps in order:<br> [![Open In Colab](https://img.shields.io/badge/Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/drive/1gcxImzBt0ObWLfW3ThEcwqKhasB4WpgX?usp=sharing)
44
+ HF [![Hugging Face Spaces](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue)]()
45
+ -->
46
+
47
+ - Open the link and follow the steps in order(Only Portuguese, sorry): [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1UZKzeqjIeEyvq9nPx7s_4mU6xlkZQn_R?usp=sharing#scrollTo=pa36OeArowme) <br>
48
+ - I couldn't get the video to download/play correctly on Gradio in Google Colab, but it's functional.
49
+
50
+ ## **Limitations**
51
+
52
+ - The quality of generated videos may vary based on the quality of the original video.
53
+ - Processing time depends heavily on your GPU.
54
+ - The **G4F** model may be unstable or have request limits. Use **Gemini** for greater stability (requires an api_key).
55
+
56
+ ## Inspiration
57
+ This project was inspired by the following repositories:
58
+
59
+ * [Reels Clips Automator](https://github.com/eddieoz/reels-clips-automator)
60
+ * [YoutubeVideoToAIPoweredShorts](https://github.com/Fitsbit/YoutubeVideoToAIPoweredShorts)
61
+
62
+ ## TODO📝
63
+ - [x] Release code
64
+ - [ ] Huggingface SpaceDemo
65
+ - [x] Two face in the cut
66
+ - [x] Custom caption and burn
67
+ - [x] Make the code faster
68
+ - [ ] More types of framing beyond 9:16
69
+ - [x] The cut follows the face as it moves
70
+ - [ ] Automatic translation
71
+ - [ ] Satisfying video on the side
72
+ - [ ] Background music
73
+ - [ ] Watermark at user's choice
74
+ - [ ] Upload directly to YouTube channel
75
+
76
+ ## Examples
77
+ ### Viral video example `with active highlight` [compressed to fit GitHub]
78
+ https://github.com/user-attachments/assets/dd9a7039-e0f3-427a-a6e1-f50ab5029082
79
+
80
+ ### Opus Clip vs ViralCutter example [compressed to fit GitHub]
81
+ https://github.com/user-attachments/assets/12916792-dc0e-4f63-a76b-5698946f50f4
82
+
83
+ ### 2-Face example [compressed to fit GitHub]
84
+ https://github.com/user-attachments/assets/ca7ebb9c-52ba-4171-a513-625bef690a2b
85
+
86
+ ## **Installation and Local Usage**
87
+
88
+ ### Prerequisites
89
+ - Python 3.10+
90
+ - FFmpeg installed and in the system PATH.
91
+ - NVIDIA GPU recommended (with CUDA installed) for WhisperX.
92
+
93
+ ### Configuration
94
+ 1. **Install dependencies**:
95
+
96
+ **Option A (Recommended - Fast)**:
97
+ Run the `install_dependencies.bat` file. It will use `uv` to install everything quickly.
98
+
99
+ **Option B (Manual)**:
100
+ ```bash
101
+ pip install -r requirements.txt
102
+ ```
103
+ *(Note: WhisperX and Torch may require specific installation instructions for your CUDA version)*.
104
+
105
+ 2. **Configure API (Optional but Recommended)**:
106
+ Edit the `api_config.json` file in the root folder:
107
+ ```json
108
+ {
109
+ "selected_api": "gemini",
110
+ "gemini": {
111
+ "api_key": "YOUR_KEY_HERE"
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### Running
117
+
118
+ #### Graphical Interface (WebUI)
119
+ To use the new visual interface:
120
+ Double-click `run_webui.bat` or run:
121
+ ```bash
122
+ .\run_webui.bat
123
+ ```
124
+
125
+ #### Interactive Mode (Simple)
126
+ Just run the script and follow the on-screen instructions:
127
+ ```bash
128
+ python main_improved.py
129
+ ```
130
+
131
+ #### CLI Mode (Advanced)
132
+ You can pass all arguments via command line for automation:
133
+
134
+ ```bash
135
+ python main_improved.py --url "https://youtu.be/EXAMPLE" --segments 3 --ai-backend gemini --model large-v3-turbo
136
+ ```
137
+
138
+ **Main Arguments:**
139
+ - `--url`: YouTube video URL.
140
+ - `--segments`: Number of cuts to generate.
141
+ - `--ai-backend`: `gemini` (Recommended), `g4f`, or `manual`.
142
+ - `--viral`: Activates automatic viral search mode.
143
+ - `--face-mode`: `auto`, `1` (one face), or `2` (two faces/split).
144
+ - `--workflow`: `1` (Full) or `2` (Cut Only, no captions/crop).
145
+
146
+ ---
147
+
148
+ ## **Contributions**
149
+ Want to help make ViralCutter even better? If you have suggestions or want to contribute to the code, feel free to open an issue or submit a pull request on our GitHub repository.
150
+
151
+ ## **Version**
152
+ `0.7v Alpha`
153
+ A free alternative to `opus.pro` and `vidyo.ai`.
154
+
155
+ ---
ViralCutter.ipynb ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "source": [
6
+ "# ViralCutter\n",
7
+ "Uma alternativa gratuita ao `opus.pro` e ao `vidyo.ai`"
8
+ ],
9
+ "metadata": {
10
+ "id": "pa36OeArowme"
11
+ }
12
+ },
13
+ {
14
+ "cell_type": "markdown",
15
+ "source": [
16
+ "# Suporte em:\n",
17
+ "[![](https://dcbadge.limes.pink/api/server/tAdPHFAbud)](https://discord.gg/tAdPHFAbud)"
18
+ ],
19
+ "metadata": {
20
+ "id": "6Q-ljfsw1unE"
21
+ }
22
+ },
23
+ {
24
+ "cell_type": "markdown",
25
+ "source": [
26
+ "# TODO📝\n",
27
+ "- [x] Release code\n",
28
+ "- [ ] Huggingface SpaceDemo\n",
29
+ "- [x] Two face in the cut\n",
30
+ "- [x] Custom caption and burn\n",
31
+ "- [x] Make the code faster\n",
32
+ "- [ ] More types of framing beyond 9:16"
33
+ ],
34
+ "metadata": {
35
+ "id": "0o3KcBT5wzws"
36
+ }
37
+ },
38
+ {
39
+ "cell_type": "code",
40
+ "execution_count": 1,
41
+ "metadata": {
42
+ "id": "e76jiRnjONmj",
43
+ "cellView": "form",
44
+ "colab": {
45
+ "base_uri": "https://localhost:8080/"
46
+ },
47
+ "outputId": "6e8baa39-5e40-45d7-e247-a70eddbc1a79"
48
+ },
49
+ "outputs": [
50
+ {
51
+ "output_type": "stream",
52
+ "name": "stdout",
53
+ "text": [
54
+ "✅ Instalação V7 Finalizada!\n",
55
+ "- Transformers 4.46.3 (Compatível com Alinhamento): INSTALADO\n",
56
+ "- Torch 2.3.1: ATIVO\n"
57
+ ]
58
+ }
59
+ ],
60
+ "source": [
61
+ "#@title 🛠️ Instalação\n",
62
+ "import os\n",
63
+ "import subprocess\n",
64
+ "import shutil\n",
65
+ "from IPython.display import clear_output\n",
66
+ "\n",
67
+ "# 1. Limpeza TOTAL\n",
68
+ "print(\"🧹 Limpando instalação anterior...\")\n",
69
+ "%cd /content\n",
70
+ "if os.path.exists(\"ViralCutter\"):\n",
71
+ " shutil.rmtree(\"ViralCutter\")\n",
72
+ "\n",
73
+ "!git clone -b dev https://github.com/RafaelGodoyEbert/ViralCutter.git\n",
74
+ "%cd /content/ViralCutter\n",
75
+ "\n",
76
+ "print(\"⏳ Instalando gerenciador UV e drivers do sistema...\")\n",
77
+ "\n",
78
+ "# 2. Instalar UV e Drivers Linux\n",
79
+ "subprocess.run(['pip', 'install', 'uv'], check=True)\n",
80
+ "subprocess.run('sudo apt update -y && sudo apt install -y libcudnn8 ffmpeg xvfb', shell=True, check=True)\n",
81
+ "\n",
82
+ "# 3. Criar Ambiente Virtual\n",
83
+ "print(\"⏳ Criando ambiente virtual...\")\n",
84
+ "subprocess.run(['uv', 'venv', '.venv'], check=True)\n",
85
+ "\n",
86
+ "# 4. INSTALAÇÃO DAS DEPENDÊNCIAS\n",
87
+ "print(\"⏳ Instalando Bibliotecas...\")\n",
88
+ "\n",
89
+ "# Passo A: WhisperX e Requisitos Básicos (Deixe instalar o que quiserem)\n",
90
+ "cmds_fase_1 = [\n",
91
+ " \"uv pip install --python .venv git+https://github.com/m-bain/whisperx.git\",\n",
92
+ " \"uv pip install --python .venv -r requirements.txt\",\n",
93
+ " \"uv pip install --python .venv yt-dlp pytubefix\",\n",
94
+ " \"uv pip install yt_dlp\"\n",
95
+ "]\n",
96
+ "\n",
97
+ "for cmd in cmds_fase_1:\n",
98
+ " subprocess.run(cmd, shell=True, check=True)\n",
99
+ "\n",
100
+ "# Passo B: CORREÇÃO DE ALINHAMENTO E GEMINI\n",
101
+ "# - google-generativeai: Para o Gemini funcionar\n",
102
+ "# - pandas: Para separar palavras\n",
103
+ "# - transformers==4.46.3: VERSÃO CRÍTICA. Versões mais novas exigem Torch 2.6 e quebram o alinhamento.\n",
104
+ "# - accelerate: Ajuda no carregamento do modelo\n",
105
+ "print(\"🔨 Aplicando downgrade estratégico no Transformers...\")\n",
106
+ "extra_libs = [\n",
107
+ " \"uv pip install --python .venv google-generativeai\",\n",
108
+ " \"uv pip install --python .venv pandas\",\n",
109
+ " \"uv pip install --python .venv onnxruntime-gpu\",\n",
110
+ " \"uv pip install --python .venv transformers==4.46.3 accelerate>=0.26.0\"\n",
111
+ "]\n",
112
+ "\n",
113
+ "for cmd in extra_libs:\n",
114
+ " subprocess.run(cmd, shell=True, check=True)\n",
115
+ "\n",
116
+ "# Passo C: O MARTELO FINAL (Torch 2.3.1 Estável)\n",
117
+ "# Reinstalamos por último para garantir que nada atualizou ele sem querer\n",
118
+ "print(\"🔨 Forçando versão estável do Torch (2.3.1)...\")\n",
119
+ "cmd_fix_torch = (\n",
120
+ " \"uv pip install --python .venv \"\n",
121
+ " \"torch==2.3.1+cu121 torchvision==0.18.1+cu121 torchaudio==2.3.1+cu121 \"\n",
122
+ " \"--index-url https://download.pytorch.org/whl/cu121\"\n",
123
+ ")\n",
124
+ "subprocess.run(cmd_fix_torch, shell=True, check=True)\n",
125
+ "\n",
126
+ "# Passo D: Trava do Numpy\n",
127
+ "print(\"🔨 Travando Numpy...\")\n",
128
+ "subprocess.run(\"uv pip install --python .venv 'numpy<2.0' setuptools==69.5.1\", shell=True, check=True)\n",
129
+ "\n",
130
+ "# Passo E: Correção de Visão Computacional (CRÍTICO)\n",
131
+ "print(\"🔨 Corrigindo InsightFace e MediaPipe...\")\n",
132
+ "\n",
133
+ "# 1. Garante que o InsightFace tenha o motor necessário (evita que ele falhe)\n",
134
+ "subprocess.run(\"uv pip install --python .venv insightface onnxruntime-gpu\", shell=True, check=True)\n",
135
+ "\n",
136
+ "# 2. Limpeza Radical e Reinstalação do MediaPipe\n",
137
+ "# Remove versões quebradas\n",
138
+ "subprocess.run(\"uv pip uninstall --python .venv mediapipe protobuf flatbuffers\", shell=True)\n",
139
+ "\n",
140
+ "# 3. Instala versão FLEXÍVEL respeitando limites críticos\n",
141
+ "# mediapipe>=0.10.0: Pega a versão mais recente compatível com Py3.12\n",
142
+ "# protobuf>=3.20,<5.0: Garante compatibilidade sem quebrar o sistema antigo\n",
143
+ "subprocess.run(\"uv pip install --python .venv 'mediapipe>=0.10.0' 'protobuf>=3.20,<5.0' 'flatbuffers>=2.0'\", shell=True, check=True)\n",
144
+ "\n",
145
+ "# 5. Configurar Monitor\n",
146
+ "os.system('Xvfb :1 -screen 0 2560x1440x8 &')\n",
147
+ "os.environ['DISPLAY'] = ':1.0'\n",
148
+ "\n",
149
+ "clear_output()\n",
150
+ "print(\"✅ Instalação V7 Finalizada!\")\n",
151
+ "print(\"- Transformers 4.46.3 (Compatível com Alinhamento): INSTALADO\")\n",
152
+ "print(\"- Torch 2.3.1: ATIVO\")"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "code",
157
+ "source": [
158
+ "#@title 🚀 Configuração e Execução\n",
159
+ "import os\n",
160
+ "\n",
161
+ "%cd /content/ViralCutter\n",
162
+ "\n",
163
+ "# 1. Configura o \"Display\" falso (para evitar erros de interface gráfica)\n",
164
+ "os.system('Xvfb :1 -screen 0 2560x1440x8 &')\n",
165
+ "os.environ['DISPLAY'] = ':1.0'\n",
166
+ "\n",
167
+ "# 2. CORREÇÃO DO ERRO ATUAL:\n",
168
+ "# Forçamos o Matplotlib a usar o backend 'Agg' (modo sem cabeça/headless)\n",
169
+ "# Isso ignora o 'module://matplotlib_inline.backend_inline' que o Colab força e quebra o script.\n",
170
+ "os.environ['MPLBACKEND'] = 'Agg'\n",
171
+ "\n",
172
+ "print(\"🚀 Iniciando ViralCutter usando o ambiente virtual...\")\n",
173
+ "print(\"⚠️ Ignore os avisos de 'UserWarning', aguarde o link public URL.\")\n",
174
+ "\n",
175
+ "# Executa usando o Python do ambiente virtual\n",
176
+ "!/content/ViralCutter/.venv/bin/python webui/app.py --colab"
177
+ ],
178
+ "metadata": {
179
+ "id": "DNn8ZKJdD5XG",
180
+ "colab": {
181
+ "base_uri": "https://localhost:8080/"
182
+ },
183
+ "outputId": "fd4689bd-f036-42de-fc7e-67854af40d81"
184
+ },
185
+ "execution_count": null,
186
+ "outputs": [
187
+ {
188
+ "output_type": "stream",
189
+ "name": "stdout",
190
+ "text": [
191
+ "/content/ViralCutter\n",
192
+ "🚀 Iniciando ViralCutter usando o ambiente virtual...\n",
193
+ "⚠️ Ignore os avisos de 'UserWarning', aguarde o link public URL.\n",
194
+ "/content/ViralCutter/webui/app.py:204: UserWarning: The parameters have been moved from the Blocks constructor to the launch() method in Gradio 6.0: theme, css. Please pass these parameters to launch() instead.\n",
195
+ " with gr.Blocks(title=i18n(\"ViralCutter WebUI\"), theme=gr.themes.Default(primary_hue=\"blue\", neutral_hue=\"slate\"), css=css) as demo:\n",
196
+ "Running in Colab mode. Generating public link...\n",
197
+ "* Running on local URL: http://127.0.0.1:7860\n",
198
+ "* Running on public URL: https://7f3b04ec670707105b.gradio.live\n",
199
+ "\n",
200
+ "This share link expires in 1 week. For free permanent hosting and GPU upgrades, run `gradio deploy` from the terminal in the working directory to deploy to Hugging Face Spaces (https://huggingface.co/spaces)\n"
201
+ ]
202
+ }
203
+ ]
204
+ },
205
+ {
206
+ "cell_type": "markdown",
207
+ "metadata": {
208
+ "id": "nEIsMZLwJ5kD"
209
+ },
210
+ "source": [
211
+ "#Créditos\n",
212
+ "\n",
213
+ "Inspirado no [reels clips automator](https://github.com/eddieoz/reels-clips-automator) e no [YoutubeVideoToAIPoweredShorts](https://github.com/Fitsbit/YoutubeVideoToAIPoweredShorts)<br>\n",
214
+ "\n",
215
+ "---\n",
216
+ "![Rafa.png](https://i.imgur.com/cGknQpU.png;base64)\n",
217
+ "\n",
218
+ "Desenvolvido por **Rafa.Godoy**<br>\n",
219
+ "[ ![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white) ](https://github.com/rafaelGodoyEbert)<br>\n",
220
+ "[ ![X](https://img.shields.io/twitter/url?url=https%3A%2F%2Ftwitter.com%2FGodoyEbert) ](https://twitter.com/GodoyEbert)<br>\n",
221
+ "[Instagram](https://www.instagram.com/rafael.godoy.ebert/)<br>\n",
222
+ "[ ![](https://dcbadge.vercel.app/api/server/aihubbrasil) ](https://discord.gg/aihubbrasil)"
223
+ ]
224
+ },
225
+ {
226
+ "cell_type": "markdown",
227
+ "source": [
228
+ "`0.1v Alpha`<br>\n",
229
+ "\n",
230
+ "Apenas uma alternativa gratuita ao `opus.pro` e ao `vidyo.ai`<br>\n"
231
+ ],
232
+ "metadata": {
233
+ "id": "tpLJmPqGT5_u"
234
+ }
235
+ }
236
+ ],
237
+ "metadata": {
238
+ "accelerator": "GPU",
239
+ "colab": {
240
+ "gpuType": "T4",
241
+ "provenance": []
242
+ },
243
+ "kernelspec": {
244
+ "display_name": "Python 3",
245
+ "name": "python3"
246
+ },
247
+ "language_info": {
248
+ "name": "python"
249
+ }
250
+ },
251
+ "nbformat": 4,
252
+ "nbformat_minor": 0
253
+ }
abrir_cmd.bat ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+
3
+ REM Vai para a pasta onde está o .bat
4
+ cd /d "%~dp0"
5
+
6
+ REM Ativa o ambiente virtual criado pelo uv
7
+ if exist ".venv\Scripts\activate.bat" (
8
+ call ".venv\Scripts\activate.bat"
9
+ echo Ambiente virtual .venv ativado.
10
+ ) else (
11
+ echo AVISO: Ambiente virtual .venv nao encontrado.
12
+ echo Execute install_dependencies.bat primeiro.
13
+ )
14
+
15
+ REM Abre um CMD interativo e mantém aberto
16
+ cmd /k
api_config.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "selected_api": "gemini",
3
+ "gemini": {
4
+ "api_key": "",
5
+ "model": "gemini-2.5-flash-lite-preview-09-2025",
6
+ "chunk_size": 20000
7
+ },
8
+ "g4f": {
9
+ "model": "gpt-4o-mini",
10
+ "chunk_size": 2000
11
+ }
12
+ }
changelog.md ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Changelog
2
+
3
+ ## Suporte a GGUF e Ajustes de Link
4
+
5
+ ### Novidades
6
+ - **Suporte a GGUF**: colocado suporte a gguf para llm local.
7
+ - **Link Público**: ajustado diretórios de link público.
8
+
9
+ ## Melhorias de Qualidade de Vídeo, Legendas e Processamento
10
+
11
+ ### Novidades
12
+
13
+ - **Aprimoramento de prompt para LLM**: melhorias no prompt para permitir que o modelo de linguagem compreenda melhor o contexto do conteúdo.
14
+ - **Aprimoramento na detecção facial**: melhorias na identificação de rostos quando várias pessoas estão falando simultaneamente.
15
+ - **Seleção de Qualidade de Vídeo**: agora é possível escolher a qualidade desejada para download de vídeos (Melhor, 1080p, 720p, 480p) diretamente pela WebUI ou CLI, permitindo otimizar entre velocidade e uso de armazenamento.
16
+ - **Controle de Legendas do YouTube**: adicionada a opção de ignorar o download de legendas oficiais do YouTube, permitindo forçar uma nova transcrição via Whisper, se desejado.
17
+ - **Suporte a VTT**: o script de transcrição foi aprimorado para oferecer suporte a arquivos de legenda `.vtt` para alinhamento, garantindo maior compatibilidade.
18
+ - **Tradução de legendas em JSON com destaque palavra por palavra**: adicionada a tradução de legendas no formato JSON, permitindo highlight e sincronização word-by-word em outro idioma durante a exibição.
19
+
20
+ ### Melhorias e Otimizações
21
+
22
+ - **yt-dlp mais robusto**: corrigidos problemas em que downloads de vídeo estavam sendo salvos como “Unknown_Video” e exibiam progresso incorreto. Também foram adicionados logs de progresso mais precisos e suporte aprimorado ao download de legendas.
23
+ - **Otimização de Legendas do YouTube**: quando legendas do YouTube estão disponíveis, o sistema agora faz o download automático e as utiliza apenas para alinhamento, pulando o processo pesado e demorado de transcrição. Isso acelera significativamente o processamento de vídeos que já possuem legendas.
24
+
25
+
26
+ ## Active Speaker & Face Controls
27
+
28
+ ### Controles Avançados de Face e Falante Ativo
29
+ - **Filtros de Face**: Controle granular para ignorar rostos pequenos, definir limite de confiança minimiza falsos positivos e "Zona Morta" para estabilizar a câmera.
30
+ - **Experimental: Active Speaker**: Novo modo experimental que tenta focar na pessoa que está falando (detecção de boca aberta e movimento), em vez de sempre dividir a tela.
31
+ - **Legendas**: Opção para remover pontuação automaticamente.
32
+
33
+ ## Editor de Legenda JSON
34
+
35
+ ### Funcionalidades
36
+ - **Editor de Legendas**: Adicionado um editor de legendas simples, dentro das limitações do Gradio, para corrigir erros de ortografia ocorridos durante o uso do WhisperX.
37
+
38
+ ### Correções
39
+ - **Geral**: Alguns Fix Colab e melhorias na geração de viral segments.
40
+
41
+ ## Gradio WebUI & UV Installation
42
+
43
+ ### Nova Interface Web (Gradio)
44
+ - **OpusClip Inspired**: Nova interface gráfica construída com Gradio, inspirada no design do OpusClip, oferecendo uma experiência de usuário moderna e intuitiva.
45
+ - **Funcionalidades da UI**: Ajustes completos para garantir que todas as funcionalidades da ferramenta estejam acessíveis e operantes através da nova interface.
46
+
47
+ ### Instalação e Infraestrutura
48
+ - **Instalação via UV**: Criação de script `.bat` para instalação otimizada de dependências utilizando o `uv`, acelerando o processo de setup.
49
+ - **Fixes Gerais**: Correções em diversos componentes que estavam quebrados ou instáveis, garantindo maior estabilidade na execução via UI.
50
+
51
+ ## WebUI 2.0 & Enhanced Configuration
52
+
53
+ ### WebUI Overhaul
54
+ - **Dark & Modern UI**: Interface completamente redesenhada com tema escuro e layout em grid responsivo (estilo Opus.pro) para a galeria de vídeos.
55
+ - **Dynamic Configuration**: Componentes da interface agora reagem dinamicamente à escolha do Backend de IA, atualizando automaticamente a lista de modelos disponíveis e o tamanho sugerido de chunk.
56
+ - **Improved Controls**: Controle granular sobre `Face Detect Interval`, `Skip Prompts`, e `Chunk Size` diretamente na interface web.
57
+ - **Refactoring**: Código da WebUI refatorado e modularizado (`library.py` separado do `app.py`) para melhor manutenção.
58
+
59
+ ### Core & CLI
60
+ - **Arguments Expansion**: `main_improved.py` agora aceita argumentos de linha de comando para `--chunk-size` e `--ai-model-name`, permitindo override total da configuração.
61
+ - **Script Update**: `create_viral_segments.py` atualizado para respeitar os parâmetros passados via CLI, priorizando-os sobre o arquivo de configuração.
62
+
63
+ ## Fix 2 faces
64
+
65
+ ### Melhorias na Detecção Facial e Layout
66
+ - **Consistência Visual (2 Faces)**: Implementada lógica para "travar" a identidade dos rostos nas posições superior e inferior, impedindo que os participantes troquem de lugar durante o vídeo.
67
+ - **Lógica de Fallback Inteligente**: Caso o rosto não seja detectado no frame atual, o sistema agora tenta recuperar a posição baseada no frame anterior, posterior ou na última coordenada válida conhecida.
68
+ - **Intervalo de Detecção Personalizável**: Adicionada configuração para o usuário escolher a frequência da varredura facial, permitindo otimizar o tempo de renderização.
69
+
70
+ ### Correções de Legendas
71
+ - **Correção de Sobreposição**: Resolvido bug onde legendas apareciam sobrepostas em momentos de fala rápida.
72
+ - **Refinamento de Centralização (2 Faces)**: Ajustes adicionais no cálculo de posição para garantir que a legenda fique perfeitamente centralizada no modo dividido.
73
+
74
+ ## Atualizações Anteriores
75
+
76
+ ### Refatoração e Melhorias de Código
77
+ - **Refatoração do Script Principal**: Criação e aprimoramento do `main_improved.py` para melhorar a estrutura e manutenibilidade do pipeline de processamento.
78
+ - **Padronização de Código (Inglês)**: Tradução completa de nomes de variáveis, funções e comentários internos para inglês, visando compatibilidade com padrões internacionais e colaboração open-source, mantendo logs de saída com suporte a i18n (`en_US`/`pt_BR`).
79
+ - **Ajuste de Diretórios**: Reorganização da estrutura de pastas e caminhos de saída para maior organização dos arquivos gerados.
80
+
81
+ ### Configuração e IA
82
+ - **Integração Multi-LLM**: Implementação de suporte ao **g4f** (GPT-4 Free) e **Google Gemini**.
83
+ - **API Config**: Centralização das chaves e seleção de modelos no novo arquivo `api_config.json`, permitindo troca rápida de provedor de IA sem alterar o código.
84
+ - **Gerenciamento de Prompts**: Criação do arquivo `prompt.txt` para edição fácil do prompt do sistema.
85
+
86
+ ### Legendas e Transcrição (Whisper)
87
+ - **Correções no Whisper**: Solução robusta para erros de `unpickling`, conflitos de DLLs (`libprotobuf`, `torchaudio`) e detecção de GPU.
88
+ - **Otimização do Fluxo (Slicing)**: O vídeo original é transcrito apenas uma vez. Os cortes reutilizam o JSON original, eliminando a re-transcrição e acelerando o processo.
89
+ - **Posicionamento de Legendas**: Correção da lógica de alinhamento para centralização no modo "2-face".
90
+
91
+ ### Processamento de Vídeo e Detecção Facial
92
+ - **Novo Motor: InsightFace**: Adição da biblioteca `InsightFace` como motor de detecção facial de alta precisão.
93
+ - **MediaPipe**: Manutenção e correção de erros no fallback para o MediaPipe.
94
+ - **Limpeza de Logs**: Redução da verbosidade dos logs do FFmpeg no console.
check_landmarks.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import sys
3
+ import os
4
+
5
+ # Adjust path to import scripts
6
+ sys.path.append(os.getcwd())
7
+
8
+ try:
9
+ from scripts.face_detection_insightface import init_insightface, suppress_stdout_stderr
10
+
11
+ app = init_insightface()
12
+
13
+ # Create a dummy frame (black) or try to read a real one if possible
14
+ # We just want to check the attributes of the returned face object
15
+ dummy = cv2.imread("C:/Users/rafam/.gemini/antigravity/brain/9bfedba9-72ff-4a40-a0d3-b77ea033233e/uploaded_image_1766002859958.png") # User uploaded image path from metadata?
16
+ # Actually I don't have access to the user's uploaded image path on disk straightforwardly unless I guess, but I can use a black frame.
17
+ # Wait, the user has processed videos in VIRALS folder.
18
+
19
+ # Let's just use a black frame 640x640 containing a drawn white face square to trigger detection? Unlikely to work with InsightFace (needs features).
20
+ # I will try to read one frame from a video in the workspace if exists.
21
+
22
+ video_path = "c:\\Users\\rafam\\Downloads\\ViralCutter\\output000_original_scale.mp4" # Guessing from edit_video
23
+ # Or search for mp4
24
+
25
+ import glob
26
+ videos = glob.glob("c:\\Users\\rafam\\Downloads\\ViralCutter\\**\\*.mp4", recursive=True)
27
+ if videos:
28
+ cap = cv2.VideoCapture(videos[0])
29
+ ret, frame = cap.read()
30
+ cap.release()
31
+
32
+ if ret:
33
+ faces = app.get(frame)
34
+ if faces:
35
+ print("Face Keys:", faces[0].keys())
36
+ if hasattr(faces[0], 'landmark_2d_106'):
37
+ print("Has 106 landmarks:", faces[0].landmark_2d_106 is not None)
38
+ if hasattr(faces[0], 'landmark_3d_68'):
39
+ print("Has 68 landmarks:", faces[0].landmark_3d_68 is not None)
40
+ print("Has kps:", faces[0].kps is not None)
41
+ else:
42
+ print("No faces found in sample frame.")
43
+ else:
44
+ print("Could not read frame.")
45
+ else:
46
+ print("No videos found to test.")
47
+
48
+ except Exception as e:
49
+ print(f"Error: {e}")
i18n/__pycache__/i18n.cpython-310.pyc ADDED
Binary file (1.2 kB). View file
 
i18n/__pycache__/i18n.cpython-311.pyc ADDED
Binary file (1.87 kB). View file
 
i18n/i18n.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import locale
3
+ import os
4
+
5
+
6
+ def load_language_list(language):
7
+ with open(f"./i18n/locale/{language}.json", "r", encoding="utf-8") as f:
8
+ language_list = json.load(f)
9
+ return language_list
10
+
11
+
12
+ class I18nAuto:
13
+ def __init__(self, language=None):
14
+ if language in ["Auto", None]:
15
+ language = locale.getdefaultlocale()[
16
+ 0
17
+ ] # getlocale can't identify the system's language ((None, None))
18
+ if not os.path.exists(f"./i18n/locale/{language}.json"):
19
+ language = "en_US"
20
+ self.language = language
21
+ self.language_map = load_language_list(language)
22
+
23
+ def __call__(self, key):
24
+ return self.language_map.get(key, key)
25
+
26
+ def __repr__(self):
27
+ return "Use Language: " + self.language
i18n/locale/en_US.json ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "main": "main",
3
+ "Burn only mode activated. Skipping to subtitle burning...": "Burn only mode activated. Skipping to subtitle burning...",
4
+ "Subtitle burning completed.": "Subtitle burning completed.",
5
+ "Enter the YouTube video URL (or press Enter to use latest project): ": "Enter the YouTube video URL (or press Enter to use latest project): ",
6
+ "Enter the number of viral segments to create: ": "Enter the number of viral segments to create: ",
7
+ "\nError: Number must be greater than 0.": "\nError: Number must be greater than 0.",
8
+ "Error: The value you entered is not an integer. Please try again.": "Error: The value you entered is not an integer. Please try again.",
9
+ "Do you want viral mode? (yes/no): ": "Do you want viral mode? (yes/no): ",
10
+ "Enter themes (comma-separated, leave blank if viral mode is True): ": "Enter themes (comma-separated, leave blank if viral mode is True): ",
11
+ "Subtitle burning skipped.": "Subtitle burning skipped.",
12
+ "Process completed successfully!": "Process completed successfully!",
13
+ "Using latest project: {}": "Using latest project: {}",
14
+ "Latest project found but 'input.mp4' is missing.": "Latest project found but 'input.mp4' is missing.",
15
+ "No existing projects found in VIRALS folder.": "No existing projects found in VIRALS folder.",
16
+ "VIRALS folder not found. Cannot load latest project.": "VIRALS folder not found. Cannot load latest project.",
17
+ "\nExisting viral segments found: {}": "\nExisting viral segments found: {}",
18
+ "Use existing viral segments? (yes/no) [default: yes]: ": "Use existing viral segments? (yes/no) [default: yes]: ",
19
+ "Loaded existing viral segments. Skipping configuration prompts.": "Loaded existing viral segments. Skipping configuration prompts.",
20
+ "Error loading JSON: {}.": "Error loading JSON: {}.",
21
+ "\nCurrent duration settings: {}s - {}s": "\nCurrent duration settings: {}s - {}s",
22
+ "Change duration? (y/n) [default: n]: ": "Change duration? (y/n) [default: n]: ",
23
+ "Minimum duration [{}]: ": "Minimum duration [{}]: ",
24
+ "Maximum duration [{}]: ": "Maximum duration [{}]: ",
25
+ "Invalid number. Using previous values.": "Invalid number. Using previous values.",
26
+ "Using AI Backend from config: {}": "Using AI Backend from config: {}",
27
+ "Select AI Backend for Viral Analysis:": "Select AI Backend for Viral Analysis:",
28
+ "1. Gemini API (Best / Recommended)": "1. Gemini API (Best / Recommended)",
29
+ "2. G4F (Free / Experimental)": "2. G4F (Free / Experimental)",
30
+ "3. Manual (Copy/Paste Prompt)": "3. Manual (Copy/Paste Prompt)",
31
+ "Choose (1/2/3): ": "Choose (1/2/3): ",
32
+ "Gemini API Key not found in api_config.json or arguments.": "Gemini API Key not found in api_config.json or arguments.",
33
+ "Enter your Gemini API Key: ": "Enter your Gemini API Key: ",
34
+ "Select Processing Workflow:": "Select Processing Workflow:",
35
+ "1. Full Processing (Face Crop 9:16 + Subtitles) [Default]": "1. Full Processing (Face Crop 9:16 + Subtitles) [Default]",
36
+ "2. Cut Only (Keep Original Size, No Subtitles)": "2. Cut Only (Keep Original Size, No Subtitles)",
37
+ "Choose (1/2): ": "Choose (1/2): ",
38
+ "Select Face Detection Model:": "Select Face Detection Model:",
39
+ "1. InsightFace (Recommended - High Accuracy, GPU Support) [Default]": "1. InsightFace (Recommended - High Accuracy, GPU Support) [Default]",
40
+ "2. MediaPipe (Google - Fast, lightweight)": "2. MediaPipe (Google - Fast, lightweight)",
41
+ "Select Face Tracking Mode:": "Select Face Tracking Mode:",
42
+ "1. Auto (Detect 1 or 2 faces dynamically) [Default]": "1. Auto (Detect 1 or 2 faces dynamically) [Default]",
43
+ "2. 1 Face (Focus on largest face)": "2. 1 Face (Focus on largest face)",
44
+ "3. 2 Faces (Split Screen)": "3. 2 Faces (Split Screen)",
45
+ "Error: No URL provided and no existing video selected.": "Error: No URL provided and no existing video selected.",
46
+ "Starting download...": "Starting download...",
47
+ "Transcribing with model {}...": "Transcribing with model {}...",
48
+ "Creating viral segments using {}...": "Creating viral segments using {}...",
49
+ "Cuts already exist. Cut again? (yes/no) [default: no]: ": "Cuts already exist. Cut again? (yes/no) [default: no]: ",
50
+ "Skipping Video Rendering (using existing cuts), but updating Subtitle JSONs...": "Skipping Video Rendering (using existing cuts), but updating Subtitle JSONs...",
51
+ "Cutting segments...": "Cutting segments...",
52
+ "Cut Only selected. Skipping Face Crop and Subtitles.": "Cut Only selected. Skipping Face Crop and Subtitles.",
53
+ "Process completed! Check your results in: {}": "Process completed! Check your results in: {}",
54
+ "Editing video with {} (Mode: {})...": "Editing video with {} (Mode: {})...",
55
+ "Processing subtitles...": "Processing subtitles...",
56
+ "Configuring subtitles for Split Screen (Center Position)...": "Configuring subtitles for Split Screen (Center Position)...",
57
+ "\nExisting cuts found in: {}": "\nExisting cuts found in: {}",
58
+ "\nAn error occurred: {}": "\nAn error occurred: {}",
59
+ "Process terminated.": "Process terminated.",
60
+ "Error terminating process: {}": "Error terminating process: {}",
61
+ "No process running.": "No process running.",
62
+ "Running...": "Running...",
63
+ "Error: No project selected.": "Error: No project selected.",
64
+ "Start Processing": "Start Processing",
65
+ "ViralCutter WebUI": "ViralCutter WebUI",
66
+ "Create New": "Create New",
67
+ "Input Source": "Input Source",
68
+ "YouTube URL": "YouTube URL",
69
+ "Existing Project": "Existing Project",
70
+ "Select Project": "Select Project",
71
+ "Segments": "Segments",
72
+ "Viral Mode": "Viral Mode",
73
+ "Themes": "Themes",
74
+ "Min Duration (s)": "Min Duration (s)",
75
+ "Max Duration (s)": "Max Duration (s)",
76
+ "AI Backend": "AI Backend",
77
+ "Gemini API Key": "Gemini API Key",
78
+ "AI Model": "AI Model",
79
+ "Chunk Size": "Chunk Size",
80
+ "Whisper Model": "Whisper Model",
81
+ "Workflow": "Workflow",
82
+ "Face Model": "Face Model",
83
+ "Face Mode": "Face Mode",
84
+ "Face Det. Interval": "Face Det. Interval",
85
+ "Subtitle Settings (alpha)": "Subtitle Settings (alpha)",
86
+ "Quick Presets": "Quick Presets",
87
+ "Enable Subtitle Customization (Includes Preset)": "Enable Subtitle Customization (Includes Preset)",
88
+ "Select options or preset to preview": "Select options or preset to preview",
89
+ "🎬 Render Animated Preview (Slow)": "🎬 Render Animated Preview (Slow)",
90
+ "Animated Preview": "Animated Preview",
91
+ "Advanced Settings": "Advanced Settings",
92
+ "Appearance": "Appearance",
93
+ "Font Name": "Font Name",
94
+ "Font Size (Base)": "Font Size (Base)",
95
+ "Highlight Size": "Highlight Size",
96
+ "Base Color": "Base Color",
97
+ "Highlight Color": "Highlight Color",
98
+ "Outline Color": "Outline Color",
99
+ "Shadow Color": "Shadow Color",
100
+ "Styling & Effects": "Styling & Effects",
101
+ "Outline Thickness": "Outline Thickness",
102
+ "Shadow Size": "Shadow Size",
103
+ "Border Style": "Border Style",
104
+ "Bold": "Bold",
105
+ "Italic": "Italic",
106
+ "Uppercase": "Uppercase",
107
+ "Underline": "Underline",
108
+ "Strikeout": "Strikeout",
109
+ "Positioning & Layout": "Positioning & Layout",
110
+ "V-Pos (Margin V)": "V-Pos (Margin V)",
111
+ "Alignment": "Alignment",
112
+ "Gap Limit": "Gap Limit",
113
+ "Mode": "Mode",
114
+ "Words per Block": "Words per Block",
115
+ "Stop": "Stop",
116
+ "Logs": "Logs",
117
+ "Results": "Results",
118
+ "Library": "Library",
119
+ "Existing Projects": "Existing Projects",
120
+ "Refresh List": "Refresh List",
121
+ "Error starting server: {}": "Error starting server: {}",
122
+ "No project selected.": "No project selected.",
123
+ "Project path not found: {}": "Project path not found: {}",
124
+ "Segment": "Segment",
125
+ "No description available.": "No description available.",
126
+ "External Video": "External Video",
127
+ "Not Found": "Not Found",
128
+ "No viral segments found.": "No viral segments found.",
129
+ "Error loading gallery: {}": "Error loading gallery: {}",
130
+ "PREVIEW": "PREVIEW",
131
+ "This is a PREVIEW of your subtitles": "This is a PREVIEW of your subtitles",
132
+ "Download": "Download",
133
+ "Error: {}": "Error: {}",
134
+ "This is a {} of your subtitles": "This is a {} of your subtitles",
135
+ "Manual": "Manual",
136
+ "Gemini": "Gemini",
137
+ "G4F": "G4F",
138
+ "Full": "Full",
139
+ "Cut Only": "Cut Only",
140
+ "Subtitles Only": "Subtitles Only",
141
+ "Outline": "Outline",
142
+ "Opaque Box": "Opaque Box",
143
+ "Left": "Left",
144
+ "Center": "Center",
145
+ "Right": "Right",
146
+ "Highlight": "Highlight",
147
+ "Word by Word": "Word by Word",
148
+ "No Highlight": "No Highlight",
149
+ "Auto": "Auto",
150
+ "funny, sad...": "funny, sad...",
151
+ "Bem-vindo ao ViralCutter! A ferramenta definitiva para transformar vídeos longos em clipes virais com o poder da IA.": "Welcome to ViralCutter! The ultimate tool to transform long videos into viral clips with the power of AI.",
152
+ "Aqui você pode:": "Here you can:",
153
+ "Cortes Automáticos": "Automatic Cuts",
154
+ "Identifique e corte os melhores momentos baseado em viralidade.": "Identify and cut the best moments based on virality.",
155
+ "Legendas Dinâmicas": "Dynamic Subtitles",
156
+ "Crie legendas estéticas (Estilo Hormozi) automaticamente.": "Create aesthetic subtitles (Hormozi Style) automatically.",
157
+ "IA Avançada": "Advanced AI",
158
+ "Suporte integrado para": "Integrated support for",
159
+ "Foco em Vertical": "Vertical Focus",
160
+ "Detecção facial inteligente para vídeos verticais (TikTok/Shorts/Reels).": "Smart face detection for vertical videos (TikTok/Shorts/Reels).",
161
+ "Este projeto foi desenvolvido para a comunidade do AI HUB Brasil.": "This project was developed for the AI HUB Brazil community.",
162
+ "Desenvolvido por Rafael Godoy": "Developed by Rafael Godoy",
163
+ "Apoie o projeto, qualquer valor é bem-vindo:": "Support the project, any amount is welcome:",
164
+ "Apoiar via PIX": "Support via PIX",
165
+ "Ignore Small Faces (0.0 - 1.0)": "Ignore Small Faces (0.0 - 1.0)",
166
+ "Relative size to ignore background.": "Relative size to ignore background.",
167
+ "Threshold for 2 Faces (0.0 - 1.0)": "Threshold for 2 Faces (0.0 - 1.0)",
168
+ "Size of 2nd face to activate split mode.": "Size of 2nd face to activate split mode.",
169
+ "Minimum Confidence (0.0 - 1.0)": "Minimum Confidence (0.0 - 1.0)",
170
+ "Ignore detections with low confidence.": "Ignore detections with low confidence.",
171
+ "Dead Zone (Stabilization)": "Dead Zone (Stabilization)",
172
+ "Movement pixels to ignore.": "Movement pixels to ignore.",
173
+ "Experimental: Focus on Speaker": "Experimental: Focus on Speaker",
174
+ "Tries to focus only on the speaking person instead of split screen.": "Tries to focus only on the speaking person instead of split screen.",
175
+ "MAR Threshold (Mouth Open)": "MAR Threshold (Mouth Open)",
176
+ "Mouth open sensitivity.": "Mouth open sensitivity.",
177
+ "Score Difference": "Score Difference",
178
+ "Minimum difference to focus on 1 face.": "Minimum difference to focus on 1 face.",
179
+ "Consider Motion": "Consider Motion",
180
+ "Increases score with motion (gestures).": "Increases score with motion (gestures).",
181
+ "Motion Dead Zone": "Motion Dead Zone",
182
+ "Pixels ignored.": "Pixels ignored.",
183
+ "Motion Sensitivity": "Motion Sensitivity",
184
+ "Points per pixel.": "Points per pixel.",
185
+ "Switch Speed": "Switch Speed",
186
+ "Speed to lose focus.": "Speed to lose focus.",
187
+ "Remove Punctuation": "Remove Punctuation",
188
+ "Advanced Face Settings": "Advanced Face Settings",
189
+ "Configuration Presets": "Configuration Presets",
190
+ "Download and use official subtitles if available. (Recommended, it speeds up the process)": "Download and use official subtitles if available. (Recommended, it speeds up the process)",
191
+ "Edit Subtitles (Smart Mode)": "Edit Subtitles (Smart Mode)",
192
+ "Error: Project folder could not be determined from logs.": "Error: Project folder could not be determined from logs.",
193
+ "Experimental: Active Speaker & Motion": "Experimental: Active Speaker & Motion",
194
+ "Load Subtitles": "Load Subtitles",
195
+ "Loaded {} segments.": "Loaded {} segments.",
196
+ "Local (GGUF)": "Local (GGUF)",
197
+ "No file loaded.": "No file loaded.",
198
+ "Please select project and file.": "Please select project and file.",
199
+ "Refresh": "Refresh",
200
+ "Render All started in background... Check terminal/logs.": "Render All started in background... Check terminal/logs.",
201
+ "Select Subtitle File": "Select Subtitle File",
202
+ "Status": "Status",
203
+ "Subtitle Editor": "Subtitle Editor",
204
+ "Subtitle Segments": "Subtitle Segments",
205
+ "Translate Subtitles To": "Translate Subtitles To",
206
+ "Use YouTube Subs": "Use YouTube Subs",
207
+ "Video Quality": "Video Quality",
208
+ "⚡ Render This Segment (Very-Fast)": "⚡ Render This Segment (Very-Fast)",
209
+ "🎬 Render All (Fast)": "🎬 Render All (Fast)",
210
+ "💾 Save Changes": "💾 Save Changes"
211
+ }
i18n/locale/pt_BR.json ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "main": "main",
3
+ "Burn only mode activated. Skipping to subtitle burning...": "Modo de apenas queimar legendas ativado. Pulando para a queima de legendas...",
4
+ "Subtitle burning completed.": "Queima de legendas concluída.",
5
+ "Enter the YouTube video URL (or press Enter to use latest project): ": "Digite a URL do vídeo do YouTube (ou pressione Enter para usar o projeto mais recente): ",
6
+ "Enter the number of viral segments to create: ": "Digite o número de segmentos virais para criar: ",
7
+ "\nError: Number must be greater than 0.": "\nErro: O número deve ser maior que 0.",
8
+ "Error: The value you entered is not an integer. Please try again.": "Erro: O valor que você digitou não é um número inteiro. Tente novamente.",
9
+ "Do you want viral mode? (yes/no): ": "Você quer o modo viral? (sim/não): ",
10
+ "Enter themes (comma-separated, leave blank if viral mode is True): ": "Digite temas (separados por vírgula, deixe em branco se o modo viral for Verdadeiro): ",
11
+ "Subtitle burning skipped.": "Queima de legendas ignorada.",
12
+ "Process completed successfully!": "Processo concluído com sucesso!",
13
+ "Using latest project: {}": "Usando o projeto mais recente: {}",
14
+ "Latest project found but 'input.mp4' is missing.": "Projeto mais recente encontrado, mas 'input.mp4' está faltando.",
15
+ "No existing projects found in VIRALS folder.": "Nenhum projeto existente encontrado na pasta VIRALS.",
16
+ "VIRALS folder not found. Cannot load latest project.": "Pasta VIRALS não encontrada. Não é possível carregar o projeto mais recente.",
17
+ "\nExisting viral segments found: {}": "\nSegmentos virais existentes encontrados: {}",
18
+ "Use existing viral segments? (yes/no) [default: yes]: ": "Usar segmentos virais existentes? (sim/não) [padrão: sim]: ",
19
+ "Loaded existing viral segments. Skipping configuration prompts.": "Segmentos virais existentes carregados. Pulando prompts de configuração.",
20
+ "Error loading JSON: {}.": "Erro ao carregar JSON: {}.",
21
+ "\nCurrent duration settings: {}s - {}s": "\nConfigurações de duração atuais: {}s - {}s",
22
+ "Change duration? (y/n) [default: n]: ": "Alterar duração? (s/n) [padrão: n]: ",
23
+ "Minimum duration [{}]: ": "Duração mínima [{}]: ",
24
+ "Maximum duration [{}]: ": "Duração máxima [{}]: ",
25
+ "Invalid number. Using previous values.": "Número inválido. Usando valores anteriores.",
26
+ "Using AI Backend from config: {}": "Usando Backend de IA da configuração: {}",
27
+ "Select AI Backend for Viral Analysis:": "Selecione o Backend de IA para Análise Viral:",
28
+ "1. Gemini API (Best / Recommended)": "1. Gemini API (Melhor / Recomendado)",
29
+ "2. G4F (Free / Experimental)": "2. G4F (Grátis / Experimental)",
30
+ "3. Manual (Copy/Paste Prompt)": "3. Manual (Copiar/Colar Prompt)",
31
+ "Choose (1/2/3): ": "Escolha (1/2/3): ",
32
+ "Gemini API Key not found in api_config.json or arguments.": "Chave da API Gemini não encontrada em api_config.json ou argumentos.",
33
+ "Enter your Gemini API Key: ": "Digite sua chave de API Gemini: ",
34
+ "Select Processing Workflow:": "Selecione o Fluxo de Trabalho de Processamento:",
35
+ "1. Full Processing (Face Crop 9:16 + Subtitles) [Default]": "1. Processamento Completo (Recorte de Rosto 9:16 + Legendas) [Padrão]",
36
+ "2. Cut Only (Keep Original Size, No Subtitles)": "2. Apenas Cortar (Manter Tamanho Original, Sem Legendas)",
37
+ "Choose (1/2): ": "Escolha (1/2): ",
38
+ "Select Face Detection Model:": "Selecione o Modelo de Detecção de Rosto:",
39
+ "1. InsightFace (Recommended - High Accuracy, GPU Support) [Default]": "1. InsightFace (Recomendado - Alta Precisão, Suporte a GPU) [Padrão]",
40
+ "2. MediaPipe (Google - Fast, lightweight)": "2. MediaPipe (Google - Rápido, leve)",
41
+ "Select Face Tracking Mode:": "Selecione o Modo de Rastreamento de Rosto:",
42
+ "1. Auto (Detect 1 or 2 faces dynamically) [Default]": "1. Auto (Detectar 1 ou 2 rostos dinamicamente) [Padrão]",
43
+ "2. 1 Face (Focus on largest face)": "2. 1 Rosto (Focar no maior rosto)",
44
+ "3. 2 Faces (Split Screen)": "3. 2 Rostos (Tela Dividida)",
45
+ "Error: No URL provided and no existing video selected.": "Erro: Nenhuma URL fornecida e nenhum vídeo existente selecionado.",
46
+ "Starting download...": "Iniciando download...",
47
+ "Transcribing with model {}...": "Transcrevendo com modelo {}...",
48
+ "Creating viral segments using {}...": "Criando segmentos virais usando {}...",
49
+ "Cuts already exist. Cut again? (yes/no) [default: no]: ": "Cortes já existem. Cortar novamente? (sim/não) [padrão: não]: ",
50
+ "Skipping Video Rendering (using existing cuts), but updating Subtitle JSONs...": "Pulando Renderização de Vídeo (usando cortes existentes), mas atualizando JSONs de Legenda...",
51
+ "Cutting segments...": "Cortando segmentos...",
52
+ "Cut Only selected. Skipping Face Crop and Subtitles.": "Selecionado Apenas Cortar. Pulando Recorte de Rosto e Legendas.",
53
+ "Process completed! Check your results in: {}": "Processo concluído! Verifique seus resultados em: {}",
54
+ "Editing video with {} (Mode: {})...": "Editando vídeo com {} (Modo: {})...",
55
+ "Processing subtitles...": "Processando legendas...",
56
+ "Configuring subtitles for Split Screen (Center Position)...": "Configurando legendas para Tela Dividida (Posição Central)...",
57
+ "\nExisting cuts found in: {}": "\nCortes existentes encontrados em: {}",
58
+ "\nAn error occurred: {}": "\nOcorreu um erro: {}",
59
+ "Process terminated.": "Processo terminado.",
60
+ "Error terminating process: {}": "Erro ao terminar processo: {}",
61
+ "No process running.": "Nenhum processo em execução.",
62
+ "Running...": "Executando...",
63
+ "Error: No project selected.": "Erro: Nenhum projeto selecionado.",
64
+ "Start Processing": "Iniciar Processamento",
65
+ "ViralCutter WebUI": "ViralCutter WebUI",
66
+ "Create New": "Criar Novo",
67
+ "Input Source": "Fonte de Entrada",
68
+ "YouTube URL": "URL do YouTube",
69
+ "Existing Project": "Projeto Existente",
70
+ "Select Project": "Selecionar Projeto",
71
+ "Segments": "Segmentos",
72
+ "Viral Mode": "Modo Viral",
73
+ "Themes": "Temas",
74
+ "Min Duration (s)": "Duração Mín (s)",
75
+ "Max Duration (s)": "Duração Máx (s)",
76
+ "AI Backend": "Backend de IA",
77
+ "Gemini API Key": "Chave API Gemini",
78
+ "AI Model": "Modelo de IA",
79
+ "Chunk Size": "Tamanho do Chunk",
80
+ "Whisper Model": "Modelo Whisper",
81
+ "Workflow": "Fluxo de Trabalho",
82
+ "Face Model": "Modelo de Face",
83
+ "Face Mode": "Modo de Face",
84
+ "Face Det. Interval": "Intervalo Det. Face",
85
+ "Subtitle Settings (alpha)": "Configurações de Legenda (alfa)",
86
+ "Quick Presets": "Predefinições Rápidas",
87
+ "Enable Subtitle Customization (Includes Preset)": "Habilitar Personalização de Legenda",
88
+ "Select options or preset to preview": "Selecione opções ou predefinição para visualizar",
89
+ "🎬 Render Animated Preview (Slow)": "🎬 Renderizar Prévia Animada (Lento)",
90
+ "Animated Preview": "Prévia Animada",
91
+ "Advanced Settings": "Configurações Avançadas",
92
+ "Appearance": "Aparência",
93
+ "Font Name": "Nome da Fonte",
94
+ "Font Size (Base)": "Tamanho da Fonte (Base)",
95
+ "Highlight Size": "Tamanho do Destaque",
96
+ "Base Color": "Cor Base",
97
+ "Highlight Color": "Cor do Destaque",
98
+ "Outline Color": "Cor do Contorno",
99
+ "Shadow Color": "Cor da Sombra",
100
+ "Styling & Effects": "Estilo & Efeitos",
101
+ "Outline Thickness": "Espessura do Contorno",
102
+ "Shadow Size": "Tamanho da Sombra",
103
+ "Border Style": "Estilo da Borda",
104
+ "Bold": "Negrito",
105
+ "Italic": "Itálico",
106
+ "Uppercase": "Maiúsculas",
107
+ "Underline": "Sublinhado",
108
+ "Strikeout": "Tachado",
109
+ "Positioning & Layout": "Posicionamento & Layout",
110
+ "V-Pos (Margin V)": "V-Pos (Margem V)",
111
+ "Alignment": "Alinhamento",
112
+ "Gap Limit": "Limite de Lacuna",
113
+ "Mode": "Modo",
114
+ "Words per Block": "Palavras por Bloco",
115
+ "Stop": "Parar",
116
+ "Logs": "Logs",
117
+ "Results": "Resultados",
118
+ "Library": "Biblioteca",
119
+ "Existing Projects": "Projetos Existentes",
120
+ "Refresh List": "Atualizar Lista",
121
+ "Error starting server: {}": "Erro ao iniciar servidor: {}",
122
+ "No project selected.": "Nenhum projeto selecionado.",
123
+ "Project path not found: {}": "Caminho do projeto não encontrado: {}",
124
+ "Segment": "Segmento",
125
+ "No description available.": "Nenhuma descrição disponível.",
126
+ "External Video": "Vídeo Externo",
127
+ "Not Found": "Não Encontrado",
128
+ "No viral segments found.": "Nenhum segmento viral encontrado.",
129
+ "Error loading gallery: {}": "Erro ao carregar galeria: {}",
130
+ "PREVIEW": "PRÉVIA",
131
+ "This is a PREVIEW of your subtitles": "Esta é uma PRÉVIA das suas legendas",
132
+ "Download": "Baixar",
133
+ "Error: {}": "Erro: {}",
134
+ "This is a {} of your subtitles": "Esta é uma {} das suas legendas",
135
+ "Manual": "Manual",
136
+ "Gemini": "Gemini",
137
+ "G4F": "G4F",
138
+ "Full": "Completo",
139
+ "Cut Only": "Apenas Corte",
140
+ "Subtitles Only": "Apenas Legendas",
141
+ "Outline": "Contorno",
142
+ "Opaque Box": "Caixa Opaca",
143
+ "Left": "Esquerda",
144
+ "Center": "Centro",
145
+ "Right": "Direita",
146
+ "Highlight": "Destacar",
147
+ "Word by Word": "Palavra por Palavra",
148
+ "No Highlight": "Sem Destaque",
149
+ "Auto": "Auto",
150
+ "funny, sad...": "engraçado, triste...",
151
+ "Bem-vindo ao ViralCutter! A ferramenta definitiva para transformar vídeos longos em clipes virais com o poder da IA.": "Bem-vindo ao ViralCutter! A ferramenta definitiva para transformar vídeos longos em clipes virais com o poder da IA.",
152
+ "Aqui você pode:": "Aqui você pode:",
153
+ "Cortes Automáticos": "Cortes Automáticos",
154
+ "Identifique e corte os melhores momentos baseado em viralidade.": "Identifique e corte os melhores momentos baseado em viralidade.",
155
+ "Legendas Dinâmicas": "Legendas Dinâmicas",
156
+ "Crie legendas estéticas (Estilo Hormozi) automaticamente.": "Crie legendas estéticas (Estilo Hormozi) automaticamente.",
157
+ "IA Avançada": "IA Avançada",
158
+ "Suporte integrado para": "Suporte integrado para",
159
+ "Foco em Vertical": "Foco em Vertical",
160
+ "Detecção facial inteligente para vídeos verticais (TikTok/Shorts/Reels).": "Detecção facial inteligente para vídeos verticais (TikTok/Shorts/Reels).",
161
+ "Este projeto foi desenvolvido para a comunidade do AI HUB Brasil.": "Este projeto foi desenvolvido para a comunidade do AI HUB Brasil.",
162
+ "Desenvolvido por Rafael Godoy": "Desenvolvido por Rafael Godoy",
163
+ "Apoie o projeto, qualquer valor é bem-vindo:": "Apoie o projeto, qualquer valor é bem-vindo:",
164
+ "Apoiar via PIX": "Apoiar via PIX",
165
+ "Ignore Small Faces (0.0 - 1.0)": "Ignorar Rostos Pequenos (0.0 - 1.0)",
166
+ "Relative size to ignore background.": "Tamanho relativo para ignorar fundo.",
167
+ "Threshold for 2 Faces (0.0 - 1.0)": "Limite para 2 Rostos (0.0 - 1.0)",
168
+ "Size of 2nd face to activate split mode.": "Tamanho do 2º rosto para ativar modo duplo.",
169
+ "Minimum Confidence (0.0 - 1.0)": "Confiança Mínima (0.0 - 1.0)",
170
+ "Ignore detections with low confidence.": "Ignorar detecções com baixa confiança.",
171
+ "Dead Zone (Stabilization)": "Zona Morta (Estabilização)",
172
+ "Movement pixels to ignore.": "Pixels de movimento para ignorar.",
173
+ "Experimental: Focus on Speaker": "Experimental: Focar em quem fala",
174
+ "Tries to focus only on the speaking person instead of split screen.": "Tenta focar apenas na pessoa falando em vez de dividir a tela.",
175
+ "MAR Threshold (Mouth Open)": "MAR Threshold (Boca Aberta)",
176
+ "Mouth open sensitivity.": "Sensibilidade da boca aberta.",
177
+ "Score Difference": "Diferença de Score",
178
+ "Minimum difference to focus on 1 face.": "Diferença mínima para focar em 1 rosto.",
179
+ "Consider Motion": "Considerar Movimento",
180
+ "Increases score with motion (gestures).": "Aumenta score com movimento (gestos).",
181
+ "Motion Dead Zone": "Zona Morta de Movimento",
182
+ "Pixels ignored.": "Pixels ignorados.",
183
+ "Motion Sensitivity": "Sensibilidade de Movimento",
184
+ "Points per pixel.": "Pontos por pixel.",
185
+ "Switch Speed": "Velocidade de Troca",
186
+ "Speed to lose focus.": "Rapidez para perder o foco.",
187
+ "Remove Punctuation": "Remover Pontuação",
188
+ "Advanced Face Settings": "Configurações Avançadas de Rosto",
189
+ "Configuration Presets": "Predefinições de Configuração",
190
+ "Download and use official subtitles if available. (Recommended, it speeds up the process)": "Baixar e usar legendas oficiais se disponíveis. (Recomendado, acelera o processo)",
191
+ "Edit Subtitles (Smart Mode)": "Editar Legendas (Modo Inteligente)",
192
+ "Error: Project folder could not be determined from logs.": "Erro: Pasta do projeto não pôde ser determinada a partir dos logs.",
193
+ "Experimental: Active Speaker & Motion": "Experimental: Falante Ativo & Movimento",
194
+ "Load Subtitles": "Carregar Legendas",
195
+ "Loaded {} segments.": "Carregado {} segmentos.",
196
+ "Local (GGUF)": "Local (GGUF)",
197
+ "No file loaded.": "Nenhum arquivo carregado.",
198
+ "Please select project and file.": "Por favor selecione projeto e arquivo.",
199
+ "Refresh": "Atualizar",
200
+ "Render All started in background... Check terminal/logs.": "Renderizar Tudo iniciado em segundo plano... Verifique terminal/logs.",
201
+ "Select Subtitle File": "Selecionar Arquivo de Legenda",
202
+ "Status": "Status",
203
+ "Subtitle Editor": "Editor de Legendas",
204
+ "Subtitle Segments": "Segmentos de Legenda",
205
+ "Translate Subtitles To": "Traduzir Legendas Para",
206
+ "Use YouTube Subs": "Usar Legendas do YouTube",
207
+ "Video Quality": "Qualidade de Vídeo",
208
+ "⚡ Render This Segment (Very-Fast)": "⚡ Renderizar Este Segmento (Muito Rápido)",
209
+ "🎬 Render All (Fast)": "🎬 Renderizar Tudo (Rápido)",
210
+ "💾 Save Changes": "💾 Salvar Alterações"
211
+ }
i18n/locale_diff.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from collections import OrderedDict
4
+
5
+ # Define the standard file name
6
+ standard_file = "locale/zh_CN.json"
7
+
8
+ # Find all JSON files in the directory
9
+ dir_path = "locale/"
10
+ languages = [
11
+ os.path.join(dir_path, f)
12
+ for f in os.listdir(dir_path)
13
+ if f.endswith(".json") and f != standard_file
14
+ ]
15
+
16
+ # Load the standard file
17
+ with open(standard_file, "r", encoding="utf-8") as f:
18
+ standard_data = json.load(f, object_pairs_hook=OrderedDict)
19
+
20
+ # Loop through each language file
21
+ for lang_file in languages:
22
+ # Load the language file
23
+ with open(lang_file, "r", encoding="utf-8") as f:
24
+ lang_data = json.load(f, object_pairs_hook=OrderedDict)
25
+
26
+ # Find the difference between the language file and the standard file
27
+ diff = set(standard_data.keys()) - set(lang_data.keys())
28
+
29
+ miss = set(lang_data.keys()) - set(standard_data.keys())
30
+
31
+ # Add any missing keys to the language file
32
+ for key in diff:
33
+ lang_data[key] = key
34
+
35
+ # Del any extra keys to the language file
36
+ for key in miss:
37
+ del lang_data[key]
38
+
39
+ # Sort the keys of the language file to match the order of the standard file
40
+ lang_data = OrderedDict(
41
+ sorted(lang_data.items(), key=lambda x: list(standard_data.keys()).index(x[0]))
42
+ )
43
+
44
+ # Save the updated language file
45
+ with open(lang_file, "w", encoding="utf-8") as f:
46
+ json.dump(lang_data, f, ensure_ascii=False, indent=4, sort_keys=True)
47
+ f.write("\n")
i18n/scan_i18n.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import glob
3
+ import json
4
+ from collections import OrderedDict
5
+
6
+
7
+ def extract_i18n_strings(node):
8
+ i18n_strings = []
9
+
10
+ if (
11
+ isinstance(node, ast.Call)
12
+ and isinstance(node.func, ast.Name)
13
+ and node.func.id == "i18n"
14
+ ):
15
+ for arg in node.args:
16
+ if isinstance(arg, ast.Str):
17
+ i18n_strings.append(arg.s)
18
+
19
+ for child_node in ast.iter_child_nodes(node):
20
+ i18n_strings.extend(extract_i18n_strings(child_node))
21
+
22
+ return i18n_strings
23
+
24
+
25
+ # scan the directory for all .py files (recursively)
26
+ # for each file, parse the code into an AST
27
+ # for each AST, extract the i18n strings
28
+
29
+ strings = []
30
+ for filename in glob.iglob("**/*.py", recursive=True):
31
+ with open(filename, "r") as f:
32
+ code = f.read()
33
+ if "I18nAuto" in code:
34
+ tree = ast.parse(code)
35
+ i18n_strings = extract_i18n_strings(tree)
36
+ print(filename, len(i18n_strings))
37
+ strings.extend(i18n_strings)
38
+ code_keys = set(strings)
39
+ """
40
+ n_i18n.py
41
+ gui_v1.py 26
42
+ app.py 16
43
+ infer-web.py 147
44
+ scan_i18n.py 0
45
+ i18n.py 0
46
+ lib/train/process_ckpt.py 1
47
+ """
48
+ print()
49
+ print("Total unique:", len(code_keys))
50
+
51
+
52
+ standard_file = "i18n/locale/zh_CN.json"
53
+ with open(standard_file, "r", encoding="utf-8") as f:
54
+ standard_data = json.load(f, object_pairs_hook=OrderedDict)
55
+ standard_keys = set(standard_data.keys())
56
+
57
+ # Define the standard file name
58
+ unused_keys = standard_keys - code_keys
59
+ print("Unused keys:", len(unused_keys))
60
+ for unused_key in unused_keys:
61
+ print("\t", unused_key)
62
+
63
+ missing_keys = code_keys - standard_keys
64
+ print("Missing keys:", len(missing_keys))
65
+ for missing_key in missing_keys:
66
+ print("\t", missing_key)
67
+
68
+ code_keys_dict = OrderedDict()
69
+ for s in strings:
70
+ code_keys_dict[s] = s
71
+
72
+ # write back
73
+ with open(standard_file, "w", encoding="utf-8") as f:
74
+ json.dump(code_keys_dict, f, ensure_ascii=False, indent=4, sort_keys=True)
75
+ f.write("\n")
install_dependencies.bat ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ echo ==========================================
3
+ echo Instalando uv (Gerenciador de pacotes rapido Python)...
4
+ echo ==========================================
5
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
6
+
7
+ echo.
8
+ echo ==========================================
9
+ echo Criando ambiente virtual (.venv)...
10
+ echo ==========================================
11
+ :: Tenta usar o uv do PATH. Se falhar, pode ser necessario reiniciar o terminal.
12
+ uv venv
13
+
14
+ echo.
15
+ echo ==========================================
16
+ echo Instalando dependencias do requirements.txt...
17
+ echo ==========================================
18
+ :: Ativa o venv temporariamente para o install (uv gerencia isso automaticamente se detectar o venv, mas vamos garantir)
19
+ :: Se o uv venv criou a pasta .venv, o uv pip install vai usar ela por padrao se estiver na raiz.
20
+ uv pip install -r requirements.txt
21
+ uv pip install auto-editor
22
+
23
+ echo.
24
+ echo ==========================================
25
+ echo Concluido!
26
+ echo ==========================================
27
+ pause
main_improved.py ADDED
@@ -0,0 +1,695 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+
4
+ # Suppress unnecessary logs before importing heavy libs
5
+ os.environ["ORT_LOGGING_LEVEL"] = "3"
6
+ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
7
+
8
+ import warnings
9
+ warnings.filterwarnings("ignore")
10
+
11
+ import json
12
+ import shutil
13
+ import subprocess
14
+ import argparse
15
+ import time
16
+ from scripts import (
17
+ download_video,
18
+ transcribe_video,
19
+ create_viral_segments,
20
+ cut_segments,
21
+ edit_video,
22
+ transcribe_cuts,
23
+ adjust_subtitles,
24
+ burn_subtitles,
25
+ save_json,
26
+ organize_output,
27
+ translate_json,
28
+ )
29
+ from i18n.i18n import I18nAuto
30
+
31
+ # Inicializa sistema de tradução
32
+ i18n = I18nAuto()
33
+ #
34
+ # Configurações de Legenda (ASS Style)
35
+ # Cores no formato BGR (Blue-Green-Red) para o ASS
36
+ COLORS = {
37
+ "red": "0000FF", # Red
38
+ "yellow": "00FFFF", # Yellow
39
+ "green": "00FF00", # Green
40
+ "white": "FFFFFF", # White
41
+ "black": "000000", # Black
42
+ "grey": "808080", # Grey
43
+ }
44
+
45
+ def get_subtitle_config(config_path=None):
46
+ """
47
+ Returns the subtitle configuration dictionary.
48
+ Can be expanded to load from a JSON/YAML file in the future.
49
+ """
50
+ # Default Config
51
+ base_color_transparency = "00"
52
+ outline_transparency = "FF"
53
+ highlight_color_transparency = "00"
54
+ shadow_color_transparency = "00"
55
+
56
+ config = {
57
+ "font": "Montserrat-Regular",
58
+ "base_size": 30,
59
+ "base_color": f"&H{base_color_transparency}{COLORS['white']}&",
60
+ "highlight_size": 35,
61
+ "words_per_block": 3,
62
+ "gap_limit": 0.5,
63
+ "mode": 'highlight', # Options: 'no_highlight', 'word_by_word', 'highlight'
64
+ "highlight_color": f"&H{highlight_color_transparency}{COLORS['green']}&",
65
+ "vertical_position": 210, # 1=170(top), ... 4=60(default)
66
+ "alignment": 2, # 2=Center
67
+ "bold": 0,
68
+ "italic": 0,
69
+ "underline": 0,
70
+ "strikeout": 0,
71
+ "border_style": 2, # 1=outline, 3=box
72
+ "outline_thickness": 1.5,
73
+ "outline_color": f"&H{outline_transparency}{COLORS['grey']}&",
74
+ "shadow_size": 2,
75
+ "shadow_color": f"&H{shadow_color_transparency}{COLORS['black']}&",
76
+ "remove_punctuation": True,
77
+ }
78
+
79
+ if config_path and os.path.exists(config_path):
80
+ try:
81
+ with open(config_path, 'r', encoding='utf-8') as f:
82
+ loaded_config = json.load(f)
83
+ config.update(loaded_config)
84
+ print(f"Loaded subtitle config from {config_path}")
85
+ except Exception as e:
86
+ print(f"Error loading subtitle config: {e}. Using defaults.")
87
+
88
+ return config
89
+
90
+ def interactive_input_int(prompt_text):
91
+ """Solicita um inteiro ao usuário via terminal."""
92
+ while True:
93
+ try:
94
+ value = int(input(i18n(prompt_text)))
95
+ if value > 0:
96
+ return value
97
+ print(i18n("\nError: Number must be greater than 0."))
98
+ except ValueError:
99
+ print(i18n("\nError: The value you entered is not an integer. Please try again."))
100
+
101
+ def main():
102
+ # Configuração de Argumentos via Linha de Comando (CLI)
103
+ parser = argparse.ArgumentParser(description="ViralCutter CLI")
104
+ parser.add_argument("--url", help="YouTube Video URL")
105
+ parser.add_argument("--segments", type=int, help="Number of segments to create")
106
+ parser.add_argument("--viral", action="store_true", help="Enable viral mode")
107
+ parser.add_argument("--themes", help="Comma-separated themes (if not viral mode)")
108
+ parser.add_argument("--burn-only", action="store_true", help="Skip processing and only burn subtitles")
109
+ parser.add_argument("--min-duration", type=int, default=15, help="Minimum segment duration (seconds)")
110
+ parser.add_argument("--max-duration", type=int, default=90, help="Maximum segment duration (seconds)")
111
+ parser.add_argument("--model", default="large-v3-turbo", help="Whisper model to use")
112
+
113
+ parser.add_argument("--ai-backend", choices=["manual", "gemini", "g4f", "local"], help="AI backend for viral analysis")
114
+ parser.add_argument("--api-key", help="Gemini API Key (required if ai-backend is gemini)")
115
+
116
+ parser.add_argument("--chunk-size", help="Override Chunk Size")
117
+ parser.add_argument("--ai-model-name", help="Override AI Model Name")
118
+
119
+ parser.add_argument("--project-path", help="Path to existing project folder (overrides URL/Latest)")
120
+ parser.add_argument("--workflow", choices=["1", "2", "3"], default="1", help="Workflow choice: 1=Full, 2=Cut Only, 3=Subtitles Only")
121
+ parser.add_argument("--face-model", choices=["insightface", "mediapipe"], default="insightface", help="Face detection model")
122
+ parser.add_argument("--face-mode", choices=["auto", "1", "2"], default="auto", help="Face tracking mode: auto, 1, 2")
123
+ parser.add_argument("--subtitle-config", help="Path to subtitle configuration JSON file")
124
+ parser.add_argument("--face-detect-interval", type=str, default="0.17,1.0", help="Face detection interval in seconds. Single value or 'interval_1face,interval_2face'")
125
+ parser.add_argument("--face-filter-threshold", type=float, default=0.35, help="Relative area threshold to ignore background faces (default: 0.35)")
126
+ parser.add_argument("--face-two-threshold", type=float, default=0.60, help="Relative area threshold to trigger 2-face mode (default: 0.60)")
127
+ parser.add_argument("--face-confidence-threshold", type=float, default=0.30, help="Face detection confidence threshold (0.0 - 1.0) (default: 0.30)")
128
+ parser.add_argument("--face-dead-zone", type=str, default="40", help="Camera movement dead zone in pixels (default: 40)") # str to support future "auto"
129
+ parser.add_argument("--focus-active-speaker", action="store_true", help="Enable experimental active speaker focus (InsightFace only)")
130
+ parser.add_argument("--active-speaker-mar", type=float, default=0.03, help="Mouth Aspect Ratio threshold for active speaker (0.0 - 1.0) (default: 0.03)")
131
+ parser.add_argument("--active-speaker-score-diff", type=float, default=1.5, help="Score difference to focus on active speaker (default: 1.5)")
132
+ parser.add_argument("--include-motion", action="store_true", help="Include motion (body/head movement) in activity score")
133
+ parser.add_argument("--active-speaker-motion-threshold", type=float, default=3.0, help="Motion deadzone in pixels (default: 3.0)")
134
+ parser.add_argument("--active-speaker-motion-sensitivity", type=float, default=0.05, help="Motion sensitivity multiplier (default: 0.05)")
135
+ parser.add_argument("--active-speaker-decay", type=float, default=2.0, help="Activity score decay rate (default: 2.0)")
136
+ parser.add_argument("--skip-prompts", action="store_true", help="Skip interactive prompts and use defaults/existing files")
137
+ parser.add_argument("--video-quality", choices=["best", "1080p", "720p", "480p"], default="best", help="Video download quality")
138
+ parser.add_argument("--skip-youtube-subs", action="store_true", help="Skip downloading YouTube subtitles")
139
+ parser.add_argument("--translate-target", help="Target language code for subtitle translation (e.g. 'pt', 'en').")
140
+
141
+ args = parser.parse_args()
142
+
143
+ # Workflow Logic
144
+ workflow_choice = args.workflow
145
+
146
+ # If Subtitles Only, checking project path
147
+ if workflow_choice == "3" and not args.project_path and not args.url and not args.skip_prompts:
148
+ # Prompt for project path or use latest if not provided?
149
+ pass # Will handle in main flow
150
+
151
+ # Modo Apenas Queimar Legenda (Legacy support, mapped to Workflow 3 internally if burn-only is set)
152
+ # Verifica o argumento CLI ou uma variável local hardcoded (para compatibilidade)
153
+ burn_only_mode = args.burn_only
154
+
155
+ if burn_only_mode:
156
+ print(i18n("Burn only mode activated. Switching to Workflow 3..."))
157
+ workflow_choice = "3"
158
+
159
+ # Obtenção de Inputs (CLI ou Interativo)
160
+ url = args.url
161
+ project_path_arg = args.project_path
162
+ input_video = None
163
+
164
+ # Se project_path for fornecido, ignoramos URL
165
+ if project_path_arg:
166
+ if os.path.exists(project_path_arg):
167
+ print(i18n("Using provided project path: {}").format(project_path_arg))
168
+ # Tentar achar o input.mp4 pra manter compatibilidade de variaveis, embora Workflow 3 não precise de download
169
+ possible_input = os.path.join(project_path_arg, "input.mp4")
170
+ if os.path.exists(possible_input):
171
+ input_video = possible_input
172
+ else:
173
+ # Se não tiver input.mp4, tudo bem para workflow 3, mas definimos um dummy para não quebrar logica
174
+ input_video = os.path.join(project_path_arg, "dummy_input.mp4")
175
+
176
+ # Se for workflow 3, não precisamos de URL
177
+ else:
178
+ print(i18n("Error: Provided project path does not exist."))
179
+ sys.exit(1)
180
+
181
+ # Se não temos URL via CLI nem Project Path, pedimos agora
182
+ if not url and not project_path_arg:
183
+ if args.skip_prompts:
184
+ print(i18n("No URL provided and skipping prompts. Trying to load latest project..."))
185
+ # Fallthrough to project loading logic
186
+ else:
187
+ user_input = input(i18n("Enter the YouTube video URL (or press Enter to use latest project): ")).strip()
188
+ if user_input:
189
+ url = user_input
190
+
191
+ if not url and not input_video:
192
+ # Usuário apertou Enter (Vazio) -> Tentar pegar último projeto
193
+ base_virals = "VIRALS"
194
+ if os.path.exists(base_virals):
195
+ subdirs = [os.path.join(base_virals, d) for d in os.listdir(base_virals) if os.path.isdir(os.path.join(base_virals, d))]
196
+ if subdirs:
197
+ latest_project = max(subdirs, key=os.path.getmtime)
198
+ detected_video = os.path.join(latest_project, "input.mp4")
199
+ if os.path.exists(detected_video):
200
+ input_video = detected_video
201
+ print(i18n("Using latest project: {}").format(latest_project))
202
+ else:
203
+ print(i18n("Latest project found but 'input.mp4' is missing."))
204
+ sys.exit(1)
205
+ else:
206
+ print(i18n("No existing projects found in VIRALS folder."))
207
+ sys.exit(1)
208
+ else:
209
+ print(i18n("VIRALS folder not found. Cannot load latest project."))
210
+ sys.exit(1)
211
+
212
+ # -------------------------------------------------------------------------
213
+ # Checagem Antecipada de Segmentos Virais (Para pular configurações se já existirem)
214
+ # -------------------------------------------------------------------------
215
+ viral_segments = None
216
+ project_folder_anticipated = None
217
+
218
+ if input_video:
219
+ # Se já temos o vídeo, podemos deduzir a pasta
220
+ project_folder_anticipated = os.path.dirname(input_video)
221
+ viral_segments_file = os.path.join(project_folder_anticipated, "viral_segments.txt")
222
+
223
+ if os.path.exists(viral_segments_file):
224
+ print(i18n("\nExisting viral segments found: {}").format(viral_segments_file))
225
+ if args.skip_prompts:
226
+ use_existing_json = 'yes'
227
+ else:
228
+ use_existing_json = input(i18n("Use existing viral segments? (yes/no) [default: yes]: ")).strip().lower()
229
+
230
+ if use_existing_json in ['', 'y', 'yes']:
231
+ try:
232
+ with open(viral_segments_file, 'r', encoding='utf-8') as f:
233
+ viral_segments = json.load(f)
234
+ print(i18n("Loaded existing viral segments. Skipping configuration prompts."))
235
+ if viral_segments and "segments" in viral_segments:
236
+ print(f"DEBUG: Loaded {len(viral_segments['segments'])} segments from file.")
237
+ else:
238
+ print("DEBUG: Loaded JSON but 'segments' key is missing or empty.")
239
+ except Exception as e:
240
+ print(i18n("Error loading JSON: {}.").format(e))
241
+
242
+ # Variaveis de config de IA (só necessárias se não tivermos os segmentos)
243
+ num_segments = None
244
+ viral_mode = False
245
+ themes = ""
246
+ ai_backend = "manual" # default
247
+ api_key = None
248
+
249
+ if not viral_segments:
250
+ num_segments = args.segments
251
+ if not num_segments:
252
+ if args.skip_prompts:
253
+ print("No segments count provided and skip-prompts is ON. Using default 3.")
254
+ num_segments = 3
255
+ else:
256
+ num_segments = interactive_input_int("Enter the number of viral segments to create: ")
257
+
258
+ viral_mode = args.viral
259
+ if not args.viral and not args.themes:
260
+ if args.skip_prompts:
261
+ print("Viral mode not set, defaulting to True.")
262
+ viral_mode = True
263
+ else:
264
+ response = input(i18n("Do you want viral mode? (yes/no): ")).lower()
265
+ viral_mode = response in ['yes', 'y']
266
+
267
+ themes = args.themes if args.themes else ""
268
+ if not viral_mode and not themes:
269
+ if not args.skip_prompts:
270
+ themes = input(i18n("Enter themes (comma-separated, leave blank if viral mode is True): "))
271
+
272
+ # Duration Config
273
+ print(i18n("\nCurrent duration settings: {}s - {}s").format(args.min_duration, args.max_duration))
274
+ if not args.skip_prompts:
275
+ change_dur = input(i18n("Change duration? (y/n) [default: n]: ")).strip().lower()
276
+ if change_dur in ['y', 'yes']:
277
+ try:
278
+ min_d = input(i18n("Minimum duration [{}]: ").format(args.min_duration)).strip()
279
+ if min_d: args.min_duration = int(min_d)
280
+
281
+ max_d = input(i18n("Maximum duration [{}]: ").format(args.max_duration)).strip()
282
+ if max_d: args.max_duration = int(max_d)
283
+ except ValueError:
284
+ print(i18n("Invalid number. Using previous values."))
285
+
286
+ # Load API Config
287
+ config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'api_config.json')
288
+ api_config = {}
289
+ if os.path.exists(config_path):
290
+ try:
291
+ with open(config_path, 'r', encoding='utf-8') as f:
292
+ api_config = json.load(f)
293
+ except:
294
+ pass
295
+
296
+ # Seleção do Backend de IA
297
+ ai_backend = args.ai_backend
298
+
299
+ # Try to load backend from config if not in args
300
+ if not ai_backend and api_config.get("selected_api"):
301
+ ai_backend = api_config.get("selected_api")
302
+ print(i18n("Using AI Backend from config: {}").format(ai_backend))
303
+
304
+ if not ai_backend:
305
+ if args.skip_prompts:
306
+ print("No AI backend selected, defaulting to Manual.")
307
+ ai_backend = "manual"
308
+ else:
309
+ print("\n" + i18n("Select AI Backend for Viral Analysis:"))
310
+ print(i18n("1. Gemini API (Best / Recommended)"))
311
+ print(i18n("2. G4F (Free / Experimental)"))
312
+ print(i18n("3. Local (GGUF via llama.cpp)"))
313
+ print(i18n("4. Manual (Copy/Paste Prompt)"))
314
+ choice = input(i18n("Choose (1-4): ")).strip()
315
+
316
+ if choice == "1":
317
+ ai_backend = "gemini"
318
+ elif choice == "2":
319
+ ai_backend = "g4f"
320
+ elif choice == "3":
321
+ ai_backend = "local"
322
+ # Interactive model selection for local
323
+ # List models
324
+ models_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "models")
325
+ if not os.path.exists(models_dir): os.makedirs(models_dir)
326
+ models = [f for f in os.listdir(models_dir) if f.endswith(".gguf")]
327
+
328
+ if not models:
329
+ print(i18n("\nNo .gguf models found in 'models' directory."))
330
+ print(i18n("Please place a module file in: {}").format(models_dir))
331
+ print(i18n("Falling back to Manual..."))
332
+ ai_backend = "manual"
333
+ else:
334
+ print(i18n("\nAvailable Models:"))
335
+ for idx, m in enumerate(models):
336
+ print(f"{idx+1}. {m}")
337
+
338
+ try:
339
+ m_idx = int(input(i18n("Select Model (Number): "))) - 1
340
+ if 0 <= m_idx < len(models):
341
+ args.ai_model_name = models[m_idx] # Set global arg
342
+ else:
343
+ print("Invalid selection. Using first model.")
344
+ args.ai_model_name = models[0]
345
+ except:
346
+ print("Invalid input. Using first model.")
347
+ args.ai_model_name = models[0]
348
+
349
+ else:
350
+ ai_backend = "manual"
351
+
352
+ api_key = args.api_key
353
+ # Check config for API Key if using Gemini
354
+ if ai_backend == "gemini" and not api_key:
355
+ cfg_key = api_config.get("gemini", {}).get("api_key", "")
356
+ if cfg_key and cfg_key != "SUA_KEY_AQUI":
357
+ api_key = cfg_key
358
+
359
+ if ai_backend == "gemini" and not api_key:
360
+ if args.skip_prompts:
361
+ print("Gemini API key missing, but skip-prompts is ON. Might fail.")
362
+ else:
363
+ print(i18n("Gemini API Key not found in api_config.json or arguments."))
364
+ api_key = input(i18n("Enter your Gemini API Key: ")).strip()
365
+
366
+ # Workflow & Face Config Inputs
367
+ workflow_choice = args.workflow
368
+ face_model = args.face_model
369
+ face_mode = args.face_mode
370
+
371
+ # If args weren't provided and we are not skipping prompts, ask user
372
+ # Note: argparse defaults are set, so they "are provided" effectively.
373
+ # To truly detect "not provided", request default=None in argparse.
374
+ # But for "Simplified Mode", defaults are good.
375
+ # Advanced users use params.
376
+ # We will assume CLI defaults are what we want if skip_prompts is on.
377
+
378
+ # Logic for detection intervals (Moved out of interactive block to support CLI/WebUI)
379
+ detection_intervals = None
380
+ if args.face_detect_interval:
381
+ try:
382
+ parts = args.face_detect_interval.split(',')
383
+ if len(parts) == 1:
384
+ val = float(parts[0])
385
+ detection_intervals = {'1': val, '2': val}
386
+ elif len(parts) >= 2:
387
+ val1 = float(parts[0])
388
+ val2 = float(parts[1])
389
+ detection_intervals = {'1': val1, '2': val2}
390
+ except ValueError:
391
+ pass
392
+
393
+ if not args.burn_only and not args.skip_prompts:
394
+ # Interactive Face Config
395
+ print(i18n("\n--- Face Detection Settings ---"))
396
+ print(i18n("Current Face Model: {} | Mode: {}").format(face_model, face_mode))
397
+
398
+ if detection_intervals:
399
+ print(i18n("Custom detection intervals: {}").format(detection_intervals))
400
+ else:
401
+ print(i18n("Using dynamic intervals: 1s for 2-face, ~0.16s for 1-face."))
402
+
403
+
404
+ # Pipeline Execution
405
+ try:
406
+ # 1. Download & Project Setup
407
+ print(f"DEBUG: Checking input_video state. input_video={input_video}")
408
+
409
+ if not input_video:
410
+ if not url:
411
+ print(i18n("Error: No URL provided and no existing video selected."))
412
+ sys.exit(1)
413
+
414
+ print(i18n("Starting download..."))
415
+ download_subs = not args.skip_youtube_subs
416
+ download_result = download_video.download(url, download_subs=download_subs, quality=args.video_quality)
417
+
418
+ if isinstance(download_result, tuple):
419
+ input_video, project_folder = download_result
420
+ else:
421
+ input_video = download_result
422
+ project_folder = os.path.dirname(input_video)
423
+
424
+ print(f"DEBUG: Download finished. input_video={input_video}, project_folder={project_folder}")
425
+
426
+ else:
427
+ # Reuso de video existente
428
+ print("DEBUG: Using existing video logic.")
429
+ project_folder = os.path.dirname(input_video)
430
+
431
+ print(f"Project Folder: {project_folder}")
432
+
433
+ # 2. Transcribe
434
+ if workflow_choice == "3":
435
+ print(i18n("Workflow 3: Skipping Transcribe."))
436
+ # We assume transcription exists (SRT/JSON) or we won't need it for 'adjust_subtitles' if it uses 'subs/*.json' which are created by 'cut_segments'
437
+ # Actually 'adjust_subtitles' reads from 'project_folder/subs'.
438
+ # viral_segments = True # Removed to avoid overwritting dict loaded earlier
439
+ else:
440
+ print(i18n("Transcribing with model {}...").format(args.model))
441
+ # Se skip config, args.model é default
442
+ srt_file, tsv_file = transcribe_video.transcribe(input_video, args.model, project_folder=project_folder)
443
+
444
+ # 3. Create Viral Segments
445
+ if workflow_choice != "3":
446
+ # Se não carregamos 'viral_segments' lá em cima (ou se era download novo), checamos agora ou criamos
447
+ if not viral_segments:
448
+ # Checagem tardia para downloads novos que por acaso ja tenham json (Ex: URL repetida)
449
+ viral_segments_file_late = os.path.join(project_folder, "viral_segments.txt")
450
+ if os.path.exists(viral_segments_file_late):
451
+ print(i18n("Found existing viral segments file at {}").format(viral_segments_file_late))
452
+ if args.skip_prompts:
453
+ print(i18n("Skipping prompts enabled. Loading existing segments."))
454
+ try:
455
+ with open(viral_segments_file_late, 'r', encoding='utf-8') as f:
456
+ viral_segments = json.load(f)
457
+ except Exception as e:
458
+ print(i18n("Error loading existing JSON: {}. Proceeding to create new segments.").format(e))
459
+ else:
460
+ print(i18n("Loading existing viral segments found at {}").format(viral_segments_file_late))
461
+ try:
462
+ with open(viral_segments_file_late, 'r', encoding='utf-8') as f:
463
+ viral_segments = json.load(f)
464
+ except Exception as e:
465
+ print(i18n("Error loading existing JSON: {}.").format(e))
466
+
467
+ if not viral_segments:
468
+ print(i18n("Creating viral segments using {}...").format(ai_backend.upper()))
469
+ viral_segments = create_viral_segments.create(
470
+ num_segments,
471
+ viral_mode,
472
+ themes,
473
+ args.min_duration,
474
+ args.max_duration,
475
+ ai_mode=ai_backend,
476
+ api_key=api_key,
477
+ project_folder=project_folder,
478
+ chunk_size_arg=args.chunk_size,
479
+ model_name_arg=args.ai_model_name
480
+ )
481
+
482
+ if not viral_segments or not viral_segments.get("segments"):
483
+ print(i18n("Error: No viral segments were generated."))
484
+ print(i18n("Possible reasons: API error, Model not found, or empty response."))
485
+ print(i18n("Stopping execution."))
486
+ sys.exit(1)
487
+
488
+ save_json.save_viral_segments(viral_segments, project_folder=project_folder)
489
+
490
+ # 4. Cut Segments
491
+ # Se workflow for 3, pulamos corte
492
+ if workflow_choice == "3":
493
+ print(i18n("Workflow 3 (Subtitles Only): Skipping Cut and Edit."))
494
+ # Deduzir cuts folder apenas para log
495
+ cuts_folder = os.path.join(project_folder, "cuts")
496
+ else:
497
+ cuts_folder = os.path.join(project_folder, "cuts")
498
+ skip_cutting = False
499
+
500
+ if os.path.exists(cuts_folder) and os.listdir(cuts_folder):
501
+ print(i18n("\nExisting cuts found in: {}").format(cuts_folder))
502
+ if args.skip_prompts:
503
+ cut_again_resp = 'no'
504
+ else:
505
+ cut_again_resp = input(i18n("Cuts already exist. Cut again? (yes/no) [default: no]: ")).strip().lower()
506
+
507
+ # Default is no (skip) if they just press enter or say no
508
+ if cut_again_resp not in ['y', 'yes']:
509
+ skip_cutting = True
510
+
511
+ if skip_cutting:
512
+ print(i18n("Skipping Video Rendering (using existing cuts), but updating Subtitle JSONs..."))
513
+ else:
514
+ print(i18n("Cutting segments..."))
515
+
516
+ cut_segments.cut(viral_segments, project_folder=project_folder, skip_video=skip_cutting)
517
+
518
+ # 5. Workflow Check
519
+ if workflow_choice == "2":
520
+ print(i18n("Cut Only selected. Skipping Face Crop and Subtitles."))
521
+ print(i18n(f"Process completed! Check your results in: {project_folder}"))
522
+ sys.exit(0)
523
+
524
+ # 5. Edit Video (Face Crop)
525
+ if workflow_choice != "3":
526
+ print(i18n("Editing video with {} (Mode: {})...").format(face_model, face_mode))
527
+
528
+ # Parse dead zone safely
529
+ try:
530
+ dead_zone_val = float(args.face_dead_zone)
531
+ except:
532
+ dead_zone_val = 40.0
533
+
534
+ edit_video.edit(
535
+ project_folder=project_folder,
536
+ face_model=face_model,
537
+ face_mode=face_mode,
538
+ detection_period=detection_intervals,
539
+ filter_threshold=args.face_filter_threshold,
540
+ two_face_threshold=args.face_two_threshold,
541
+ confidence_threshold=args.face_confidence_threshold,
542
+ dead_zone=dead_zone_val,
543
+ focus_active_speaker=args.focus_active_speaker,
544
+ active_speaker_mar=args.active_speaker_mar,
545
+ active_speaker_score_diff=args.active_speaker_score_diff,
546
+ include_motion=args.include_motion,
547
+ active_speaker_motion_deadzone=args.active_speaker_motion_threshold,
548
+ active_speaker_motion_sensitivity=args.active_speaker_motion_sensitivity,
549
+ active_speaker_decay=args.active_speaker_decay,
550
+ segments_data=viral_segments.get("segments", []) if viral_segments else None
551
+ )
552
+
553
+
554
+ else:
555
+ print(i18n("Workflow 3: Skipping Face Crop."))
556
+ # Rename existing files if viral_segments available (since edit_video didn't run)
557
+ if viral_segments and "segments" in viral_segments:
558
+ segments_data = viral_segments.get("segments", [])
559
+ final_folder = os.path.join(project_folder, "final")
560
+ subs_folder = os.path.join(project_folder, "subs")
561
+
562
+ print("Renaming existing files with titles...")
563
+ for idx, segment in enumerate(segments_data):
564
+ title = segment.get("title", f"Segment_{idx}")
565
+ safe_title = "".join([c for c in title if c.isalnum() or c in " _-"]).strip()
566
+ safe_title = safe_title.replace(" ", "_")[:60]
567
+
568
+ new_base_name = f"{idx:03d}_{safe_title}"
569
+
570
+ # 1. MP4
571
+ old_mp4_name = f"final-output{idx:03d}_processed.mp4"
572
+ old_mp4_path = os.path.join(final_folder, old_mp4_name)
573
+ new_mp4_path = os.path.join(final_folder, f"{new_base_name}.mp4")
574
+ if os.path.exists(old_mp4_path) and not os.path.exists(new_mp4_path):
575
+ os.rename(old_mp4_path, new_mp4_path)
576
+ print(f"Renamed (Workflow 3): {old_mp4_name} -> {new_base_name}.mp4")
577
+
578
+ # 2. JSON Sub
579
+ old_json_name = f"final-output{idx:03d}_processed.json"
580
+ old_json_path = os.path.join(subs_folder, old_json_name)
581
+ new_json_path = os.path.join(subs_folder, f"{new_base_name}_processed.json")
582
+ if os.path.exists(old_json_path) and not os.path.exists(new_json_path):
583
+ os.rename(old_json_path, new_json_path)
584
+ print(f"Renamed (Workflow 3): {old_json_name} -> {new_base_name}_processed.json")
585
+
586
+ # 3. Timeline
587
+ old_tl_name = f"temp_video_no_audio_{idx}_timeline.json"
588
+ old_tl_path = os.path.join(final_folder, old_tl_name)
589
+ new_tl_path = os.path.join(final_folder, f"{new_base_name}_timeline.json")
590
+ if os.path.exists(old_tl_path) and not os.path.exists(new_tl_path):
591
+ os.rename(old_tl_path, new_tl_path)
592
+ print(f"Renamed (Workflow 3): {old_tl_name} -> {new_base_name}_timeline.json")
593
+
594
+ # 6. Subtitles
595
+ burn_subtitles_option = True
596
+ if burn_subtitles_option:
597
+ print(i18n("Processing subtitles..."))
598
+ # transcribe_cuts removido: JSON de legenda já é gerado no corte
599
+ # transcribe_cuts.transcribe(project_folder=project_folder)
600
+
601
+ # --- Translation Integration ---
602
+ if args.translate_target and args.translate_target.lower() != "none":
603
+ print(i18n("Translating subtitles to: {}").format(args.translate_target))
604
+ import asyncio
605
+ try:
606
+ asyncio.run(translate_json.translate_project_subs(project_folder, args.translate_target))
607
+ except Exception as e:
608
+ print(i18n("Translation failed: {}").format(e))
609
+ # -------------------------------
610
+
611
+ sub_config = get_subtitle_config(args.subtitle_config)
612
+
613
+
614
+
615
+ # Passa o dicionário desempacotado como argumentos, mais o project_folder
616
+ try:
617
+ adjust_subtitles.adjust(project_folder=project_folder, **sub_config)
618
+ burn_subtitles.burn(project_folder=project_folder)
619
+ except FileNotFoundError as fnf_error:
620
+ print(i18n("\n[ERROR] Subtitle processing failed: {}").format(str(fnf_error)))
621
+ print(i18n("Tip: If you are using Workflow 3 (Subtitles Only), ensure the 'subs' folder exists and contains valid JSON files."))
622
+ sys.exit(1)
623
+ except Exception as e:
624
+ print(i18n("\n[ERROR] Unexpected error during subtitle processing: {}").format(str(e)))
625
+ raise e
626
+ else:
627
+ print(i18n("Subtitle burning skipped."))
628
+
629
+ # Organização Final (Opcional, pois agora já está tudo em project_folder)
630
+ # organize_output.organize(project_folder=project_folder)
631
+
632
+ # --- Save Processing Configuration ---
633
+ try:
634
+ # Determine AI Model used
635
+ used_ai_model = args.ai_model_name
636
+ if not used_ai_model and ai_backend != "manual":
637
+ if ai_backend == "gemini":
638
+ used_ai_model = api_config.get("gemini", {}).get("model", "default")
639
+ elif ai_backend == "g4f":
640
+ used_ai_model = api_config.get("g4f", {}).get("model", "default")
641
+
642
+ # Ensure sub_config exists
643
+ current_sub_config = sub_config if 'sub_config' in locals() else get_subtitle_config(args.subtitle_config)
644
+
645
+ final_config = {
646
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
647
+ "workflow": workflow_choice,
648
+ "ai_config": {
649
+ "backend": ai_backend,
650
+ "model_name": used_ai_model,
651
+ "viral_mode": viral_mode,
652
+ "themes": themes,
653
+ "num_segments": num_segments,
654
+ "chunk_size": args.chunk_size
655
+ },
656
+ "face_config": {
657
+ "model": face_model,
658
+ "mode": face_mode,
659
+ "detect_interval": args.face_detect_interval,
660
+ "filter_threshold": args.face_filter_threshold,
661
+ "two_face_threshold": args.face_two_threshold,
662
+ "confidence_threshold": args.face_confidence_threshold,
663
+ "dead_zone": args.face_dead_zone,
664
+ "focus_active_speaker": args.focus_active_speaker,
665
+ "active_speaker_mar": args.active_speaker_mar,
666
+ "active_speaker_score_diff": args.active_speaker_score_diff,
667
+ "include_motion": args.include_motion
668
+ },
669
+ "video_config": {
670
+ "min_duration": args.min_duration,
671
+ "max_duration": args.max_duration,
672
+ "whisper_model": args.model
673
+ },
674
+ "subtitle_config": current_sub_config
675
+ }
676
+
677
+ config_save_path = os.path.join(project_folder, "process_config.json")
678
+ with open(config_save_path, "w", encoding="utf-8") as f:
679
+ json.dump(final_config, f, indent=4, ensure_ascii=False)
680
+ print(i18n("Configuration saved to: {}").format(config_save_path))
681
+
682
+ except Exception as e:
683
+ print(i18n("Error saving configuration JSON: {}").format(e))
684
+ # -------------------------------------
685
+
686
+ print(i18n("Process completed! Check your results in: {}").format(project_folder))
687
+
688
+ except Exception as e:
689
+ print(i18n("\nAn error occurred: {}").format(str(e)))
690
+ import traceback
691
+ traceback.print_exc()
692
+ sys.exit(1)
693
+
694
+ if __name__ == "__main__":
695
+ main()
models/.local-model-here ADDED
File without changes
prompt.txt ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ You are a World-Class Viral Video Editor and Storyteller. You are an expert at finding "gold nuggets" in long transcripts that can stand alone as viral TikToks/Reels/Shorts.
3
+
4
+ Your goal is to extract segments that have **Perfect Narrative Completeness** (Start, Middle, End) while respecting time constraints.
5
+
6
+ ### INPUT FORMAT EXPLAINED
7
+ The transcript below is a continuous text stream with embedded **Time Tags** like `(12s)`.
8
+ - Example: `"Hello world (0s). Today we are going to (3s) fly to the moon."`
9
+ - These tags represent the approximate timestamp of the *preceding* text.
10
+ - Use them to calculate duration. Duration = (End Tag - Start Tag).
11
+
12
+ ### STRICT VIRAL RULES (The "ViralCutter Standard"):
13
+ 1. **NO "GARBAGE" STARTS:**
14
+ - NEVER start with filler words ("Um", "Uh", "So...", "And then").
15
+ - NEVER start with low-energy intros ("Hi guys, welcome back").
16
+ - **ALWAYS** start with a Hook: A strong statement, a question, or an action verb.
17
+ - *Bad Start:* "(10s) So, I was thinking..."
18
+ - *Good Start:* "(12s) I almost died yesterday."
19
+
20
+ 2. **THE "STANDALONE" TEST:**
21
+ - Can this segment be shown to a stranger without ANY context?
22
+ - If it contains "That's why *he* said that" (and "he" is unknown), it FAILS. Eliminate or expand context.
23
+
24
+ 3. **NARRATIVE ARC:**
25
+ - **Start:** Hook (0-3s).
26
+ - **Middle:** Value/Story (Retain attention).
27
+ - **End:** Punchline/Conclusion (Satisfying ending). NEVER cut mid-sentence.
28
+
29
+ 4. **DURATION MATH:**
30
+ - Use the `(XXs)` tags to estimate duration.
31
+ - Target: {min_duration}s to {max_duration}s.
32
+
33
+ ### YOUR TASK:
34
+ Analyze the transcript below. Find {amount} potential viral segments.
35
+
36
+ TRANSCRIPT:
37
+ {transcript_chunk}
38
+
39
+ ### OUTPUT FORMAT (JSON ONLY):
40
+ {json_template}
41
+
42
+ Return ONLY VALID JSON. No markdown, no commentary.
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ g4f[all]
2
+ yt-dlp
3
+ ffmpeg-python
4
+ whisperx
5
+ mediapipe
6
+ google-genai
7
+ insightface
8
+ onnxruntime-gpu
9
+ gradio
10
+ opencv-python
11
+ numpy
12
+ psutil
13
+ fastapi
14
+ uvicorn
15
+ torch
16
+ deep-translator
17
+ tqdm
18
+
19
+
20
+
21
+ # Local LLM Support with CUDA 12.4
22
+ --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cu124
23
+ llama-cpp-python
run.bat ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ setlocal
3
+ title ViralCutter
4
+
5
+ cd /d "%~dp0"
6
+ call .venv\Scripts\activate.bat
7
+ python main_improved.py
8
+ echo.
9
+ pause
run_webui.bat ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ @echo off
2
+ call .venv\Scripts\activate.bat
3
+ python webui\app.py
4
+ pause
scripts/__pycache__/adjust_subtitles.cpython-310.pyc ADDED
Binary file (5.29 kB). View file
 
scripts/__pycache__/adjust_subtitles.cpython-311.pyc ADDED
Binary file (13.1 kB). View file
 
scripts/__pycache__/burn_subtitles.cpython-310.pyc ADDED
Binary file (1.72 kB). View file
 
scripts/__pycache__/burn_subtitles.cpython-311.pyc ADDED
Binary file (5.22 kB). View file
 
scripts/__pycache__/create_viral_segments.cpython-310.pyc ADDED
Binary file (7.59 kB). View file
 
scripts/__pycache__/create_viral_segments.cpython-311.pyc ADDED
Binary file (30.9 kB). View file
 
scripts/__pycache__/cut_json.cpython-310.pyc ADDED
Binary file (1.66 kB). View file
 
scripts/__pycache__/cut_json.cpython-311.pyc ADDED
Binary file (3.34 kB). View file
 
scripts/__pycache__/cut_segments.cpython-310.pyc ADDED
Binary file (3.3 kB). View file
 
scripts/__pycache__/cut_segments.cpython-311.pyc ADDED
Binary file (7.34 kB). View file
 
scripts/__pycache__/download_video.cpython-310.pyc ADDED
Binary file (2.12 kB). View file
 
scripts/__pycache__/download_video.cpython-311.pyc ADDED
Binary file (13.3 kB). View file
 
scripts/__pycache__/edit_video.cpython-310.pyc ADDED
Binary file (14.4 kB). View file
 
scripts/__pycache__/edit_video.cpython-311.pyc ADDED
Binary file (50.4 kB). View file
 
scripts/__pycache__/face_detection_insightface.cpython-310.pyc ADDED
Binary file (2.73 kB). View file
 
scripts/__pycache__/face_detection_insightface.cpython-311.pyc ADDED
Binary file (5.59 kB). View file
 
scripts/__pycache__/generate_premiere_xml.cpython-311.pyc ADDED
Binary file (10.6 kB). View file
 
scripts/__pycache__/one_face.cpython-310.pyc ADDED
Binary file (2.79 kB). View file
 
scripts/__pycache__/one_face.cpython-311.pyc ADDED
Binary file (5.7 kB). View file
 
scripts/__pycache__/organize_output.cpython-310.pyc ADDED
Binary file (2.35 kB). View file
 
scripts/__pycache__/organize_output.cpython-311.pyc ADDED
Binary file (5.1 kB). View file
 
scripts/__pycache__/save_json.cpython-310.pyc ADDED
Binary file (1.23 kB). View file
 
scripts/__pycache__/save_json.cpython-311.pyc ADDED
Binary file (2.4 kB). View file
 
scripts/__pycache__/transcribe_cuts.cpython-310.pyc ADDED
Binary file (1.79 kB). View file
 
scripts/__pycache__/transcribe_cuts.cpython-311.pyc ADDED
Binary file (3.54 kB). View file
 
scripts/__pycache__/transcribe_video.cpython-310.pyc ADDED
Binary file (3.93 kB). View file
 
scripts/__pycache__/transcribe_video.cpython-311.pyc ADDED
Binary file (15.3 kB). View file