bened commited on
Commit
81e01b6
·
verified ·
1 Parent(s): 3dca3c4

Upload 131 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. .github/ISSUE_TEMPLATE/bug_report.yml +70 -0
  2. .github/ISSUE_TEMPLATE/config.yml +7 -0
  3. .github/workflows/update-build-number.yml +37 -0
  4. LICENSE +340 -0
  5. docker-compose.md +200 -0
  6. docs/adding_routes.md +81 -0
  7. docs/audio/concatenate.md +207 -0
  8. docs/cloud-installation/do.md +109 -0
  9. docs/cloud-installation/gcp.md +140 -0
  10. docs/code/execute/execute_python.md +174 -0
  11. docs/ffmpeg/ffmpeg_compose.md +245 -0
  12. docs/image/convert/image_to_video.md +159 -0
  13. docs/image/screenshot_webpage.md +218 -0
  14. docs/media/convert/media_convert.md +138 -0
  15. docs/media/convert/media_to_mp3.md +142 -0
  16. docs/media/cut.md +169 -0
  17. docs/media/download.md +317 -0
  18. docs/media/feedback.md +44 -0
  19. docs/media/generate_ass.md +350 -0
  20. docs/media/media_transcribe.md +270 -0
  21. docs/media/metadata.md +156 -0
  22. docs/media/silence.md +176 -0
  23. docs/s3/upload.md +66 -0
  24. docs/toolkit/authenticate.md +92 -0
  25. docs/toolkit/job_status.md +141 -0
  26. docs/toolkit/jobs_status.md +141 -0
  27. docs/toolkit/test.md +89 -0
  28. docs/video/caption_video.md +354 -0
  29. docs/video/concatenate.md +180 -0
  30. docs/video/cut.md +197 -0
  31. docs/video/split.md +173 -0
  32. docs/video/thumbnail.md +165 -0
  33. docs/video/trim.md +147 -0
  34. routes/audio_mixing.py +73 -0
  35. routes/authenticate.py +35 -0
  36. routes/caption_video.py +92 -0
  37. routes/combine_videos.py +70 -0
  38. routes/extract_keyframes.py +66 -0
  39. routes/gdrive_upload.py +265 -0
  40. routes/image_to_video.py +72 -0
  41. routes/media_to_mp3.py +64 -0
  42. routes/transcribe_media.py +68 -0
  43. routes/v1/audio/concatenate.py +57 -0
  44. routes/v1/code/execute/execute_python.py +140 -0
  45. routes/v1/ffmpeg/ffmpeg_compose.py +149 -0
  46. routes/v1/image/convert/image_to_video.py +73 -0
  47. routes/v1/image/screenshot_webpage.py +125 -0
  48. routes/v1/media/convert/media_convert.py +81 -0
  49. routes/v1/media/convert/media_to_mp3.py +67 -0
  50. routes/v1/media/download.py +230 -0
.github/ISSUE_TEMPLATE/bug_report.yml ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Bug Report
2
+ title: "[Bug]: "
3
+ labels: ["bug"]
4
+ description: Report broken or incorrect behaviour
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: >
9
+ Thanks for taking the time to fill out a bug.
10
+ Please note that this form is for bugs only!
11
+ - type: textarea
12
+ id: what-happened
13
+ attributes:
14
+ label: Describe the bug
15
+ description: A clear and concise description of what the bug is.
16
+ validations:
17
+ required: true
18
+ - type: textarea
19
+ attributes:
20
+ label: Reproduction Steps
21
+ description: >
22
+ What you did to make it happen. Include any request payloads, API calls, or specific commands used.
23
+ validations:
24
+ required: true
25
+ - type: textarea
26
+ attributes:
27
+ label: Expected behavior
28
+ description: >
29
+ A clear and concise description of what you expected to happen.
30
+ validations:
31
+ required: false
32
+ - type: textarea
33
+ attributes:
34
+ label: Screenshots and relevant files
35
+ description: >
36
+ If applicable, add screenshots or relevant files to help explain your problem.
37
+ validations:
38
+ required: false
39
+ - type: dropdown
40
+ id: platform
41
+ attributes:
42
+ label: "Platform"
43
+ description: "Select the platform where you encountered this bug"
44
+ options:
45
+ - "Google Cloud Platform"
46
+ - "Digital Ocean"
47
+ - "Local"
48
+ - "Other"
49
+ validations:
50
+ required: true
51
+ - type: dropdown
52
+ id: assign
53
+ attributes:
54
+ label: "Would you like to work on this issue?"
55
+ options:
56
+ - "Yes"
57
+ - type: checkboxes
58
+ attributes:
59
+ label: Checklist
60
+ description: >
61
+ Let's make sure you've properly done due diligence when reporting this issue!
62
+ options:
63
+ - label: I have searched the open issues for duplicates.
64
+ required: true
65
+ - label: I have shown the entire traceback, if possible.
66
+ required: true
67
+ - type: textarea
68
+ attributes:
69
+ label: Additional Context
70
+ description: Add any other context about the problem here.
.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ contact_links:
2
+ - name: Help or Questions?
3
+ url: https://www.skool.com/no-code-architects/about?ref=2f302c52a77541efa2dd5e8b27f3f8c9
4
+ about: Get courses, community, support daily calls and more.
5
+
6
+ blank_issues_enabled: true
7
+ # Allow blank issues for now?
.github/workflows/update-build-number.yml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Update Build Number
2
+ on:
3
+ push:
4
+ branches:
5
+ - build
6
+ permissions:
7
+ contents: write
8
+ jobs:
9
+ update-build-number:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Checkout repository
13
+ uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0
16
+ token: ${{ secrets.ACTION_BYPASS }}
17
+
18
+ - name: Update build number
19
+ run: |
20
+ if [ ! -f build_number.txt ]; then
21
+ echo "0" > build_number.txt
22
+ fi
23
+ build_number=$(( $(cat build_number.txt) + 1 ))
24
+ echo $build_number > build_number.txt
25
+
26
+ # Update the version in your code file (e.g., version.py)
27
+ echo "BUILD_NUMBER = $build_number" > version.py
28
+
29
+ git config user.name github-actions
30
+ git config user.email github-actions@github.com
31
+ git add build_number.txt version.py
32
+ git commit -m "Build $build_number: Bump build number [skip ci]"
33
+ git push origin build
34
+
35
+ - name: Push changes to testing branch
36
+ run: |
37
+ git push origin build:testing --force
LICENSE ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2025 Stephen G. Pope
2
+
3
+ GNU GENERAL PUBLIC LICENSE
4
+ Version 2, June 1991
5
+
6
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
7
+ <https://fsf.org/>
8
+ Everyone is permitted to copy and distribute verbatim copies
9
+ of this license document, but changing it is not allowed.
10
+
11
+ Preamble
12
+
13
+ The licenses for most software are designed to take away your
14
+ freedom to share and change it. By contrast, the GNU General Public
15
+ License is intended to guarantee your freedom to share and change free
16
+ software--to make sure the software is free for all its users. This
17
+ General Public License applies to most of the Free Software
18
+ Foundation's software and to any other program whose authors commit to
19
+ using it. (Some other Free Software Foundation software is covered by
20
+ the GNU Lesser General Public License instead.) You can apply it to
21
+ your programs, too.
22
+
23
+ When we speak of free software, we are referring to freedom, not
24
+ price. Our General Public Licenses are designed to make sure that you
25
+ have the freedom to distribute copies of free software (and charge for
26
+ this service if you wish), that you receive source code or can get it
27
+ if you want it, that you can change the software or use pieces of it
28
+ in new free programs; and that you know you can do these things.
29
+
30
+ To protect your rights, we need to make restrictions that forbid
31
+ anyone to deny you these rights or to ask you to surrender the rights.
32
+ These restrictions translate to certain responsibilities for you if you
33
+ distribute copies of the software, or if you modify it.
34
+
35
+ For example, if you distribute copies of such a program, whether
36
+ gratis or for a fee, you must give the recipients all the rights that
37
+ you have. You must make sure that they, too, receive or can get the
38
+ source code. And you must show them these terms so they know their
39
+ rights.
40
+
41
+ We protect your rights with two steps: (1) copyright the software, and
42
+ (2) offer you this license which gives you legal permission to copy,
43
+ distribute and/or modify the software.
44
+
45
+ Also, for each author's protection and ours, we want to make certain
46
+ that everyone understands that there is no warranty for this free
47
+ software. If the software is modified by someone else and passed on, we
48
+ want its recipients to know that what they have is not the original, so
49
+ that any problems introduced by others will not reflect on the original
50
+ authors' reputations.
51
+
52
+ Finally, any free program is threatened constantly by software
53
+ patents. We wish to avoid the danger that redistributors of a free
54
+ program will individually obtain patent licenses, in effect making the
55
+ program proprietary. To prevent this, we have made it clear that any
56
+ patent must be licensed for everyone's free use or not licensed at all.
57
+
58
+ The precise terms and conditions for copying, distribution and
59
+ modification follow.
60
+
61
+ GNU GENERAL PUBLIC LICENSE
62
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
63
+
64
+ 0. This License applies to any program or other work which contains
65
+ a notice placed by the copyright holder saying it may be distributed
66
+ under the terms of this General Public License. The "Program", below,
67
+ refers to any such program or work, and a "work based on the Program"
68
+ means either the Program or any derivative work under copyright law:
69
+ that is to say, a work containing the Program or a portion of it,
70
+ either verbatim or with modifications and/or translated into another
71
+ language. (Hereinafter, translation is included without limitation in
72
+ the term "modification".) Each licensee is addressed as "you".
73
+
74
+ Activities other than copying, distribution and modification are not
75
+ covered by this License; they are outside its scope. The act of
76
+ running the Program is not restricted, and the output from the Program
77
+ is covered only if its contents constitute a work based on the
78
+ Program (independent of having been made by running the Program).
79
+ Whether that is true depends on what the Program does.
80
+
81
+ 1. You may copy and distribute verbatim copies of the Program's
82
+ source code as you receive it, in any medium, provided that you
83
+ conspicuously and appropriately publish on each copy an appropriate
84
+ copyright notice and disclaimer of warranty; keep intact all the
85
+ notices that refer to this License and to the absence of any warranty;
86
+ and give any other recipients of the Program a copy of this License
87
+ along with the Program.
88
+
89
+ You may charge a fee for the physical act of transferring a copy, and
90
+ you may at your option offer warranty protection in exchange for a fee.
91
+
92
+ 2. You may modify your copy or copies of the Program or any portion
93
+ of it, thus forming a work based on the Program, and copy and
94
+ distribute such modifications or work under the terms of Section 1
95
+ above, provided that you also meet all of these conditions:
96
+
97
+ a) You must cause the modified files to carry prominent notices
98
+ stating that you changed the files and the date of any change.
99
+
100
+ b) You must cause any work that you distribute or publish, that in
101
+ whole or in part contains or is derived from the Program or any
102
+ part thereof, to be licensed as a whole at no charge to all third
103
+ parties under the terms of this License.
104
+
105
+ c) If the modified program normally reads commands interactively
106
+ when run, you must cause it, when started running for such
107
+ interactive use in the most ordinary way, to print or display an
108
+ announcement including an appropriate copyright notice and a
109
+ notice that there is no warranty (or else, saying that you provide
110
+ a warranty) and that users may redistribute the program under
111
+ these conditions, and telling the user how to view a copy of this
112
+ License. (Exception: if the Program itself is interactive but
113
+ does not normally print such an announcement, your work based on
114
+ the Program is not required to print an announcement.)
115
+
116
+ These requirements apply to the modified work as a whole. If
117
+ identifiable sections of that work are not derived from the Program,
118
+ and can be reasonably considered independent and separate works in
119
+ themselves, then this License, and its terms, do not apply to those
120
+ sections when you distribute them as separate works. But when you
121
+ distribute the same sections as part of a whole which is a work based
122
+ on the Program, the distribution of the whole must be on the terms of
123
+ this License, whose permissions for other licensees extend to the
124
+ entire whole, and thus to each and every part regardless of who wrote it.
125
+
126
+ Thus, it is not the intent of this section to claim rights or contest
127
+ your rights to work written entirely by you; rather, the intent is to
128
+ exercise the right to control the distribution of derivative or
129
+ collective works based on the Program.
130
+
131
+ In addition, mere aggregation of another work not based on the Program
132
+ with the Program (or with a work based on the Program) on a volume of
133
+ a storage or distribution medium does not bring the other work under
134
+ the scope of this License.
135
+
136
+ 3. You may copy and distribute the Program (or a work based on it,
137
+ under Section 2) in object code or executable form under the terms of
138
+ Sections 1 and 2 above provided that you also do one of the following:
139
+
140
+ a) Accompany it with the complete corresponding machine-readable
141
+ source code, which must be distributed under the terms of Sections
142
+ 1 and 2 above on a medium customarily used for software interchange; or,
143
+
144
+ b) Accompany it with a written offer, valid for at least three
145
+ years, to give any third party, for a charge no more than your
146
+ cost of physically performing source distribution, a complete
147
+ machine-readable copy of the corresponding source code, to be
148
+ distributed under the terms of Sections 1 and 2 above on a medium
149
+ customarily used for software interchange; or,
150
+
151
+ c) Accompany it with the information you received as to the offer
152
+ to distribute corresponding source code. (This alternative is
153
+ allowed only for noncommercial distribution and only if you
154
+ received the program in object code or executable form with such
155
+ an offer, in accord with Subsection b above.)
156
+
157
+ The source code for a work means the preferred form of the work for
158
+ making modifications to it. For an executable work, complete source
159
+ code means all the source code for all modules it contains, plus any
160
+ associated interface definition files, plus the scripts used to
161
+ control compilation and installation of the executable. However, as a
162
+ special exception, the source code distributed need not include
163
+ anything that is normally distributed (in either source or binary
164
+ form) with the major components (compiler, kernel, and so on) of the
165
+ operating system on which the executable runs, unless that component
166
+ itself accompanies the executable.
167
+
168
+ If distribution of executable or object code is made by offering
169
+ access to copy from a designated place, then offering equivalent
170
+ access to copy the source code from the same place counts as
171
+ distribution of the source code, even though third parties are not
172
+ compelled to copy the source along with the object code.
173
+
174
+ 4. You may not copy, modify, sublicense, or distribute the Program
175
+ except as expressly provided under this License. Any attempt
176
+ otherwise to copy, modify, sublicense or distribute the Program is
177
+ void, and will automatically terminate your rights under this License.
178
+ However, parties who have received copies, or rights, from you under
179
+ this License will not have their licenses terminated so long as such
180
+ parties remain in full compliance.
181
+
182
+ 5. You are not required to accept this License, since you have not
183
+ signed it. However, nothing else grants you permission to modify or
184
+ distribute the Program or its derivative works. These actions are
185
+ prohibited by law if you do not accept this License. Therefore, by
186
+ modifying or distributing the Program (or any work based on the
187
+ Program), you indicate your acceptance of this License to do so, and
188
+ all its terms and conditions for copying, distributing or modifying
189
+ the Program or works based on it.
190
+
191
+ 6. Each time you redistribute the Program (or any work based on the
192
+ Program), the recipient automatically receives a license from the
193
+ original licensor to copy, distribute or modify the Program subject to
194
+ these terms and conditions. You may not impose any further
195
+ restrictions on the recipients' exercise of the rights granted herein.
196
+ You are not responsible for enforcing compliance by third parties to
197
+ this License.
198
+
199
+ 7. If, as a consequence of a court judgment or allegation of patent
200
+ infringement or for any other reason (not limited to patent issues),
201
+ conditions are imposed on you (whether by court order, agreement or
202
+ otherwise) that contradict the conditions of this License, they do not
203
+ excuse you from the conditions of this License. If you cannot
204
+ distribute so as to satisfy simultaneously your obligations under this
205
+ License and any other pertinent obligations, then as a consequence you
206
+ may not distribute the Program at all. For example, if a patent
207
+ license would not permit royalty-free redistribution of the Program by
208
+ all those who receive copies directly or indirectly through you, then
209
+ the only way you could satisfy both it and this License would be to
210
+ refrain entirely from distribution of the Program.
211
+
212
+ If any portion of this section is held invalid or unenforceable under
213
+ any particular circumstance, the balance of the section is intended to
214
+ apply and the section as a whole is intended to apply in other
215
+ circumstances.
216
+
217
+ It is not the purpose of this section to induce you to infringe any
218
+ patents or other property right claims or to contest validity of any
219
+ such claims; this section has the sole purpose of protecting the
220
+ integrity of the free software distribution system, which is
221
+ implemented by public license practices. Many people have made
222
+ generous contributions to the wide range of software distributed
223
+ through that system in reliance on consistent application of that
224
+ system; it is up to the author/donor to decide if he or she is willing
225
+ to distribute software through any other system and a licensee cannot
226
+ impose that choice.
227
+
228
+ This section is intended to make thoroughly clear what is believed to
229
+ be a consequence of the rest of this License.
230
+
231
+ 8. If the distribution and/or use of the Program is restricted in
232
+ certain countries either by patents or by copyrighted interfaces, the
233
+ original copyright holder who places the Program under this License
234
+ may add an explicit geographical distribution limitation excluding
235
+ those countries, so that distribution is permitted only in or among
236
+ countries not thus excluded. In such case, this License incorporates
237
+ the limitation as if written in the body of this License.
238
+
239
+ 9. The Free Software Foundation may publish revised and/or new versions
240
+ of the General Public License from time to time. Such new versions will
241
+ be similar in spirit to the present version, but may differ in detail to
242
+ address new problems or concerns.
243
+
244
+ Each version is given a distinguishing version number. If the Program
245
+ specifies a version number of this License which applies to it and "any
246
+ later version", you have the option of following the terms and conditions
247
+ either of that version or of any later version published by the Free
248
+ Software Foundation. If the Program does not specify a version number of
249
+ this License, you may choose any version ever published by the Free Software
250
+ Foundation.
251
+
252
+ 10. If you wish to incorporate parts of the Program into other free
253
+ programs whose distribution conditions are different, write to the author
254
+ to ask for permission. For software which is copyrighted by the Free
255
+ Software Foundation, write to the Free Software Foundation; we sometimes
256
+ make exceptions for this. Our decision will be guided by the two goals
257
+ of preserving the free status of all derivatives of our free software and
258
+ of promoting the sharing and reuse of software generally.
259
+
260
+ NO WARRANTY
261
+
262
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
263
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
264
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
265
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
266
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
267
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
268
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
269
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
270
+ REPAIR OR CORRECTION.
271
+
272
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
273
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
274
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
275
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
276
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
277
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
278
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
279
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
280
+ POSSIBILITY OF SUCH DAMAGES.
281
+
282
+ END OF TERMS AND CONDITIONS
283
+
284
+ How to Apply These Terms to Your New Programs
285
+
286
+ If you develop a new program, and you want it to be of the greatest
287
+ possible use to the public, the best way to achieve this is to make it
288
+ free software which everyone can redistribute and change under these terms.
289
+
290
+ To do so, attach the following notices to the program. It is safest
291
+ to attach them to the start of each source file to most effectively
292
+ convey the exclusion of warranty; and each file should have at least
293
+ the "copyright" line and a pointer to where the full notice is found.
294
+
295
+ <one line to give the program's name and a brief idea of what it does.>
296
+ Copyright (C) <year> <name of author>
297
+
298
+ This program is free software; you can redistribute it and/or modify
299
+ it under the terms of the GNU General Public License as published by
300
+ the Free Software Foundation; either version 2 of the License, or
301
+ (at your option) any later version.
302
+
303
+ This program is distributed in the hope that it will be useful,
304
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
305
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
306
+ GNU General Public License for more details.
307
+
308
+ You should have received a copy of the GNU General Public License along
309
+ with this program; if not, see <https://www.gnu.org/licenses/>.
310
+
311
+ Also add information on how to contact you by electronic and paper mail.
312
+
313
+ If the program is interactive, make it output a short notice like this
314
+ when it starts in an interactive mode:
315
+
316
+ Gnomovision version 69, Copyright (C) year name of author
317
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
318
+ This is free software, and you are welcome to redistribute it
319
+ under certain conditions; type `show c' for details.
320
+
321
+ The hypothetical commands `show w' and `show c' should show the appropriate
322
+ parts of the General Public License. Of course, the commands you use may
323
+ be called something other than `show w' and `show c'; they could even be
324
+ mouse-clicks or menu items--whatever suits your program.
325
+
326
+ You should also get your employer (if you work as a programmer) or your
327
+ school, if any, to sign a "copyright disclaimer" for the program, if
328
+ necessary. Here is a sample; alter the names:
329
+
330
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
331
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
332
+
333
+ <signature of Moe Ghoul>, 1 April 1989
334
+ Moe Ghoul, President of Vice
335
+
336
+ This General Public License does not permit incorporating your program into
337
+ proprietary programs. If your program is a subroutine library, you may
338
+ consider it more useful to permit linking proprietary applications with the
339
+ library. If this is what you want to do, use the GNU Lesser General
340
+ Public License instead of this License.
docker-compose.md ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Install No Code Architect Toolkit with Docker
2
+
3
+ Installation of No Code Architect Toolkit with Docker offers the following advantages:
4
+ - Install No Code Architect Toolkit in a clean environment.
5
+ - Simplify the setup process.
6
+ - Avoid compatibility issues across different operating systems with Docker's consistent environment.
7
+
8
+ > **Info**
9
+ > If your domain/subdomain is already pointed to the server, start at step 2.
10
+ > If you have already installed Docker and Docker-Compose, start at step 3.
11
+
12
+ ---
13
+
14
+ ## 1. DNS Setup
15
+
16
+ Point your domain/subdomain to the server. Add an A record to route the domain/subdomain accordingly:
17
+
18
+ - **Type**: A
19
+ - **Name**: The desired domain/subdomain
20
+ - **IP Address**: `<IP_OF_YOUR_SERVER>`
21
+
22
+ ---
23
+
24
+ ## 2. Install Docker
25
+
26
+ This can vary depending on the Linux distribution used. Below are instructions for Ubuntu:
27
+
28
+ ### Set up Docker's APT Repository
29
+
30
+ ```bash
31
+ # Add Docker's official GPG key:
32
+ sudo apt-get update
33
+ sudo apt-get install ca-certificates curl
34
+ sudo install -m 0755 -d /etc/apt/keyrings
35
+ sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
36
+ sudo chmod a+r /etc/apt/keyrings/docker.asc
37
+
38
+ # Add the repository to APT sources:
39
+ echo \
40
+ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
41
+ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
42
+ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
43
+ sudo apt-get update
44
+ ```
45
+
46
+ ### Install the Docker Packages
47
+
48
+ ```bash
49
+ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
50
+ ```
51
+
52
+ ---
53
+
54
+ ## 3. Create Docker Compose File
55
+
56
+ Create a `docker-compose.yml` file and paste the following configuration:
57
+
58
+ ### With SSL Support
59
+ Enables SSL/TLS for secure, encrypted communications. Ideal for those wanting a hands-off approach to SSL setup.
60
+
61
+ ```yaml
62
+ services:
63
+ traefik:
64
+ image: "traefik"
65
+ restart: unless-stopped
66
+ command:
67
+ - "--api=true"
68
+ - "--api.insecure=true"
69
+ - "--providers.docker=true"
70
+ - "--providers.docker.exposedbydefault=false"
71
+ - "--entrypoints.web.address=:80"
72
+ - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
73
+ - "--entrypoints.web.http.redirections.entrypoint.scheme=https"
74
+ - "--entrypoints.websecure.address=:443"
75
+ - "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
76
+ - "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
77
+ - "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
78
+ ports:
79
+ - "80:80"
80
+ - "443:443"
81
+ volumes:
82
+ - traefik_data:/letsencrypt
83
+ - /var/run/docker.sock:/var/run/docker.sock:ro
84
+ ncat:
85
+ image: stephengpope/no-code-architects-toolkit:latest
86
+ env_file:
87
+ - .env
88
+ labels:
89
+ - traefik.enable=true
90
+ - traefik.http.routers.ncat.rule=Host(`${APP_DOMAIN}`)
91
+ - traefik.http.routers.ncat.tls=true
92
+ - traefik.http.routers.ncat.entrypoints=web,websecure
93
+ - traefik.http.routers.ncat.tls.certresolver=mytlschallenge
94
+ volumes:
95
+ - storage:/var/www/html/storage/app
96
+ - logs:/var/www/html/storage/logs
97
+ restart: unless-stopped
98
+
99
+ volumes:
100
+ traefik_data:
101
+ driver: local
102
+ storage:
103
+ driver: local
104
+ logs:
105
+ driver: local
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 4. Create `.env` File
111
+
112
+ Create an `.env` file and configure it accordingly:
113
+
114
+ ```env
115
+ # The name of your application.
116
+ APP_NAME=NCAToolkit
117
+
118
+ # Debug mode setting. Set to `false` for production environments.
119
+ APP_DEBUG=false
120
+
121
+ # Your app's domain or subdomain, without the 'http://' or 'https://' prefix.
122
+ APP_DOMAIN=example.com
123
+
124
+ # Full application URL is automatically configured; no modification required.
125
+ APP_URL=https://${APP_DOMAIN}
126
+
127
+ # SSL settings
128
+ SSL_EMAIL=user@example.com
129
+
130
+ # API_KEY
131
+ # Purpose: Used for API authentication.
132
+ # Requirement: Mandatory.
133
+ API_KEY=your_api_key_here
134
+
135
+ # s3 Compatible Storage Env Vars
136
+ #
137
+ #S3_ACCESS_KEY=your_access_key
138
+ #S3_SECRET_KEY=your_secret_key
139
+ #S3_ENDPOINT_URL=https://your-endpoint-url
140
+ #S3_REGION=your-region
141
+ #S3_BUCKET_NAME=your-bucket-name
142
+
143
+
144
+ # Google Cloud Storage Env Variables
145
+ #
146
+ # GCP_SA_CREDENTIALS
147
+ # Purpose: The JSON credentials for the GCP Service Account.
148
+ # Requirement: Mandatory if using GCP storage.
149
+ #GCP_SA_CREDENTIALS=/path/to/your/gcp/service_account.json
150
+
151
+ # GCP_BUCKET_NAME
152
+ # Purpose: The name of the GCP storage bucket.
153
+ # Requirement: Mandatory if using GCP storage.
154
+ #GCP_BUCKET_NAME=your_gcp_bucket_name
155
+
156
+ # STORAGE_PATH
157
+ # Purpose: The base path for storage operations.
158
+ # Default: GCP
159
+ # Requirement: Optional.
160
+ #STORAGE_PATH=GCP
161
+
162
+ ```
163
+
164
+ ---
165
+
166
+ ## 5. Start Docker Compose
167
+
168
+ Start No Code Architect Toolkit using the following command:
169
+
170
+ ```bash
171
+ docker compose up -d
172
+ ```
173
+
174
+ To view logs in real time:
175
+
176
+ ```bash
177
+ docker compose logs -f
178
+ ```
179
+
180
+ To stop the containers:
181
+
182
+ ```bash
183
+ docker compose stop
184
+ ```
185
+
186
+ To restart and reload env vars
187
+
188
+ # First update your .env file with the correct values
189
+ # Then run:
190
+
191
+ ```bash
192
+ docker compose up -d --force-recreate ncat
193
+ ```
194
+
195
+ ---
196
+
197
+ ## 6. Done
198
+
199
+ No Code Architect Toolkit is now accessible through the specified APP_URL. For example:
200
+ [https://example.com](https://example.com)
docs/adding_routes.md ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Adding New Routes
2
+
3
+ This document explains how to add new routes to the application using the dynamic route registration system.
4
+
5
+ ## Overview
6
+
7
+ The application now uses a dynamic route registration system that automatically discovers and registers all Flask blueprints in the `routes` directory. This means you no longer need to manually import and register blueprints in `app.py`.
8
+
9
+ ## How to Add a New Route
10
+
11
+ 1. **Create a new route file**
12
+
13
+ Create a new Python file in the appropriate location in the `routes` directory. For a v1 API endpoint, you would typically place it in a subdirectory under `routes/v1/` based on the functionality.
14
+
15
+ For example:
16
+ ```
17
+ routes/v1/email/send_email.py
18
+ ```
19
+
20
+ 2. **Define your Blueprint**
21
+
22
+ In your route file, define a Flask Blueprint with a unique name. Make sure to follow the naming convention:
23
+
24
+ ```python
25
+ # routes/v1/email/send_email.py
26
+ from flask import Blueprint, request
27
+ from services.authentication import authenticate
28
+ from app_utils import queue_task_wrapper
29
+
30
+ v1_email_send_bp = Blueprint('v1_email_send', __name__)
31
+
32
+ @v1_email_send_bp.route('/v1/email/send', methods=['POST'])
33
+ @authenticate
34
+ @queue_task_wrapper(bypass_queue=False)
35
+ def send_email(job_id, data):
36
+ """
37
+ Send an email
38
+
39
+ Args:
40
+ job_id (str): Job ID assigned by queue_task_wrapper
41
+ data (dict): Request data containing email details
42
+
43
+ Returns:
44
+ Tuple of (response_data, endpoint_string, status_code)
45
+ """
46
+ # Your implementation here
47
+ endpoint = "/v1/email/send"
48
+
49
+ # Return response
50
+ return {"message": "Email sent"}, endpoint, 200
51
+ ```
52
+
53
+ 3. **That's it!**
54
+
55
+ No need to modify `app.py`. The blueprint will be automatically discovered and registered when the application starts.
56
+
57
+ ## Naming Conventions
58
+
59
+ When creating new routes, please follow these naming conventions:
60
+
61
+ 1. **Blueprint names**: Use the format `{version}_{category}_{action}_bp`
62
+ - Example: `v1_email_send_bp` for sending emails
63
+
64
+ 2. **Route paths**: Use the format `/{version}/{category}/{action}`
65
+ - Example: `/v1/email/send`
66
+
67
+ 3. **File structure**: Place files in directories that match the route structure
68
+ - Example: `routes/v1/email/send_email.py`
69
+
70
+ ## Testing Your Route
71
+
72
+ After adding your route, restart the application and your new endpoint should be available immediately.
73
+
74
+ ## Troubleshooting
75
+
76
+ If your route isn't being registered:
77
+
78
+ 1. Check logs for any import errors
79
+ 2. Ensure your blueprint variable is defined at the module level
80
+ 3. Verify the blueprint name follows the naming convention
81
+ 4. Make sure your Python file is in the correct directory under `routes/`
docs/audio/concatenate.md ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Audio Concatenation API Endpoint Documentation
2
+
3
+ ## Overview
4
+
5
+ The `/v1/audio/concatenate` endpoint provides functionality to combine multiple audio files into a single audio file. This endpoint is part of the v1 API structure and is registered in the main application through the `v1_audio_concatenate_bp` Blueprint. It leverages the application's queuing system to handle asynchronous processing, which is particularly useful for potentially time-consuming audio processing operations.
6
+
7
+ ## Endpoint
8
+
9
+ - **URL**: `/v1/audio/concatenate`
10
+ - **Method**: `POST`
11
+
12
+ ## Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key`: Required. Your API authentication key.
17
+
18
+ ### Body Parameters
19
+
20
+ | Parameter | Type | Required | Description |
21
+ |-----------|------|----------|-------------|
22
+ | `audio_urls` | Array | Yes | An array of objects, each containing an `audio_url` property pointing to an audio file to be concatenated. Must contain at least one item. |
23
+ | `webhook_url` | String | No | A URL to receive a callback notification when processing is complete. If provided, the request will be processed asynchronously. |
24
+ | `id` | String | No | A custom identifier for tracking the request. |
25
+
26
+ Each object in the `audio_urls` array must have:
27
+ - `audio_url`: String (URI format). The URL of an audio file to be concatenated.
28
+
29
+ ### Example Request
30
+
31
+ ```json
32
+ {
33
+ "audio_urls": [
34
+ { "audio_url": "https://example.com/audio1.mp3" },
35
+ { "audio_url": "https://example.com/audio2.mp3" },
36
+ { "audio_url": "https://example.com/audio3.mp3" }
37
+ ],
38
+ "webhook_url": "https://your-webhook-endpoint.com/callback",
39
+ "id": "custom-request-id-123"
40
+ }
41
+ ```
42
+
43
+ ### Example cURL Command
44
+
45
+ ```bash
46
+ curl -X POST \
47
+ https://api.example.com/v1/audio/concatenate \
48
+ -H 'Content-Type: application/json' \
49
+ -H 'x-api-key: your-api-key-here' \
50
+ -d '{
51
+ "audio_urls": [
52
+ { "audio_url": "https://example.com/audio1.mp3" },
53
+ { "audio_url": "https://example.com/audio2.mp3" }
54
+ ],
55
+ "webhook_url": "https://your-webhook-endpoint.com/callback",
56
+ "id": "custom-request-id-123"
57
+ }'
58
+ ```
59
+
60
+ ## Response
61
+
62
+ ### Synchronous Response (No webhook_url provided)
63
+
64
+ If no `webhook_url` is provided, the request will be processed synchronously and return:
65
+
66
+ ```json
67
+ {
68
+ "code": 200,
69
+ "id": "custom-request-id-123",
70
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
71
+ "response": "https://storage.example.com/combined-audio-file.mp3",
72
+ "message": "success",
73
+ "run_time": 2.345,
74
+ "queue_time": 0,
75
+ "total_time": 2.345,
76
+ "pid": 12345,
77
+ "queue_id": 67890,
78
+ "queue_length": 0,
79
+ "build_number": "1.0.123"
80
+ }
81
+ ```
82
+
83
+ ### Asynchronous Response (webhook_url provided)
84
+
85
+ If a `webhook_url` is provided, the request will be queued for processing and immediately return:
86
+
87
+ ```json
88
+ {
89
+ "code": 202,
90
+ "id": "custom-request-id-123",
91
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
92
+ "message": "processing",
93
+ "pid": 12345,
94
+ "queue_id": 67890,
95
+ "max_queue_length": "unlimited",
96
+ "queue_length": 1,
97
+ "build_number": "1.0.123"
98
+ }
99
+ ```
100
+
101
+ When processing is complete, a webhook will be sent to the provided URL with the following payload:
102
+
103
+ ```json
104
+ {
105
+ "endpoint": "/v1/audio/concatenate",
106
+ "code": 200,
107
+ "id": "custom-request-id-123",
108
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
109
+ "response": "https://storage.example.com/combined-audio-file.mp3",
110
+ "message": "success",
111
+ "pid": 12345,
112
+ "queue_id": 67890,
113
+ "run_time": 3.456,
114
+ "queue_time": 1.234,
115
+ "total_time": 4.690,
116
+ "queue_length": 0,
117
+ "build_number": "1.0.123"
118
+ }
119
+ ```
120
+
121
+ ### Error Responses
122
+
123
+ #### Invalid Request Format (400 Bad Request)
124
+
125
+ ```json
126
+ {
127
+ "code": 400,
128
+ "id": null,
129
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
130
+ "message": "Invalid request: 'audio_urls' is a required property",
131
+ "pid": 12345,
132
+ "queue_id": 67890,
133
+ "queue_length": 0,
134
+ "build_number": "1.0.123"
135
+ }
136
+ ```
137
+
138
+ #### Authentication Error (401 Unauthorized)
139
+
140
+ ```json
141
+ {
142
+ "code": 401,
143
+ "message": "Invalid or missing API key",
144
+ "build_number": "1.0.123"
145
+ }
146
+ ```
147
+
148
+ #### Queue Limit Reached (429 Too Many Requests)
149
+
150
+ ```json
151
+ {
152
+ "code": 429,
153
+ "id": "custom-request-id-123",
154
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
155
+ "message": "MAX_QUEUE_LENGTH (100) reached",
156
+ "pid": 12345,
157
+ "queue_id": 67890,
158
+ "queue_length": 100,
159
+ "build_number": "1.0.123"
160
+ }
161
+ ```
162
+
163
+ #### Processing Error (500 Internal Server Error)
164
+
165
+ ```json
166
+ {
167
+ "code": 500,
168
+ "id": "custom-request-id-123",
169
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
170
+ "message": "Error downloading audio file: Connection refused",
171
+ "pid": 12345,
172
+ "queue_id": 67890,
173
+ "queue_length": 0,
174
+ "build_number": "1.0.123"
175
+ }
176
+ ```
177
+
178
+ ## Error Handling
179
+
180
+ - **Missing Required Parameters**: If `audio_urls` is missing or empty, a 400 Bad Request response will be returned.
181
+ - **Invalid URL Format**: If any `audio_url` is not a valid URI, a 400 Bad Request response will be returned.
182
+ - **Authentication Failure**: If the API key is invalid or missing, a 401 Unauthorized response will be returned.
183
+ - **Queue Limit**: If the queue is full (when MAX_QUEUE_LENGTH is set), a 429 Too Many Requests response will be returned.
184
+ - **Processing Errors**: Any errors during audio download, processing, or upload will result in a 500 Internal Server Error response with details in the message field.
185
+
186
+ ## Usage Notes
187
+
188
+ 1. **Asynchronous Processing**: For long audio files, it's recommended to use the `webhook_url` parameter to process the request asynchronously.
189
+ 2. **File Formats**: The service supports common audio formats. The output will be in a standard format (typically MP3).
190
+ 3. **File Size**: There may be limits on the size of audio files that can be processed. Very large files might cause timeouts or failures.
191
+ 4. **Queue Behavior**: If the system is under heavy load, requests with `webhook_url` will be queued. The MAX_QUEUE_LENGTH environment variable controls the maximum queue size.
192
+
193
+ ## Common Issues
194
+
195
+ 1. **Inaccessible Audio URLs**: Ensure all audio URLs are publicly accessible. Private or authentication-required URLs will cause failures.
196
+ 2. **Incompatible Audio Formats**: Some exotic audio formats might not be supported. Stick to common formats like MP3, WAV, or AAC.
197
+ 3. **Webhook Failures**: If your webhook endpoint is unavailable when the processing completes, you might not receive the completion notification.
198
+ 4. **Timeout Issues**: Very large audio files might cause timeouts during download or processing.
199
+
200
+ ## Best Practices
201
+
202
+ 1. **Use Webhooks for Large Files**: Always use the webhook approach for large audio files or when concatenating many files.
203
+ 2. **Include an ID**: Always include a custom `id` parameter to help track your requests, especially in webhook responses.
204
+ 3. **Error Handling**: Implement robust error handling in your client application to handle various HTTP status codes.
205
+ 4. **Webhook Reliability**: Ensure your webhook endpoint is reliable and can handle retries if necessary.
206
+ 5. **File Preparation**: Pre-process your audio files to ensure they have compatible formats, sample rates, and channel configurations for best results.
207
+ 6. **Queue Monitoring**: Monitor the `queue_length` in responses to understand system load and adjust your request patterns if needed.
docs/cloud-installation/do.md ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Installing on Digital Ocean
2
+
3
+ This guide walks you through deploying the No-Code Architects Toolkit API on Digital Ocean's App Platform.
4
+
5
+ ## Prerequisites
6
+
7
+ - A Digital Ocean account ([Sign up here](https://www.digitalocean.com/))
8
+ - Basic familiarity with Digital Ocean App Platform
9
+ - A credit/debit card for billing (you'll only be charged for what you use)
10
+
11
+ ## Step 1: Create a New Project
12
+
13
+ 1. Sign in to your Digital Ocean account
14
+ 2. Create a new project or select an existing one
15
+ 3. This will organize your resources for the NCA Toolkit
16
+
17
+ ## Step 2: Create a Digital Ocean Space
18
+
19
+ You'll need to create a Space (Digital Ocean's object storage) for the toolkit to store processed files:
20
+
21
+ 1. Navigate to **Spaces Object Storage** in the Digital Ocean dashboard
22
+ 2. Click **Create a Space**
23
+ 3. Select a region (e.g., New York)
24
+ 4. Give your bucket a name (e.g., `nca-toolkit-bucket`)
25
+ 5. Select your project
26
+ 6. Click **Create Space**
27
+
28
+ ## Step 3: Generate API Keys for Your Space
29
+
30
+ 1. From your new Space, go to **Settings**
31
+ 2. Click **Create Access Key**
32
+ 3. Select **Full Access**
33
+ 4. Give your key a name (e.g., `nca-toolkit-key`)
34
+ 5. Click **Create Access Key**
35
+ 6. **IMPORTANT**: Save both the Access Key and Secret Key shown - you will only see them once!
36
+ 7. Also copy the Space URL (endpoint) for use in the next step
37
+
38
+ ## Step 4: Deploy the App
39
+
40
+ 1. From your Digital Ocean dashboard, click **Create** and select **App**
41
+ 2. Choose **Container Image** as the deployment source
42
+ 3. Select **Docker Hub** for the repository
43
+ 4. Enter `stephengpope/no-code-architects-toolkit` as the image name
44
+ 5. Enter `latest` for the image tag
45
+ 6. Click **Next**
46
+ 7. If needed, edit the name to remove any extra dashes (Digital Ocean may show an error for long names)
47
+ 8. Choose **Web Service** as the service type
48
+
49
+ ## Step 5: Configure Resources
50
+
51
+ 1. Select a plan with adequate resources for your needs:
52
+ - For testing, a $50/month instance provides good performance
53
+ - For smaller workloads, you can select a smaller instance
54
+ - Note: You're only charged for the time the server is running
55
+ 2. Set Containers to 1
56
+ 3. Close the resource selection dialog
57
+
58
+ ## Step 6: Configure Environment Variables
59
+
60
+ Add the following environment variables exactly as shown (be careful with underscores vs. dashes and avoid any leading/trailing spaces):
61
+
62
+ 1. `API_KEY`: Your API key (e.g., `test123` for testing - change for production)
63
+ 2. `S3_ENDPOINT_URL`: The URL of your Space (copied from Step 3)
64
+ 3. `S3_ACCESS_KEY`: The access key from Step 3
65
+ 4. `S3_SECRET_KEY`: The secret key from Step 3
66
+ 5. `S3_BUCKET_NAME`: The name of your Space bucket (e.g., `nca-toolkit-bucket`)
67
+ 6. `S3_REGION`: The region code of your Space (e.g., `NYC3` for New York)
68
+
69
+ ## Step 7: Finalize and Deploy
70
+
71
+ 1. For Deployment Region, select a region close to your location (e.g., San Francisco)
72
+ 2. You can use the default app name or choose a custom name
73
+ 3. Click **Create Resource**
74
+ 4. Wait for the deployment to complete (this may take a few minutes)
75
+ - You may need to refresh the page to see updates
76
+
77
+ ## Step 8: Test Your Deployment
78
+
79
+ ### Using Postman
80
+
81
+ 1. Sign up for or log in to [Postman](https://www.postman.com/)
82
+ 2. Import the [NCA Toolkit Postman Collection](https://bit.ly/49Gkh61)
83
+ 3. Fork the collection to your workspace
84
+ 4. Create a new environment:
85
+ - Name it "Digital Ocean" or similar
86
+ - Add a variable `x-api-key` with the value matching your API_KEY (e.g., `test123`)
87
+ - Add a variable `base_url` with the value of your app's URL (shown in the Digital Ocean dashboard)
88
+ - Save the environment
89
+ 5. In the collection, navigate to the `toolkit/authenticate` endpoint and click Send
90
+ 6. If you receive a success response, your deployment is working correctly
91
+ 7. Then test the `toolkit/test` endpoint to verify complete functionality
92
+
93
+ ## Monitoring and Management
94
+
95
+ - **Overview**: View basic information about your app
96
+ - **Insights**: Monitor CPU and memory usage
97
+ - **Runtime Logs**: View logs of API calls and server activity
98
+ - **Console**: Access the server's command line (rarely needed)
99
+ - **Settings**: Modify your app's configuration
100
+
101
+ ## Next Steps
102
+
103
+ Now that you have successfully deployed the NCA Toolkit API, you can:
104
+ - Explore all the available endpoints in the Postman collection
105
+ - Integrate the API with your applications
106
+ - Consider securing your API key with a more complex value
107
+ - Scale your resources up or down based on your usage requirements
108
+
109
+ Remember, Digital Ocean charges based on usage, so you can always delete the app when you're not using it to save costs.
docs/cloud-installation/gcp.md ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Installing on the Google Cloud Platform (GCP)
2
+
3
+ ## 🎥 Video Instructions
4
+
5
+ Watch **[Detailed Video Instructions](https://youtu.be/6bC93sek9v8)** to set up the No-Code Architects Toolkit API.
6
+
7
+ - Use the **Docker Image** below:
8
+
9
+ ```
10
+ stephengpope/no-code-architects-toolkit:latest
11
+ ```
12
+
13
+ ### Video Resources
14
+
15
+ - **[Postman Template](https://bit.ly/49Gkh61)**
16
+ - **[NCA Toolkit API GPT](https://bit.ly/4feDDk4)**
17
+
18
+ Or use the guide below walks you through the steps to install the NCA Toolkit API on GCP.
19
+
20
+ ---
21
+
22
+ ## **Prerequisites**
23
+ - A Google Cloud account. [Sign up here](https://cloud.google.com/) if you don't already have one.
24
+ - New users receive $300 in free credits.
25
+ - Basic knowledge of GCP services such as Cloud Run and Cloud Storage.
26
+ - A terminal or code editor for managing files.
27
+
28
+ ---
29
+
30
+ ## **Step 1: Create a Google Cloud Project**
31
+ 1. Log into the [GCP Console](https://console.cloud.google.com/).
32
+ 2. Click on the **Project Selector** in the top navigation bar and select **New Project**.
33
+ 3. Enter a project name, such as `NCA Toolkit Project`.
34
+ 4. Click **Create**.
35
+
36
+ ---
37
+
38
+ ## **Step 2: Enable Required APIs**
39
+ Enable the following APIs:
40
+ - **Cloud Storage API**
41
+ - **Cloud Storage JSON API**
42
+ - **Cloud Run API**
43
+
44
+ ### **How to Enable APIs:**
45
+ 1. In the GCP Console, navigate to **APIs & Services** > **Enable APIs and Services**.
46
+ 2. Search for each API, click on it, and enable it.
47
+
48
+ ---
49
+
50
+ ## **Step 3: Create a Service Account**
51
+ 1. Navigate to **IAM & Admin** > **Service Accounts** in the GCP Console.
52
+ 2. Click **+ Create Service Account**.
53
+ - Enter a name (e.g., `NCA Toolkit Service Account`).
54
+ 3. Assign the following roles to the service account:
55
+ - **Storage Admin**
56
+ - **Viewer**
57
+ 4. Click **Done** to create the service account.
58
+ 5. Open the service account details and navigate to the **Keys** tab.
59
+ - Click **Add Key** > **Create New Key**.
60
+ - Choose **JSON** format, download the file, and store it securely.
61
+
62
+ ---
63
+
64
+ ## **Step 4: Create a Cloud Storage Bucket**
65
+ 1. Navigate to **Storage** > **Buckets** in the GCP Console.
66
+ 2. Click **+ Create Bucket**.
67
+ - Choose a unique bucket name (e.g., `nca-toolkit-bucket`).
68
+ - Leave default settings, but:
69
+ - Uncheck **Enforce public access prevention**.
70
+ - Set **Access Control** to **Uniform**.
71
+ 3. Click **Create** to finish.
72
+ 4. Go to the bucket permissions, and add **allUsers** as a principal with the role:
73
+ - **Storage Object Viewer**.
74
+ 5. Save changes.
75
+
76
+ ---
77
+
78
+ ## **Step 5: Deploy on Google Cloud Run**
79
+
80
+ ### 1. Navigate to Cloud Run
81
+ - Open the **Cloud Run** service in the **Google Cloud Console**.
82
+
83
+ ### 2. Create a New Service
84
+ - Click **Create Service**.
85
+ - Then **Deploy one revision from Docker Hub using the image below**:
86
+
87
+ ```
88
+ stephengpope/no-code-architects-toolkit:latest
89
+ ```
90
+
91
+ ### 3. Allow Unauthenticated Invocations
92
+ - Check the box to **allow unauthenticated invocations**.
93
+
94
+ ### 4. Configure Resource Allocation
95
+ - Set **Memory**: `16 GB`.
96
+ - Set **CPU**: `4 CPUs`.
97
+ - Set **CPU Allocation**: **Always Allocated**.
98
+
99
+ ### 5. Adjust Scaling Settings
100
+ - **Minimum Instances**: `0` (to minimize cost during idle times).
101
+ - **Maximum Instances**: `5` (adjustable based on expected load).
102
+
103
+ ### 6. Use Second-Generation Servers
104
+ - Scroll to **Platform Version** and select **Second Generation**.
105
+ - Second-generation servers offer better performance and feature support for advanced use cases.
106
+
107
+ ### 7. Add Environment Variables
108
+ - Add the following environment variables:
109
+ - `API_KEY`: Your API key (e.g., `Test123`).
110
+ - `GCP_BUCKET_NAME`: The name of your Cloud Storage bucket.
111
+ - `GCP_SA_CREDENTIALS`: The JSON key of your service account.
112
+ - Paste the **entire contents** of the downloaded JSON key file into this field.
113
+ - Ensure:
114
+ - Proper JSON formatting.
115
+ - No leading or trailing spaces.
116
+
117
+ ### 8. Configure Advanced Settings
118
+ - Set the **Container Port**: Default to `8080`.
119
+ - **Request Timeout**: `300 seconds` (to handle long-running requests).
120
+ - Enable **Startup Boost** to improve performance for the first request after a cold start.
121
+
122
+ ### 9. Deploy the Service
123
+ - Verify all settings and click **Create**.
124
+ - The deployment process might take a few minutes. Once completed, a green checkmark should appear in the Cloud Run dashboard.
125
+
126
+ By following these steps, the NCA Toolkit will be successfully deployed and accessible via Google Cloud Run with second-generation servers for optimal performance.
127
+
128
+ ---
129
+
130
+ ## **Step 6: Test the Deployment**
131
+
132
+ 1. Install **[Postman Template](https://bit.ly/49Gkh61)** on your computer.
133
+ 2. Import the API example requests from the NCA Toolkit GitHub repository.
134
+ 3. Configure two environment variables in Postman:
135
+ - `base_url`: Your deployed Cloud Run service URL.
136
+ - `x-api-key`: The API key you configured in **Step 5**.
137
+ 4. Use the example requests to validate that the API is functioning correctly.
138
+ 5. Use the **[NCA Toolkit API GPT](https://bit.ly/4feDDk4)** to learn more.
139
+
140
+ By following these steps, your NCA Toolkit API should be successfully deployed on Google Cloud Platform.
docs/code/execute/execute_python.md ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Execute Python Code Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/code/execute/python` endpoint allows users to execute Python code on the server. This endpoint is part of the version 1.0 API structure defined in `app.py`. It is designed to provide a secure and controlled environment for executing Python code, with features like input validation, output capturing, and timeout handling.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/code/execute/python`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+
22
+ - `code` (string, required): The Python code to be executed.
23
+ - `timeout` (integer, optional): The maximum execution time in seconds, between 1 and 300. Default is 30 seconds.
24
+ - `webhook_url` (string, optional): The URL to receive the execution result via a webhook.
25
+ - `id` (string, optional): A unique identifier for the request.
26
+
27
+ The `validate_payload` directive in the routes file enforces the following JSON schema for the request body:
28
+
29
+ ```json
30
+ {
31
+ "type": "object",
32
+ "properties": {
33
+ "code": {"type": "string"},
34
+ "timeout": {"type": "integer", "minimum": 1, "maximum": 300},
35
+ "webhook_url": {"type": "string", "format": "uri"},
36
+ "id": {"type": "string"}
37
+ },
38
+ "required": ["code"],
39
+ "additionalProperties": False
40
+ }
41
+ ```
42
+
43
+ ### Example Request
44
+
45
+ **Request Payload:**
46
+
47
+ ```json
48
+ {
49
+ "code": "print('Hello, World!')",
50
+ "timeout": 10,
51
+ "webhook_url": "https://example.com/webhook",
52
+ "id": "unique-request-id"
53
+ }
54
+ ```
55
+
56
+ **cURL Command:**
57
+
58
+ ```bash
59
+ curl -X POST \
60
+ -H "x-api-key: YOUR_API_KEY" \
61
+ -H "Content-Type: application/json" \
62
+ -d '{"code": "print('Hello, World!')", "timeout": 10, "webhook_url": "https://example.com/webhook", "id": "unique-request-id"}' \
63
+ http://your-api-endpoint/v1/code/execute/python
64
+ ```
65
+
66
+ ## 4. Response
67
+
68
+ ### Success Response
69
+
70
+ The success response follows the general response format defined in `app.py`. Here's an example:
71
+
72
+ ```json
73
+ {
74
+ "endpoint": "/v1/code/execute/python",
75
+ "code": 200,
76
+ "id": "unique-request-id",
77
+ "job_id": "generated-job-id",
78
+ "response": {
79
+ "result": null,
80
+ "stdout": "Hello, World!\n",
81
+ "stderr": "",
82
+ "exit_code": 0
83
+ },
84
+ "message": "success",
85
+ "pid": 12345,
86
+ "queue_id": 1234567890,
87
+ "run_time": 0.123,
88
+ "queue_time": 0.0,
89
+ "total_time": 0.123,
90
+ "queue_length": 0,
91
+ "build_number": "1.0.0"
92
+ }
93
+ ```
94
+
95
+ ### Error Responses
96
+
97
+ #### Missing or Invalid Parameters
98
+
99
+ **Status Code:** 400 Bad Request
100
+
101
+ ```json
102
+ {
103
+ "error": "Missing or invalid parameters",
104
+ "stdout": "",
105
+ "exit_code": 400
106
+ }
107
+ ```
108
+
109
+ #### Execution Error
110
+
111
+ **Status Code:** 400 Bad Request
112
+
113
+ ```json
114
+ {
115
+ "error": "Error message from the executed code",
116
+ "stdout": "Output from the executed code",
117
+ "exit_code": 400
118
+ }
119
+ ```
120
+
121
+ #### Execution Timeout
122
+
123
+ **Status Code:** 408 Request Timeout
124
+
125
+ ```json
126
+ {
127
+ "error": "Execution timed out after 10 seconds"
128
+ }
129
+ ```
130
+
131
+ #### Internal Server Error
132
+
133
+ **Status Code:** 500 Internal Server Error
134
+
135
+ ```json
136
+ {
137
+ "error": "An internal server error occurred",
138
+ "stdout": "",
139
+ "stderr": "",
140
+ "exit_code": 500
141
+ }
142
+ ```
143
+
144
+ ## 5. Error Handling
145
+
146
+ The endpoint handles various types of errors, including:
147
+
148
+ - Missing or invalid parameters (400 Bad Request)
149
+ - Execution errors, such as syntax errors or exceptions (400 Bad Request)
150
+ - Execution timeout (408 Request Timeout)
151
+ - Internal server errors (500 Internal Server Error)
152
+
153
+ The main application context (`app.py`) also includes error handling for queue overload (429 Too Many Requests) and other general errors.
154
+
155
+ ## 6. Usage Notes
156
+
157
+ - The executed code runs in a sandboxed environment, with limited access to system resources.
158
+ - The code execution is limited to a maximum of 300 seconds (5 minutes) by default, but this can be adjusted using the `timeout` parameter.
159
+ - The execution result, including stdout, stderr, and the return value, is captured and returned in the response.
160
+ - If a `webhook_url` is provided, the execution result will also be sent to the specified webhook.
161
+
162
+ ## 7. Common Issues
163
+
164
+ - Attempting to execute code that accesses restricted resources or performs disallowed operations may result in an execution error.
165
+ - Long-running or resource-intensive code may trigger the execution timeout.
166
+ - Providing an invalid `webhook_url` will prevent the execution result from being delivered to the specified webhook.
167
+
168
+ ## 8. Best Practices
169
+
170
+ - Always validate and sanitize user input to prevent code injection attacks.
171
+ - Set an appropriate timeout value based on the expected execution time of the code.
172
+ - Monitor the execution logs for any errors or unexpected behavior.
173
+ - Implement rate limiting or queue management to prevent abuse or overload of the endpoint.
174
+ - Consider implementing additional security measures, such as code sandboxing or whitelisting/blacklisting certain operations or modules.
docs/ffmpeg/ffmpeg_compose.md ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FFmpeg Compose API Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/ffmpeg/compose` endpoint is a flexible and powerful API that allows users to compose complex FFmpeg commands by providing input files, filters, and output options. This endpoint is part of the version 1.0 API structure, as shown in the `app.py` file. It is designed to handle various media processing tasks, such as video and audio manipulation, transcoding, and more.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/ffmpeg/compose`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body should be a JSON object with the following properties:
21
+
22
+ - `inputs` (required, array): An array of input file objects, each containing:
23
+ - `file_url` (required, string): The URL of the input file.
24
+ - `options` (optional, array): An array of option objects, each containing:
25
+ - `option` (required, string): The FFmpeg option.
26
+ - `argument` (optional, string, number, or null): The argument for the option.
27
+ - `filters` (optional, array): An array of filter objects, each containing:
28
+ - `filter` (required, string): The FFmpeg filter.
29
+ - `outputs` (required, array): An array of output option objects, each containing:
30
+ - `options` (required, array): An array of option objects, each containing:
31
+ - `option` (required, string): The FFmpeg option.
32
+ - `argument` (optional, string, number, or null): The argument for the option.
33
+ - `global_options` (optional, array): An array of global option objects, each containing:
34
+ - `option` (required, string): The FFmpeg global option.
35
+ - `argument` (optional, string, number, or null): The argument for the option.
36
+ - `metadata` (optional, object): An object specifying which metadata to include in the response, with the following properties:
37
+ - `thumbnail` (optional, boolean): Whether to include a thumbnail for the output file.
38
+ - `filesize` (optional, boolean): Whether to include the file size of the output file.
39
+ - `duration` (optional, boolean): Whether to include the duration of the output file.
40
+ - `bitrate` (optional, boolean): Whether to include the bitrate of the output file.
41
+ - `encoder` (optional, boolean): Whether to include the encoder used for the output file.
42
+ - `webhook_url` (required, string): The URL to send the response webhook.
43
+ - `id` (required, string): A unique identifier for the request.
44
+
45
+ ### Example Request
46
+
47
+ ```json
48
+ {
49
+ "inputs": [
50
+ {
51
+ "file_url": "https://example.com/video1.mp4",
52
+ "options": [
53
+ {
54
+ "option": "-ss",
55
+ "argument": 10
56
+ },
57
+ {
58
+ "option": "-t",
59
+ "argument": 20
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ "file_url": "https://example.com/video2.mp4"
65
+ }
66
+ ],
67
+ "filters": [
68
+ {
69
+ "filter": "hflip"
70
+ }
71
+ ],
72
+ "outputs": [
73
+ {
74
+ "options": [
75
+ {
76
+ "option": "-c:v",
77
+ "argument": "libx264"
78
+ },
79
+ {
80
+ "option": "-crf",
81
+ "argument": 23
82
+ }
83
+ ]
84
+ }
85
+ ],
86
+ "global_options": [
87
+ {
88
+ "option": "-y"
89
+ }
90
+ ],
91
+ "metadata": {
92
+ "thumbnail": true,
93
+ "filesize": true,
94
+ "duration": true,
95
+ "bitrate": true,
96
+ "encoder": true
97
+ },
98
+ "webhook_url": "https://example.com/webhook",
99
+ "id": "unique-request-id"
100
+ }
101
+ ```
102
+
103
+ ```bash
104
+ curl -X POST \
105
+ https://api.example.com/v1/ffmpeg/compose \
106
+ -H 'x-api-key: YOUR_API_KEY' \
107
+ -H 'Content-Type: application/json' \
108
+ -d '{
109
+ "inputs": [
110
+ {
111
+ "file_url": "https://example.com/video1.mp4",
112
+ "options": [
113
+ {
114
+ "option": "-ss",
115
+ "argument": 10
116
+ },
117
+ {
118
+ "option": "-t",
119
+ "argument": 20
120
+ }
121
+ ]
122
+ },
123
+ {
124
+ "file_url": "https://example.com/video2.mp4"
125
+ }
126
+ ],
127
+ "filters": [
128
+ {
129
+ "filter": "hflip"
130
+ }
131
+ ],
132
+ "outputs": [
133
+ {
134
+ "options": [
135
+ {
136
+ "option": "-c:v",
137
+ "argument": "libx264"
138
+ },
139
+ {
140
+ "option": "-crf",
141
+ "argument": 23
142
+ }
143
+ ]
144
+ }
145
+ ],
146
+ "global_options": [
147
+ {
148
+ "option": "-y"
149
+ }
150
+ ],
151
+ "metadata": {
152
+ "thumbnail": true,
153
+ "filesize": true,
154
+ "duration": true,
155
+ "bitrate": true,
156
+ "encoder": true
157
+ },
158
+ "webhook_url": "https://example.com/webhook",
159
+ "id": "unique-request-id"
160
+ }'
161
+ ```
162
+
163
+ ## 4. Response
164
+
165
+ ### Success Response
166
+
167
+ The response will be sent to the specified `webhook_url` as a JSON object with the following properties:
168
+
169
+ - `endpoint` (string): The endpoint URL (`/v1/ffmpeg/compose`).
170
+ - `code` (number): The HTTP status code (200 for success).
171
+ - `id` (string): The unique identifier for the request.
172
+ - `job_id` (string): The unique job ID assigned to the request.
173
+ - `response` (array): An array of output file objects, each containing:
174
+ - `file_url` (string): The URL of the uploaded output file.
175
+ - `thumbnail_url` (string, optional): The URL of the uploaded thumbnail, if requested.
176
+ - `filesize` (number, optional): The file size of the output file, if requested.
177
+ - `duration` (number, optional): The duration of the output file, if requested.
178
+ - `bitrate` (number, optional): The bitrate of the output file, if requested.
179
+ - `encoder` (string, optional): The encoder used for the output file, if requested.
180
+ - `message` (string): The success message ("success").
181
+ - `pid` (number): The process ID of the worker that processed the request.
182
+ - `queue_id` (number): The ID of the queue used for processing the request.
183
+ - `run_time` (number): The time taken to process the request (in seconds).
184
+ - `queue_time` (number): The time the request spent in the queue (in seconds).
185
+ - `total_time` (number): The total time taken to process the request, including queue time (in seconds).
186
+ - `queue_length` (number): The current length of the processing queue.
187
+ - `build_number` (string): The build number of the application.
188
+
189
+ ### Error Responses
190
+
191
+ - **400 Bad Request**: The request payload is invalid or missing required parameters.
192
+ - **401 Unauthorized**: The provided API key is invalid or missing.
193
+ - **429 Too Many Requests**: The maximum queue length has been reached.
194
+ - **500 Internal Server Error**: An unexpected error occurred while processing the request.
195
+
196
+ Example error response:
197
+
198
+ ```json
199
+ {
200
+ "code": 400,
201
+ "id": "unique-request-id",
202
+ "job_id": "job-id",
203
+ "message": "Invalid request payload: 'inputs' is a required property",
204
+ "pid": 123,
205
+ "queue_id": 456,
206
+ "queue_length": 0,
207
+ "build_number": "1.0.0"
208
+ }
209
+ ```
210
+
211
+ ## 5. Error Handling
212
+
213
+ The API handles various types of errors, including:
214
+
215
+ - **Missing or invalid parameters**: If the request payload is missing required parameters or contains invalid data types, a 400 Bad Request error will be returned.
216
+ - **Authentication failure**: If the provided API key is invalid or missing, a 401 Unauthorized error will be returned.
217
+ - **Queue limit reached**: If the maximum queue length is reached, a 429 Too Many Requests error will be returned.
218
+ - **Unexpected errors**: If an unexpected error occurs during request processing, a 500 Internal Server Error will be returned.
219
+
220
+ The main application context (`app.py`) includes error handling for the processing queue. If the maximum queue length is set and the queue size reaches that limit, new requests will be rejected with a 429 Too Many Requests error.
221
+
222
+ ## 6. Usage Notes
223
+
224
+ - The `inputs` array must contain at least one input file object.
225
+ - The `outputs` array must contain at least one output option object.
226
+ - The `filters` array is optional and can be used to apply FFmpeg filters to the input files.
227
+ - The `global_options` array is optional and can be used to specify global FFmpeg options.
228
+ - The `metadata` object is optional and can be used to request specific metadata for the output files.
229
+ - The `webhook_url` parameter is required and specifies the URL where the response should be sent.
230
+ - The `id` parameter is required and should be a unique identifier for the request.
231
+
232
+ ## 7. Common Issues
233
+
234
+ - Providing invalid or malformed input file URLs.
235
+ - Specifying invalid or unsupported FFmpeg options or filters.
236
+ - Reaching the maximum queue length, resulting in a 429 Too Many Requests error.
237
+ - Network or connectivity issues that prevent the response webhook from being delivered.
238
+
239
+ ## 8. Best Practices
240
+
241
+ - Validate input file URLs and ensure they are accessible before sending the request.
242
+ - Test your FFmpeg command locally before using the API to ensure it works as expected.
243
+ - Monitor the queue length and adjust the maximum queue length as needed to prevent overloading the system.
244
+ - Implement retry mechanisms for handling failed webhook deliveries or other transient errors.
245
+ - Use unique and descriptive `id` values for each request to aid in troubleshooting and monitoring.
docs/image/convert/image_to_video.md ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Image to Video Conversion
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/image/convert/video` endpoint is part of the Flask API application and is responsible for converting an image into a video file. This endpoint is registered in the `app.py` file under the `v1_image_convert_video_bp` blueprint, which is imported from the `routes.v1.image.convert.image_to_video` module.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/image/convert/video`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be in JSON format and should include the following parameters:
21
+
22
+ | Parameter | Type | Required | Description |
23
+ |-------------|--------|----------|--------------------------------------------------------------|
24
+ | `image_url` | string | Yes | The URL of the image to be converted into a video. |
25
+ | `length` | number | No | The desired length of the video in seconds (default: 5). |
26
+ | `frame_rate`| integer| No | The frame rate of the output video (default: 30). |
27
+ | `zoom_speed`| number | No | The speed of the zoom effect (0-100, default: 3). |
28
+ | `webhook_url`| string| No | The URL to receive a webhook notification upon completion. |
29
+ | `id` | string | No | An optional identifier for the request. |
30
+
31
+ The `validate_payload` decorator in the `routes.v1.image.convert.image_to_video` module enforces the following JSON schema for the request body:
32
+
33
+ ```json
34
+ {
35
+ "type": "object",
36
+ "properties": {
37
+ "image_url": {"type": "string", "format": "uri"},
38
+ "length": {"type": "number", "minimum": 1, "maximum": 60},
39
+ "frame_rate": {"type": "integer", "minimum": 15, "maximum": 60},
40
+ "zoom_speed": {"type": "number", "minimum": 0, "maximum": 100},
41
+ "webhook_url": {"type": "string", "format": "uri"},
42
+ "id": {"type": "string"}
43
+ },
44
+ "required": ["image_url"],
45
+ "additionalProperties": false
46
+ }
47
+ ```
48
+
49
+ ### Example Request
50
+
51
+ ```json
52
+ {
53
+ "image_url": "https://example.com/image.jpg",
54
+ "length": 10,
55
+ "frame_rate": 24,
56
+ "zoom_speed": 5,
57
+ "webhook_url": "https://example.com/webhook",
58
+ "id": "request-123"
59
+ }
60
+ ```
61
+
62
+ ```bash
63
+ curl -X POST \
64
+ -H "x-api-key: YOUR_API_KEY" \
65
+ -H "Content-Type: application/json" \
66
+ -d '{"image_url": "https://example.com/image.jpg", "length": 10, "frame_rate": 24, "zoom_speed": 5, "webhook_url": "https://example.com/webhook", "id": "request-123"}' \
67
+ http://your-api-endpoint/v1/image/convert/video
68
+ ```
69
+
70
+ ## 4. Response
71
+
72
+ ### Success Response
73
+
74
+ Upon successful processing, the endpoint returns a JSON response with the following structure:
75
+
76
+ ```json
77
+ {
78
+ "code": 200,
79
+ "id": "request-123",
80
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
81
+ "response": "https://cloud-storage.example.com/converted-video.mp4",
82
+ "message": "success",
83
+ "run_time": 2.345,
84
+ "queue_time": 0.123,
85
+ "total_time": 2.468,
86
+ "pid": 12345,
87
+ "queue_id": 1234567890,
88
+ "queue_length": 0,
89
+ "build_number": "1.0.0"
90
+ }
91
+ ```
92
+
93
+ The `response` field contains the URL of the converted video file uploaded to cloud storage.
94
+
95
+ ### Error Responses
96
+
97
+ #### 429 Too Many Requests
98
+
99
+ If the maximum queue length is reached, the endpoint returns a 429 Too Many Requests response:
100
+
101
+ ```json
102
+ {
103
+ "code": 429,
104
+ "id": "request-123",
105
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
106
+ "message": "MAX_QUEUE_LENGTH (10) reached",
107
+ "pid": 12345,
108
+ "queue_id": 1234567890,
109
+ "queue_length": 10,
110
+ "build_number": "1.0.0"
111
+ }
112
+ ```
113
+
114
+ #### 500 Internal Server Error
115
+
116
+ If an exception occurs during the image-to-video conversion process, the endpoint returns a 500 Internal Server Error response:
117
+
118
+ ```json
119
+ {
120
+ "code": 500,
121
+ "id": "request-123",
122
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
123
+ "message": "Error message describing the exception",
124
+ "pid": 12345,
125
+ "queue_id": 1234567890,
126
+ "queue_length": 0,
127
+ "build_number": "1.0.0"
128
+ }
129
+ ```
130
+
131
+ ## 5. Error Handling
132
+
133
+ The endpoint handles the following types of errors:
134
+
135
+ - **Missing or invalid parameters**: If the request body is missing required parameters or contains invalid parameter values, the `validate_payload` decorator will return a 400 Bad Request response with a descriptive error message.
136
+ - **Queue length exceeded**: If the maximum queue length is reached and the `bypass_queue` parameter is set to `False`, the endpoint returns a 429 Too Many Requests response.
137
+ - **Exceptions during processing**: If an exception occurs during the image-to-video conversion process, the endpoint returns a 500 Internal Server Error response with the error message.
138
+
139
+ ## 6. Usage Notes
140
+
141
+ - The `image_url` parameter must be a valid URL pointing to an image file.
142
+ - The `length` parameter specifies the duration of the output video in seconds and must be between 1 and 60.
143
+ - The `frame_rate` parameter specifies the frame rate of the output video and must be between 15 and 60.
144
+ - The `zoom_speed` parameter controls the speed of the zoom effect and must be between 0 and 100.
145
+ - The `webhook_url` parameter is optional and can be used to receive a notification when the conversion is complete.
146
+ - The `id` parameter is optional and can be used to identify the request.
147
+
148
+ ## 7. Common Issues
149
+
150
+ - Providing an invalid or inaccessible `image_url` will result in an error during processing.
151
+ - Specifying invalid parameter values outside the allowed ranges will result in a 400 Bad Request response.
152
+ - If the maximum queue length is reached and the `bypass_queue` parameter is set to `False`, the request will be rejected with a 429 Too Many Requests response.
153
+
154
+ ## 8. Best Practices
155
+
156
+ - Validate the `image_url` parameter before sending the request to ensure it points to a valid and accessible image file.
157
+ - Use the `webhook_url` parameter to receive notifications about the completion of the conversion process, rather than polling the API repeatedly.
158
+ - Provide the `id` parameter to easily identify and track the request in logs or notifications.
159
+ - Consider setting the `bypass_queue` parameter to `True` for time-sensitive requests to bypass the queue and process the request immediately.
docs/image/screenshot_webpage.md ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Playwright Screenshot Endpoint
2
+
3
+ **Implemented by:** [Harrison Fisher](https://github.com/HarrisonFisher)
4
+
5
+ ## ⚠️ Disclaimer
6
+ - This endpoint is **not intended to bypass CAPTCHAs, Cloudflare**, or other anti-bot protections.
7
+ - Please **do not create issues or requests** asking for CAPTCHA or Cloudflare bypass features.
8
+ - It will not work for sites with such defenses, and attempting to automate against protected sites could result in your IP address being blacklisted by Cloudflare or similar services.
9
+ - The screenshot endpoint supports custom HTML, JavaScript, and CSS input for flexible rendering and automation, as an alternative to using a URL.
10
+ - This feature should only be used on sites you own or have explicit permission to automate.
11
+
12
+
13
+ ## 1. Overview
14
+
15
+ The `/v1/image/screenshot/webpage` endpoint allows you to capture screenshots of web pages using the Playwright browser automation library. It supports advanced options such as viewport size, device emulation, cookies, headers, element targeting, and more. Screenshots are uploaded to cloud storage, and the resulting URL is returned. This endpoint is part of the v1 API suite and is registered in the main Flask application as a blueprint.
16
+
17
+ ## 2. Endpoint
18
+
19
+ - **URL Path:** `/v1/image/screenshot/webpage`
20
+ - **HTTP Method:** `POST`
21
+
22
+ ## 3. Request
23
+
24
+ ### Headers
25
+
26
+ - `x-api-key` (required): Your API key for authentication.
27
+ - `Content-Type`: `application/json`
28
+
29
+ ### Body Parameters
30
+
31
+ The request body must be a JSON object with the following properties:
32
+
33
+ - `url` (string, required): The URL of the web page to capture.
34
+ - `html` (string, optional): Raw HTML content to render and capture.
35
+ **Note:** Either `url` or `html` must be provided, but not both.
36
+ - `viewport_width` (integer, optional): Viewport width in pixels.
37
+ - `viewport_height` (integer, optional): Viewport height in pixels.
38
+ - `full_page` (boolean, optional): Capture the full scrollable page. Default: `false`.
39
+ - `format` (string, optional): Image format, either `png` or `jpeg`. Default: `png`.
40
+ - `delay` (integer, optional): Delay in milliseconds before taking the screenshot.
41
+ - `device_scale_factor` (number, optional): Device scale factor (e.g., 2 for retina).
42
+ - `user_agent` (string, optional): Custom user agent string.
43
+ - `cookies` (array, optional): List of cookies to set. Each cookie is an object with `name`, `value`, and `domain`.
44
+ - `headers` (object, optional): Additional HTTP headers to set.
45
+ - `quality` (integer, optional): JPEG quality (0-100, only for `jpeg` format).
46
+ - `clip` (object, optional): Region to capture, with `x`, `y`, `width`, `height` (all numbers).
47
+ - `timeout` (integer, optional): Navigation timeout in milliseconds. Minimum: 100.
48
+ - `wait_until` (string, optional): When to consider navigation succeeded. One of `load`, `domcontentloaded`, `networkidle`, `networkidle2`. Default: `load`.
49
+ - `wait_for_selector` (string, optional): Wait for a selector before screenshot.
50
+ - `emulate` (object, optional): Emulation options, e.g., `{ "color_scheme": "dark" }`.
51
+ - `omit_background` (boolean, optional): Hide default white background. Default: `false`.
52
+ - `selector` (string, optional): CSS selector for a specific element to screenshot.
53
+ - `webhook_url` (string, optional): If provided, results are sent to this URL asynchronously.
54
+ - `id` (string, optional): Custom identifier for the request.
55
+ - `js` (string, optional): JavaScript code to inject into the page before taking the screenshot.
56
+ - `css` (string, optional): CSS code to inject into the page before taking the screenshot.
57
+
58
+ #### Example Request
59
+
60
+ ```json
61
+ {
62
+ "url": "https://example.com",
63
+ "viewport_width": 1280,
64
+ "viewport_height": 720,
65
+ "js": "document.body.style.background = 'red';",
66
+ "css": "body { font-size: 30px; }",
67
+ "full_page": true,
68
+ "format": "png",
69
+ "delay": 500,
70
+ "device_scale_factor": 2,
71
+ "user_agent": "CustomAgent/1.0",
72
+ "cookies": [
73
+ {
74
+ "name": "test_cookie",
75
+ "value": "test_value",
76
+ "domain": "example.com",
77
+ "path": "/"
78
+ }
79
+ ],
80
+ "headers": {
81
+ "Accept-Language": "en-US,en;q=0.9"
82
+ },
83
+ "quality": 90,
84
+ "clip": {
85
+ "x": 0,
86
+ "y": 0,
87
+ "width": 800,
88
+ "height": 600
89
+ },
90
+ "timeout": 10000,
91
+ "wait_until": "networkidle",
92
+ "wait_for_selector": "#main-content",
93
+ "selector": "#main-content",
94
+ "emulate": {
95
+ "color_scheme": "dark"
96
+ },
97
+ "omit_background": true,
98
+ "webhook_url": "https://your-webhook.com/callback",
99
+ "id": "custom-job-123"
100
+ }
101
+ ```
102
+
103
+ **cURL Example:**
104
+
105
+ ```bash
106
+ curl -X POST \
107
+ -H "x-api-key: YOUR_API_KEY" \
108
+ -H "Content-Type: application/json" \
109
+ -d '{
110
+ "url": "https://example.com",
111
+ "viewport_width": 1280,
112
+ "viewport_height": 720,
113
+ "js": "document.body.style.background = '\''red'\'';",
114
+ "css": "body { font-size: 30px; }",
115
+ "full_page": true,
116
+ "format": "png",
117
+ "delay": 500,
118
+ "device_scale_factor": 2,
119
+ "user_agent": "CustomAgent/1.0",
120
+ "cookies": [
121
+ {"name": "test_cookie", "value": "test_value", "domain": "example.com", "path": "/"}
122
+ ],
123
+ "headers": {"Accept-Language": "en-US,en;q=0.9"},
124
+ "quality": 90,
125
+ "clip": {"x": 0, "y": 0, "width": 800, "height": 600},
126
+ "timeout": 10000,
127
+ "wait_until": "networkidle",
128
+ "wait_for_selector": "#main-content",
129
+ "selector": "#main-content",
130
+ "emulate": {"color_scheme": "dark"},
131
+ "omit_background": true,
132
+ "webhook_url": "https://your-webhook.com/callback",
133
+ "id": "custom-job-123"
134
+ }' \
135
+ https://your-api-endpoint.com/v1/image/screenshot/webpage
136
+ ```
137
+
138
+ ## 4. Response
139
+
140
+ ### Success Response
141
+
142
+ The response is a JSON object containing the cloud storage URL of the screenshot and job metadata. If a `webhook_url` is provided, the result is sent asynchronously to the webhook.
143
+
144
+ ```json
145
+ {
146
+ "endpoint": "/v1/image/screenshot/webpage",
147
+ "code": 200,
148
+ "id": "custom-job-123",
149
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
150
+ "response": "https://cloud.example.com/screenshot.png",
151
+ "message": "success",
152
+ "pid": 12345,
153
+ "queue_id": 140682639937472,
154
+ "run_time": 2.345,
155
+ "queue_time": 0.012,
156
+ "total_time": 2.357,
157
+ "queue_length": 0,
158
+ "build_number": "1.0.0"
159
+ }
160
+ ```
161
+
162
+ ### Error Responses
163
+
164
+ - **400 Bad Request**: Invalid or missing parameters.
165
+ - **401 Unauthorized**: Invalid or missing API key.
166
+ - **429 Too Many Requests**: Queue is full.
167
+ - **500 Internal Server Error**: An error occurred during processing.
168
+
169
+ Example error response:
170
+
171
+ ```json
172
+ {
173
+ "endpoint": "/v1/image/screenshot/webpage",
174
+ "code": 500,
175
+ "id": "custom-job-123",
176
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
177
+ "response": null,
178
+ "message": "Error message details",
179
+ "pid": 12345,
180
+ "queue_id": 140682639937472,
181
+ "run_time": 0.123,
182
+ "queue_time": 0.056,
183
+ "total_time": 0.179,
184
+ "queue_length": 1,
185
+ "build_number": "1.0.0"
186
+ }
187
+ ```
188
+
189
+ ## 5. Error Handling
190
+
191
+ - **Missing or invalid parameters**: Returns 400 with details.
192
+ - **Authentication failure**: Returns 401.
193
+ - **Queue full**: Returns 429.
194
+ - **Processing error**: Returns 500 with error message.
195
+
196
+ ## 6. Usage Notes
197
+
198
+ - If `webhook_url` is provided, the request is processed asynchronously and the result is sent to the webhook.
199
+ - Screenshots are always uploaded to cloud storage; the response contains the file URL.
200
+ - Use `selector` to capture a specific element instead of the full page.
201
+ - The `clip` parameter allows capturing a specific region.
202
+ - The endpoint enforces strict payload validation.
203
+
204
+ ## 7. Common Issues
205
+
206
+ - Invalid or inaccessible URL.
207
+ - Selector not found (if using `wait_for_selector` or `selector`).
208
+ - Cookie domain mismatch.
209
+ - Timeout errors for slow-loading pages.
210
+ - Invalid API key.
211
+
212
+ ## 8. Best Practices
213
+
214
+ - Always validate your input parameters before sending the request.
215
+ - Use unique `id` values for tracking jobs.
216
+ - Monitor queue length and handle 429 errors gracefully.
217
+ - Use HTTPS for all URLs and webhooks.
218
+ - Test your selectors and page state locally before automating screenshots.
docs/media/convert/media_convert.md ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media Convert Endpoint Documentation
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/media/convert` endpoint is part of the Flask API application and is responsible for converting media files (audio or video) from one format to another. This endpoint fits into the overall API structure as a part of the `v1` blueprint, which contains various media-related functionalities.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/media/convert`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+
22
+ - `media_url` (required, string): The URL of the media file to be converted.
23
+ - `format` (required, string): The desired output format for the converted media file.
24
+ - `video_codec` (optional, string): The video codec to be used for the conversion. Default is `libx264`.
25
+ - `video_preset` (optional, string): The video preset to be used for the conversion. Default is `medium`.
26
+ - `video_crf` (optional, number): The Constant Rate Factor (CRF) value for video encoding. Must be between 0 and 51. Default is 23.
27
+ - `audio_codec` (optional, string): The audio codec to be used for the conversion. Default is `aac`.
28
+ - `audio_bitrate` (optional, string): The audio bitrate to be used for the conversion. Default is `128k`.
29
+ - `webhook_url` (optional, string): The URL to receive a webhook notification upon completion of the conversion process.
30
+ - `id` (optional, string): An optional identifier for the conversion request.
31
+
32
+ ### Example Request
33
+
34
+ ```json
35
+ {
36
+ "media_url": "https://example.com/video.mp4",
37
+ "format": "avi",
38
+ "video_codec": "libx264",
39
+ "video_preset": "medium",
40
+ "video_crf": 23,
41
+ "audio_codec": "aac",
42
+ "audio_bitrate": "128k",
43
+ "webhook_url": "https://example.com/webhook",
44
+ "id": "unique-request-id"
45
+ }
46
+ ```
47
+
48
+ ```bash
49
+ curl -X POST \
50
+ https://api.example.com/v1/media/convert \
51
+ -H 'x-api-key: YOUR_API_KEY' \
52
+ -H 'Content-Type: application/json' \
53
+ -d '{
54
+ "media_url": "https://example.com/video.mp4",
55
+ "format": "avi",
56
+ "video_codec": "libx264",
57
+ "video_preset": "medium",
58
+ "video_crf": 23,
59
+ "audio_codec": "aac",
60
+ "audio_bitrate": "128k",
61
+ "webhook_url": "https://example.com/webhook",
62
+ "id": "unique-request-id"
63
+ }'
64
+ ```
65
+
66
+ ## 4. Response
67
+
68
+ ### Success Response
69
+
70
+ The success response will be a JSON object containing the URL of the converted media file uploaded to cloud storage, the endpoint path, and a status code of 200.
71
+
72
+ ```json
73
+ {
74
+ "code": 200,
75
+ "id": "unique-request-id",
76
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
77
+ "response": "https://cloud.example.com/converted-video.avi",
78
+ "message": "success",
79
+ "pid": 12345,
80
+ "queue_id": 1234567890,
81
+ "run_time": 10.234,
82
+ "queue_time": 0.123,
83
+ "total_time": 10.357,
84
+ "queue_length": 0,
85
+ "build_number": "1.0.0"
86
+ }
87
+ ```
88
+
89
+ ### Error Responses
90
+
91
+ - **400 Bad Request**: Returned when the request payload is missing or invalid.
92
+ - **401 Unauthorized**: Returned when the `x-api-key` header is missing or invalid.
93
+ - **500 Internal Server Error**: Returned when an unexpected error occurs during the conversion process.
94
+
95
+ Example error response:
96
+
97
+ ```json
98
+ {
99
+ "code": 400,
100
+ "id": "unique-request-id",
101
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
102
+ "message": "Invalid request payload",
103
+ "pid": 12345,
104
+ "queue_id": 1234567890,
105
+ "queue_length": 0,
106
+ "build_number": "1.0.0"
107
+ }
108
+ ```
109
+
110
+ ## 5. Error Handling
111
+
112
+ The endpoint uses the `validate_payload` decorator to validate the request payload against a JSON schema. If the payload is missing or invalid, a 400 Bad Request error is returned.
113
+
114
+ The `authenticate` decorator is used to ensure that the request includes a valid `x-api-key` header. If the header is missing or invalid, a 401 Unauthorized error is returned.
115
+
116
+ If an unexpected error occurs during the conversion process, a 500 Internal Server Error is returned, and the error is logged.
117
+
118
+ ## 6. Usage Notes
119
+
120
+ - The `media_url` parameter must be a valid URL pointing to the media file to be converted.
121
+ - The `format` parameter must be a valid media format supported by the conversion process.
122
+ - The optional parameters (`video_codec`, `video_preset`, `video_crf`, `audio_codec`, `audio_bitrate`) allow you to customize the conversion settings.
123
+ - If the `webhook_url` parameter is provided, a webhook notification will be sent to the specified URL upon completion of the conversion process.
124
+ - The `id` parameter is optional and can be used to identify the conversion request.
125
+
126
+ ## 7. Common Issues
127
+
128
+ - Providing an invalid or inaccessible `media_url`.
129
+ - Specifying an unsupported `format`.
130
+ - Providing invalid values for the optional parameters (e.g., `video_crf` outside the valid range).
131
+
132
+ ## 8. Best Practices
133
+
134
+ - Always validate the input parameters on the client-side before sending the request.
135
+ - Use the `id` parameter to track and identify conversion requests.
136
+ - Provide a `webhook_url` to receive notifications about the conversion process completion.
137
+ - Monitor the API logs for any errors or issues during the conversion process.
138
+ - Consider implementing rate limiting or queue management to handle high volumes of requests.
docs/media/convert/media_to_mp3.md ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media to MP3 Conversion
2
+
3
+ The `/v1/media/convert/mp3` endpoint is part of the Flask API application and is responsible for converting various media files into MP3 format. This endpoint is registered in the `app.py` file under the `v1_media_convert_mp3_bp` blueprint.
4
+
5
+ ## Endpoint Details
6
+
7
+ **URL Path:** `/v1/media/convert/mp3`
8
+
9
+ ## 1. Overview
10
+
11
+ The `/v1/media/convert/mp3` endpoint is a part of the API's media transformation functionality. It allows users to convert various media files (audio or video) to MP3 format. This endpoint fits into the overall API structure as a part of the `v1` namespace, which represents the first version of the API.
12
+
13
+ ## 2. Endpoint
14
+
15
+ ```
16
+ POST /v1/media/convert/mp3
17
+ ```
18
+
19
+ ## 3. Request
20
+
21
+ ### Headers
22
+
23
+ - `x-api-key` (required): The API key for authentication.
24
+
25
+ ### Body Parameters
26
+
27
+ - `media_url` (required, string): The URL of the media file to be converted.
28
+ - `webhook_url` (optional, string): The URL to receive a webhook notification upon completion.
29
+ - `id` (optional, string): A unique identifier for the request.
30
+ - `bitrate` (optional, string): The desired bitrate for the output MP3 file, in the format `<value>k` (e.g., `128k`). If not provided, defaults to `128k`.
31
+
32
+ The `validate_payload` directive in the routes file enforces the following JSON schema for the request body:
33
+
34
+ ```json
35
+ {
36
+ "type": "object",
37
+ "properties": {
38
+ "media_url": {"type": "string", "format": "uri"},
39
+ "webhook_url": {"type": "string", "format": "uri"},
40
+ "id": {"type": "string"},
41
+ "bitrate": {"type": "string", "pattern": "^[0-9]+k$"}
42
+ },
43
+ "required": ["media_url"],
44
+ "additionalProperties": False
45
+ }
46
+ ```
47
+
48
+ ### Example Request
49
+
50
+ ```json
51
+ {
52
+ "media_url": "https://example.com/video.mp4",
53
+ "webhook_url": "https://example.com/webhook",
54
+ "id": "unique-request-id",
55
+ "bitrate": "192k"
56
+ }
57
+ ```
58
+
59
+ ```bash
60
+ curl -X POST \
61
+ -H "x-api-key: YOUR_API_KEY" \
62
+ -H "Content-Type: application/json" \
63
+ -d '{"media_url": "https://example.com/video.mp4", "webhook_url": "https://example.com/webhook", "id": "unique-request-id", "bitrate": "192k"}' \
64
+ https://your-api-endpoint.com/v1/media/convert/mp3
65
+ ```
66
+
67
+ ## 4. Response
68
+
69
+ ### Success Response
70
+
71
+ The success response follows the general response structure defined in `app.py`. Here's an example:
72
+
73
+ ```json
74
+ {
75
+ "endpoint": "/v1/media/convert/mp3",
76
+ "code": 200,
77
+ "id": "unique-request-id",
78
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
79
+ "response": "https://cloud-storage.example.com/converted-file.mp3",
80
+ "message": "success",
81
+ "pid": 12345,
82
+ "queue_id": 6789,
83
+ "run_time": 5.234,
84
+ "queue_time": 0.123,
85
+ "total_time": 5.357,
86
+ "queue_length": 0,
87
+ "build_number": "1.0.0"
88
+ }
89
+ ```
90
+
91
+ ### Error Responses
92
+
93
+ - **400 Bad Request**: Returned when the request payload is invalid or missing required parameters.
94
+ - **401 Unauthorized**: Returned when the `x-api-key` header is missing or invalid.
95
+ - **500 Internal Server Error**: Returned when an unexpected error occurs during the conversion process.
96
+
97
+ Example error response:
98
+
99
+ ```json
100
+ {
101
+ "code": 400,
102
+ "id": "unique-request-id",
103
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
104
+ "message": "Invalid request payload: 'media_url' is a required property",
105
+ "pid": 12345,
106
+ "queue_id": 6789,
107
+ "queue_length": 0,
108
+ "build_number": "1.0.0"
109
+ }
110
+ ```
111
+
112
+ ## 5. Error Handling
113
+
114
+ The endpoint handles the following common errors:
115
+
116
+ - Missing or invalid `media_url` parameter: Returns a 400 Bad Request error.
117
+ - Invalid `bitrate` parameter: Returns a 400 Bad Request error.
118
+ - Authentication failure: Returns a 401 Unauthorized error.
119
+ - Unexpected exceptions during the conversion process: Returns a 500 Internal Server Error.
120
+
121
+ Additionally, the main application context (`app.py`) includes error handling for queue overload. If the maximum queue length is reached, the endpoint will return a 429 Too Many Requests error.
122
+
123
+ ## 6. Usage Notes
124
+
125
+ - The `media_url` parameter should point to a valid media file (audio or video) that can be converted to MP3 format.
126
+ - If the `webhook_url` parameter is provided, a webhook notification will be sent to the specified URL upon completion of the conversion process.
127
+ - The `id` parameter can be used to uniquely identify the request, which can be helpful for tracking and logging purposes.
128
+ - The `bitrate` parameter allows you to specify the desired bitrate for the output MP3 file. If not provided, the default bitrate of 128k will be used.
129
+
130
+ ## 7. Common Issues
131
+
132
+ - Providing an invalid or inaccessible `media_url`.
133
+ - Attempting to convert unsupported media formats.
134
+ - Exceeding the maximum queue length, resulting in a 429 Too Many Requests error.
135
+
136
+ ## 8. Best Practices
137
+
138
+ - Validate the `media_url` parameter before sending the request to ensure it points to a valid and accessible media file.
139
+ - Consider providing a `webhook_url` parameter to receive notifications about the conversion process completion.
140
+ - Use a unique `id` parameter for each request to facilitate tracking and logging.
141
+ - Implement retry mechanisms in case of transient errors or queue overload situations.
142
+ - Monitor the API logs for any errors or issues during the conversion process.
docs/media/cut.md ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media Cut Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/media/cut` endpoint is part of the Flask API application and is designed to cut specified segments from a media file (video or audio) with optional encoding settings. This endpoint fits into the overall API structure as a part of the `v1` blueprint, which contains various media-related functionalities.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/media/cut`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+
22
+ - `media_url` (required, string): The URL of the media file to be cut.
23
+ - `cuts` (required, array of objects): An array of cut segments, where each object has the following properties:
24
+ - `start` (required, string): The start time of the cut segment in the format `hh:mm:ss.ms`.
25
+ - `end` (required, string): The end time of the cut segment in the format `hh:mm:ss.ms`.
26
+ - `video_codec` (optional, string): The video codec to be used for encoding the output file. Default is `libx264`.
27
+ - `video_preset` (optional, string): The video preset to be used for encoding the output file. Default is `medium`.
28
+ - `video_crf` (optional, number): The Constant Rate Factor (CRF) value for video encoding. Must be between 0 and 51. Default is 23.
29
+ - `audio_codec` (optional, string): The audio codec to be used for encoding the output file. Default is `aac`.
30
+ - `audio_bitrate` (optional, string): The audio bitrate to be used for encoding the output file. Default is `128k`.
31
+ - `webhook_url` (optional, string): The URL to receive a webhook notification upon completion of the task.
32
+ - `id` (optional, string): A unique identifier for the request.
33
+
34
+ ### Example Request
35
+
36
+ ```json
37
+ {
38
+ "media_url": "https://example.com/video.mp4",
39
+ "cuts": [
40
+ {
41
+ "start": "00:00:10.000",
42
+ "end": "00:00:20.000"
43
+ },
44
+ {
45
+ "start": "00:00:30.000",
46
+ "end": "00:00:40.000"
47
+ }
48
+ ],
49
+ "video_codec": "libx264",
50
+ "video_preset": "medium",
51
+ "video_crf": 23,
52
+ "audio_codec": "aac",
53
+ "audio_bitrate": "128k",
54
+ "webhook_url": "https://example.com/webhook",
55
+ "id": "unique-request-id"
56
+ }
57
+ ```
58
+
59
+ ```
60
+ curl -X POST \
61
+ https://api.example.com/v1/media/cut \
62
+ -H 'x-api-key: YOUR_API_KEY' \
63
+ -H 'Content-Type: application/json' \
64
+ -d '{
65
+ "media_url": "https://example.com/video.mp4",
66
+ "cuts": [
67
+ {
68
+ "start": "00:00:10.000",
69
+ "end": "00:00:20.000"
70
+ },
71
+ {
72
+ "start": "00:00:30.000",
73
+ "end": "00:00:40.000"
74
+ }
75
+ ],
76
+ "video_codec": "libx264",
77
+ "video_preset": "medium",
78
+ "video_crf": 23,
79
+ "audio_codec": "aac",
80
+ "audio_bitrate": "128k",
81
+ "webhook_url": "https://example.com/webhook",
82
+ "id": "unique-request-id"
83
+ }'
84
+ ```
85
+
86
+ ## 4. Response
87
+
88
+ ### Success Response
89
+
90
+ The success response follows the general response structure defined in the `app.py` file. Here's an example:
91
+
92
+ ```json
93
+ {
94
+ "code": 200,
95
+ "id": "unique-request-id",
96
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
97
+ "response": {
98
+ "file_url": "https://example.com/output.mp4"
99
+ },
100
+ "message": "success",
101
+ "run_time": 5.234,
102
+ "queue_time": 0.012,
103
+ "total_time": 5.246,
104
+ "pid": 12345,
105
+ "queue_id": 1234567890,
106
+ "queue_length": 0,
107
+ "build_number": "1.0.0"
108
+ }
109
+ ```
110
+
111
+ ### Error Responses
112
+
113
+ - **400 Bad Request**: Returned when the request payload is missing or invalid.
114
+
115
+ ```json
116
+ {
117
+ "code": 400,
118
+ "message": "Invalid request payload"
119
+ }
120
+ ```
121
+
122
+ - **401 Unauthorized**: Returned when the `x-api-key` header is missing or invalid.
123
+
124
+ ```json
125
+ {
126
+ "code": 401,
127
+ "message": "Unauthorized"
128
+ }
129
+ ```
130
+
131
+ - **500 Internal Server Error**: Returned when an unexpected error occurs on the server.
132
+
133
+ ```json
134
+ {
135
+ "code": 500,
136
+ "message": "Internal Server Error"
137
+ }
138
+ ```
139
+
140
+ ## 5. Error Handling
141
+
142
+ The endpoint handles the following common errors:
143
+
144
+ - Missing or invalid request parameters: Returns a 400 Bad Request error.
145
+ - Authentication failure: Returns a 401 Unauthorized error if the `x-api-key` header is missing or invalid.
146
+ - Unexpected exceptions: Returns a 500 Internal Server Error if an unexpected exception occurs during the media cut process.
147
+
148
+ The main application context (`app.py`) also includes error handling for queue overload. If the maximum queue length is reached, the endpoint returns a 429 Too Many Requests error.
149
+
150
+ ## 6. Usage Notes
151
+
152
+ - The `media_url` parameter must be a valid URL pointing to a media file (video or audio).
153
+ - The `cuts` parameter must be an array of objects, where each object specifies a start and end time for a cut segment in the format `hh:mm:ss.ms`.
154
+ - The optional encoding parameters (`video_codec`, `video_preset`, `video_crf`, `audio_codec`, `audio_bitrate`) can be used to customize the output file encoding settings.
155
+ - The `webhook_url` parameter is optional and can be used to receive a webhook notification upon completion of the task.
156
+ - The `id` parameter is optional and can be used to uniquely identify the request.
157
+
158
+ ## 7. Common Issues
159
+
160
+ - Providing an invalid or inaccessible `media_url`.
161
+ - Providing invalid or out-of-range values for the encoding parameters.
162
+ - Providing overlapping or invalid cut segments in the `cuts` parameter.
163
+
164
+ ## 8. Best Practices
165
+
166
+ - Validate the input parameters on the client-side before sending the request.
167
+ - Use the `webhook_url` parameter to receive notifications and handle the response asynchronously.
168
+ - Monitor the `queue_length` parameter in the response to manage the load on the API.
169
+ - Use the `id` parameter to correlate requests and responses for better tracking and debugging.
docs/media/download.md ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media Download API Endpoint Documentation
2
+
3
+ ## Overview
4
+
5
+ The `/v1/BETA/media/download` endpoint provides a powerful interface for downloading media content from various online sources using the yt-dlp library. This endpoint is part of the v1 media services in the API structure, allowing users to download videos, extract audio, and retrieve thumbnails and subtitles from supported platforms. The endpoint handles authentication, request validation, and queues tasks for processing, making it suitable for handling resource-intensive media downloads without blocking the main application thread.
6
+
7
+ ## Endpoint
8
+
9
+ - **URL**: `/v1/BETA/media/download`
10
+ - **Method**: `POST`
11
+ - **Blueprint**: `v1_media_download_bp`
12
+
13
+ ## Request
14
+
15
+ ### Headers
16
+
17
+ - `x-api-key`: Required for authentication (handled by the `@authenticate` decorator)
18
+
19
+ ### Body Parameters
20
+
21
+ #### Required Parameters
22
+
23
+ | Parameter | Type | Description |
24
+ |-----------|------|-------------|
25
+ | `media_url` | string (URI format) | The URL of the media to download |
26
+
27
+ #### Optional Parameters
28
+
29
+ | Parameter | Type | Description |
30
+ |-----------|------|-------------|
31
+ | `webhook_url` | string (URI format) | URL to receive the result when processing is complete |
32
+ | `id` | string | Custom identifier for tracking the request |
33
+ | `cookie` | string | Path to cookie file, URL to cookie file, or cookie string in Netscape format |
34
+ | `cloud_upload` | boolean | When true (default), the downloaded media will be uploaded to cloud storage and a cloud URL will be returned. When false, the direct download URL of the media will be returned instead. |
35
+
36
+ #### Format Options (Optional)
37
+
38
+ ```json
39
+ "format": {
40
+ "quality": "string", // Quality specification (e.g., "best")
41
+ "format_id": "string", // Specific format ID
42
+ "resolution": "string", // Resolution specification (e.g., "720p")
43
+ "video_codec": "string", // Video codec preference
44
+ "audio_codec": "string" // Audio codec preference
45
+ }
46
+ ```
47
+
48
+ #### Audio Options (Optional)
49
+
50
+ ```json
51
+ "audio": {
52
+ "extract": boolean, // Whether to extract audio
53
+ "format": "string", // Audio format (e.g., "mp3", "m4a")
54
+ "quality": "string" // Audio quality specification
55
+ }
56
+ ```
57
+
58
+ #### Thumbnail Options (Optional)
59
+
60
+ ```json
61
+ "thumbnails": {
62
+ "download": boolean, // Whether to download thumbnails
63
+ "download_all": boolean, // Whether to download all available thumbnails
64
+ "formats": ["string"], // Array of thumbnail formats to download
65
+ "convert": boolean, // Whether to convert thumbnails
66
+ "embed_in_audio": boolean // Whether to embed thumbnails in audio files
67
+ }
68
+ ```
69
+
70
+ #### Subtitle Options (Optional)
71
+
72
+ ```json
73
+ "subtitles": {
74
+ "download": boolean, // Whether to download subtitles
75
+ "languages": ["string"], // Array of language codes for subtitles
76
+ "formats": ["string"] // Array of subtitle formats to download
77
+ }
78
+ ```
79
+
80
+ #### Download Options (Optional)
81
+
82
+ ```json
83
+ "download": {
84
+ "max_filesize": integer, // Maximum file size in bytes
85
+ "rate_limit": "string", // Download rate limit (e.g., "50K")
86
+ "retries": integer // Number of download retry attempts
87
+ }
88
+ ```
89
+
90
+ ### Example Request
91
+
92
+ ```json
93
+ {
94
+ "media_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
95
+ "webhook_url": "https://example.com/webhook",
96
+ "id": "custom-request-123",
97
+ "cookie": "# Netscape HTTP Cookie File\n.youtube.com\tTRUE\t/\tFALSE\t0\tCONSENT\tYES+cb",
98
+ "cloud_upload": true,
99
+ "format": {
100
+ "quality": "best",
101
+ "resolution": "720p"
102
+ },
103
+ "audio": {
104
+ "extract": true,
105
+ "format": "mp3"
106
+ },
107
+ "thumbnails": {
108
+ "download": true
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Example cURL Command
114
+
115
+ ```bash
116
+ curl -X POST \
117
+ https://api.example.com/v1/BETA/media/download \
118
+ -H 'Content-Type: application/json' \
119
+ -H 'x-api-key: your-api-key-here' \
120
+ -d '{
121
+ "media_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
122
+ "webhook_url": "https://example.com/webhook",
123
+ "id": "custom-request-123",
124
+ "cookie": "# Netscape HTTP Cookie File\n.youtube.com\tTRUE\t/\tFALSE\t0\tCONSENT\tYES+cb",
125
+ "cloud_upload": true,
126
+ "format": {
127
+ "quality": "best",
128
+ "resolution": "720p"
129
+ },
130
+ "audio": {
131
+ "extract": true,
132
+ "format": "mp3"
133
+ },
134
+ "thumbnails": {
135
+ "download": true
136
+ }
137
+ }'
138
+ ```
139
+
140
+ ## Response
141
+
142
+ ### Immediate Response (When Using Webhook)
143
+
144
+ When a webhook URL is provided, the API will queue the task and return an immediate response with a 202 status code:
145
+
146
+ ```json
147
+ {
148
+ "code": 202,
149
+ "id": "custom-request-123",
150
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
151
+ "message": "processing",
152
+ "pid": 12345,
153
+ "queue_id": 67890,
154
+ "max_queue_length": "unlimited",
155
+ "queue_length": 3,
156
+ "build_number": "1.0.123"
157
+ }
158
+ ```
159
+
160
+ ### Success Response (When Not Using Webhook or When Webhook Is Called)
161
+
162
+ ```json
163
+ {
164
+ "code": 200,
165
+ "id": "custom-request-123",
166
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
167
+ "response": {
168
+ "media": {
169
+ "media_url": "https://storage.example.com/media/video-123.mp4",
170
+ "title": "Never Gonna Give You Up",
171
+ "format_id": "22",
172
+ "ext": "mp4",
173
+ "resolution": "720p",
174
+ "filesize": 12345678,
175
+ "width": 1280,
176
+ "height": 720,
177
+ "fps": 30,
178
+ "video_codec": "avc1.4d401f",
179
+ "audio_codec": "mp4a.40.2",
180
+ "upload_date": "20090325",
181
+ "duration": 212,
182
+ "view_count": 1234567890,
183
+ "uploader": "Rick Astley",
184
+ "uploader_id": "RickAstleyVEVO",
185
+ "description": "Official music video for Rick Astley - Never Gonna Give You Up"
186
+ },
187
+ "thumbnails": [
188
+ {
189
+ "id": "default",
190
+ "image_url": "https://storage.example.com/media/thumbnail-123.jpg",
191
+ "width": 1280,
192
+ "height": 720,
193
+ "original_format": "jpg",
194
+ "converted": false
195
+ }
196
+ ]
197
+ },
198
+ "message": "success",
199
+ "pid": 12345,
200
+ "queue_id": 67890,
201
+ "run_time": 5.123,
202
+ "queue_time": 0.456,
203
+ "total_time": 5.579,
204
+ "queue_length": 2,
205
+ "build_number": "1.0.123"
206
+ }
207
+ ```
208
+
209
+ ### Error Responses
210
+
211
+ #### Invalid Request (400)
212
+
213
+ ```json
214
+ {
215
+ "code": 400,
216
+ "id": "custom-request-123",
217
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
218
+ "message": "Invalid request: 'media_url' is a required property",
219
+ "pid": 12345,
220
+ "queue_id": 67890,
221
+ "queue_length": 2,
222
+ "build_number": "1.0.123"
223
+ }
224
+ ```
225
+
226
+ #### Authentication Error (401)
227
+
228
+ ```json
229
+ {
230
+ "code": 401,
231
+ "message": "Invalid API key",
232
+ "build_number": "1.0.123"
233
+ }
234
+ ```
235
+
236
+ #### Queue Full (429)
237
+
238
+ ```json
239
+ {
240
+ "code": 429,
241
+ "id": "custom-request-123",
242
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
243
+ "message": "MAX_QUEUE_LENGTH (100) reached",
244
+ "pid": 12345,
245
+ "queue_id": 67890,
246
+ "queue_length": 100,
247
+ "build_number": "1.0.123"
248
+ }
249
+ ```
250
+
251
+ #### Server Error (500)
252
+
253
+ ```json
254
+ {
255
+ "code": 500,
256
+ "id": "custom-request-123",
257
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
258
+ "message": "Error during download process - HTTP Error 403: Forbidden",
259
+ "pid": 12345,
260
+ "queue_id": 67890,
261
+ "queue_length": 2,
262
+ "build_number": "1.0.123"
263
+ }
264
+ ```
265
+
266
+ ## Error Handling
267
+
268
+ The endpoint handles various error scenarios:
269
+
270
+ - **Missing Required Parameters**: Returns a 400 status code with details about the missing parameter
271
+ - **Invalid Parameter Format**: Returns a 400 status code if parameters don't match the expected format
272
+ - **Authentication Failures**: Returns a 401 status code if the API key is invalid or missing
273
+ - **Queue Limits**: Returns a 429 status code if the task queue is full (when MAX_QUEUE_LENGTH is set)
274
+ - **Download Failures**: Returns a 500 status code with details about the download failure
275
+ - **Media Source Errors**: Returns a 500 status code if the media source is unavailable or restricted
276
+
277
+ ## Usage Notes
278
+
279
+ 1. **Webhook Handling**:
280
+ - When providing a `webhook_url`, the request will be queued and processed asynchronously
281
+ - Without a `webhook_url`, the request will be processed synchronously, which may lead to longer response times
282
+
283
+ 2. **Format Selection**:
284
+ - The `format` options allow fine-grained control over the downloaded media quality
285
+ - When multiple format options are specified, they are combined with a '+' separator
286
+
287
+ 3. **Audio Extraction**:
288
+ - Setting `audio.extract` to `true` will extract audio from the media
289
+ - Specify `audio.format` to control the output audio format (e.g., "mp3", "m4a")
290
+
291
+ 4. **Thumbnail Handling**:
292
+ - When `thumbnails.download` is `true`, the API will download and provide URLs for thumbnails
293
+ - Use `thumbnails.download_all` to retrieve all available thumbnails
294
+
295
+ 5. **Rate Limiting**:
296
+ - Use `download.rate_limit` to control download speed (e.g., "50K" for 50 KB/s)
297
+ - This can help prevent IP blocking from some media sources
298
+
299
+ ## Common Issues
300
+
301
+ 1. **Geo-restricted Content**: Some media may be unavailable in certain regions
302
+ 2. **Rate Limiting**: Media sources may rate-limit or block frequent downloads
303
+ 3. **Large File Downloads**: Very large files may time out during download
304
+ 4. **Format Availability**: Not all requested formats may be available for all media sources
305
+ 5. **Webhook Failures**: If the webhook URL is unreachable, you won't receive the final result
306
+ 6. **Queue Overflow**: Requests may be rejected if the processing queue is full
307
+
308
+ ## Best Practices
309
+
310
+ 1. **Use Webhooks for Large Downloads**: Always use webhooks for potentially large or slow downloads to avoid timeout issues
311
+ 2. **Specify Format Constraints**: Be specific about format requirements to avoid unnecessarily large downloads
312
+ 3. **Handle Thumbnails Separately**: For efficiency, only request thumbnails when needed
313
+ 4. **Implement Retry Logic**: Implement client-side retry logic for handling temporary failures
314
+ 5. **Monitor Queue Length**: Check the `queue_length` in responses to gauge system load
315
+ 6. **Use Reasonable Rate Limits**: Set appropriate `download.rate_limit` values to avoid being blocked by media sources
316
+ 7. **Validate Media URLs**: Ensure media URLs are valid and accessible before submitting
317
+ 8. **Store Downloaded Media**: The cloud URLs provided in responses may have expiration times, so download and store important media promptly
docs/media/feedback.md ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media Feedback Portal
2
+
3
+ This endpoint serves a static web page for collecting media feedback.
4
+
5
+ ## Endpoint
6
+
7
+ ```
8
+ GET /v1/media/feedback
9
+ ```
10
+
11
+ ### Authentication
12
+
13
+ This endpoint does not require authentication and is publicly accessible.
14
+
15
+ ### Response
16
+
17
+ Returns the HTML feedback form page.
18
+
19
+ ## Static Files
20
+
21
+ Additional static files (CSS, JavaScript, images) can be accessed at:
22
+
23
+ ```
24
+ GET /v1/media/feedback/<filename>
25
+ ```
26
+
27
+ Replace `<filename>` with the path to the static resource relative to the static directory.
28
+
29
+ ## Development
30
+
31
+ The static website files are stored in:
32
+
33
+ ```
34
+ services/v1/media/feedback/static/
35
+ ```
36
+
37
+ This directory contains:
38
+
39
+ - `index.html` - Main HTML file
40
+ - `css/styles.css` - Stylesheet
41
+ - `js/script.js` - JavaScript code
42
+ - `images/` - Directory for image assets
43
+
44
+ To modify the feedback page, edit these files directly.
docs/media/generate_ass.md ADDED
@@ -0,0 +1,350 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ASS Subtitle Generation Endpoint (v1)
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/media/generate/ass` endpoint is part of the Media API and is responsible for generating an ASS (Advanced SubStation Alpha) subtitle file from a media file (typically a video or audio). It accepts a media URL and various styling options for the subtitles. The endpoint utilizes the `generate_ass_captions_v1` service to generate the ASS file, which is then uploaded to cloud storage, and the cloud URL is returned in the response.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL:** `/v1/media/generate/ass`
10
+ **Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key`: Required. The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+ > **Note:** `canvas_width` and `canvas_height` are recommended for audio files (e.g., MP3) to control the subtitle canvas size.
22
+
23
+ - `media_url` (string, required): The URL of the media file (video or audio) to generate subtitles for.
24
+ - `canvas_width` (integer, optional): Subtitle canvas width in pixels.
25
+ - `canvas_height` (integer, optional): Subtitle canvas height in pixels.
26
+ - `settings` (object, optional): An object containing various styling options for the subtitles. See the schema below for available options.
27
+ - `replace` (array, optional): An array of objects with `find` and `replace` properties, specifying text replacements to be made in the subtitles.
28
+ - `exclude_time_ranges` (array, optional): List of time ranges to skip when generating subtitles. Each item must be an object with:
29
+ - `start`: (string, required) The start time of the excluded range, as a string timecode in `hh:mm:ss.ms` format (e.g., `00:01:23.456`).
30
+ - `end`: (string, required) The end time, as a string timecode in `hh:mm:ss.ms` format, which must be strictly greater than `start`.
31
+ - `language` (string, optional): The language code for the subtitles (e.g., "en", "fr"). Defaults to "auto".
32
+ - `webhook_url` (string, optional): A URL to receive a webhook notification when the subtitle generation process is complete.
33
+ - `id` (string, optional): An identifier for the request.
34
+
35
+
36
+
37
+ #### Settings Schema
38
+
39
+ ```json
40
+ {
41
+ "type": "object",
42
+ "properties": {
43
+ "line_color": {"type": "string"},
44
+ "word_color": {"type": "string"},
45
+ "outline_color": {"type": "string"},
46
+ "all_caps": {"type": "boolean"},
47
+ "max_words_per_line": {"type": "integer"},
48
+ "x": {"type": "integer"},
49
+ "y": {"type": "integer"},
50
+ "position": {
51
+ "type": "string",
52
+ "enum": [
53
+ "bottom_left", "bottom_center", "bottom_right",
54
+ "middle_left", "middle_center", "middle_right",
55
+ "top_left", "top_center", "top_right"
56
+ ]
57
+ },
58
+ "alignment": {
59
+ "type": "string",
60
+ "enum": ["left", "center", "right"]
61
+ },
62
+ "font_family": {"type": "string"},
63
+ "font_size": {"type": "integer"},
64
+ "bold": {"type": "boolean"},
65
+ "italic": {"type": "boolean"},
66
+ "underline": {"type": "boolean"},
67
+ "strikeout": {"type": "boolean"},
68
+ "style": {
69
+ "type": "string",
70
+ "enum": [
71
+ "classic", // Regular subtitle with all text displayed at once
72
+ "karaoke", // Highlights words sequentially in a karaoke style
73
+ "highlight", // Shows full text but highlights the current word
74
+ "underline", // Shows full text but underlines the current word
75
+ "word_by_word" // Shows one word at a time
76
+ ]
77
+ },
78
+ "outline_width": {"type": "integer"},
79
+ "spacing": {"type": "integer"},
80
+ "angle": {"type": "integer"},
81
+ "shadow_offset": {"type": "integer"}
82
+ },
83
+ "additionalProperties": false
84
+ }
85
+ ```
86
+
87
+ ### Example Requests
88
+
89
+ #### Example 1: Basic Automatic Subtitle Generation
90
+ ```json
91
+ {
92
+ "media_url": "https://example.com/video.mp4"
93
+ }
94
+ ```
95
+ This minimal request will automatically transcribe the media and generate white subtitles at the bottom center.
96
+
97
+ #### Example 2: Custom Styling
98
+ ```json
99
+ {
100
+ "media_url": "https://example.com/video.mp4",
101
+ "settings": {
102
+ "style": "classic",
103
+ "line_color": "#FFFFFF",
104
+ "outline_color": "#000000",
105
+ "position": "bottom_center",
106
+ "alignment": "center",
107
+ "font_family": "Arial",
108
+ "font_size": 24,
109
+ "bold": true
110
+ }
111
+ }
112
+ ```
113
+
114
+ #### Example 3: Karaoke-Style Subtitles with Advanced Options
115
+ ```json
116
+ {
117
+ "media_url": "https://example.com/video.mp4",
118
+ "settings": {
119
+ "line_color": "#FFFFFF",
120
+ "word_color": "#FFFF00",
121
+ "outline_color": "#000000",
122
+ "all_caps": false,
123
+ "max_words_per_line": 10,
124
+ "position": "bottom_center",
125
+ "alignment": "center",
126
+ "font_family": "Arial",
127
+ "font_size": 24,
128
+ "bold": false,
129
+ "italic": false,
130
+ "style": "karaoke",
131
+ "outline_width": 2,
132
+ "shadow_offset": 2
133
+ },
134
+ "replace": [
135
+ {
136
+ "find": "um",
137
+ "replace": ""
138
+ },
139
+ {
140
+ "find": "like",
141
+ "replace": ""
142
+ }
143
+ ],
144
+ "webhook_url": "https://example.com/webhook",
145
+ "id": "request-123",
146
+ "language": "en"
147
+ }
148
+ ```
149
+
150
+ #### Example 4: Excluding Time Ranges from Subtitle Generation
151
+ ```json
152
+ {
153
+ "media_url": "https://example.com/video.mp4",
154
+ "settings": {
155
+ "style": "classic",
156
+ "line_color": "#FFFFFF",
157
+ "outline_color": "#000000",
158
+ "position": "bottom_center",
159
+ "font_family": "Arial",
160
+ "font_size": 24
161
+ },
162
+ "exclude_time_ranges": [
163
+ { "start": "00:00:10.000", "end": "00:00:20.000" },
164
+ { "start": "00:00:30.000", "end": "00:00:40.000" }
165
+ ]
166
+ }
167
+ ```
168
+
169
+ #### Example 5: Generating Subtitles for an MP3 (Audio) File
170
+ ```json
171
+ {
172
+ "canvas_width": 1280,
173
+ "canvas_height": 720,
174
+ "media_url": "https://example.com/audio.mp3",
175
+ "settings": {
176
+ "style": "classic",
177
+ "font_family": "Arial",
178
+ "font_size": 32,
179
+ "line_color": "#FFFFFF",
180
+ "outline_color": "#000000"
181
+ }
182
+ }
183
+ ```
184
+
185
+
186
+ ```bash
187
+ curl -X POST \
188
+ -H "x-api-key: YOUR_API_KEY" \
189
+ -H "Content-Type: application/json" \
190
+ -d '{
191
+ "media_url": "https://example.com/video.mp4",
192
+ "settings": {
193
+ "line_color": "#FFFFFF",
194
+ "word_color": "#FFFF00",
195
+ "outline_color": "#000000",
196
+ "all_caps": false,
197
+ "max_words_per_line": 10,
198
+ "position": "bottom_center",
199
+ "alignment": "center",
200
+ "font_family": "Arial",
201
+ "font_size": 24,
202
+ "style": "karaoke",
203
+ "outline_width": 2
204
+ },
205
+ "replace": [
206
+ {
207
+ "find": "um",
208
+ "replace": ""
209
+ }
210
+ ],
211
+ "id": "custom-request-id"
212
+ }' \
213
+ https://your-api-endpoint.com/v1/media/generate/ass
214
+ ```
215
+
216
+ ## 4. Response
217
+
218
+ ### Success Response
219
+
220
+ The response will be a JSON object with the following properties:
221
+
222
+ - `code` (integer): The HTTP status code (200 for success).
223
+ - `id` (string): The request identifier, if provided in the request.
224
+ - `job_id` (string): A unique identifier for the job.
225
+ - `response` (string): The cloud URL of the generated ASS subtitle file.
226
+ - `message` (string): A success message.
227
+ - `pid` (integer): The process ID of the worker that processed the request.
228
+ - `queue_id` (integer): The ID of the queue used for processing the request.
229
+ - `run_time` (float): The time taken to process the request (in seconds).
230
+ - `queue_time` (float): The time the request spent in the queue (in seconds).
231
+ - `total_time` (float): The total time taken for the request (in seconds).
232
+ - `queue_length` (integer): The current length of the processing queue.
233
+ - `build_number` (string): The build number of the application.
234
+
235
+ Example:
236
+
237
+ ```json
238
+ {
239
+ "code": 200,
240
+ "id": "request-123",
241
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
242
+ "response": "https://cloud.example.com/generated-subtitles.ass",
243
+ "message": "success",
244
+ "pid": 12345,
245
+ "queue_id": 140682639937472,
246
+ "run_time": 2.345,
247
+ "queue_time": 0.010,
248
+ "total_time": 2.355,
249
+ "queue_length": 0,
250
+ "build_number": "1.0.0"
251
+ }
252
+ ```
253
+
254
+ ### Error Responses
255
+
256
+ #### Missing or Invalid Parameters
257
+
258
+ **Status Code:** 400 Bad Request
259
+
260
+ ```json
261
+ {
262
+ "code": 400,
263
+ "id": "request-123",
264
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
265
+ "message": "Missing or invalid parameters",
266
+ "pid": 12345,
267
+ "queue_id": 140682639937472,
268
+ "queue_length": 0,
269
+ "build_number": "1.0.0"
270
+ }
271
+ ```
272
+
273
+ #### Font Error
274
+
275
+ **Status Code:** 400 Bad Request
276
+
277
+ ```json
278
+ {
279
+ "code": 400,
280
+ "error": "The requested font 'InvalidFont' is not available. Please choose from the available fonts.",
281
+ "available_fonts": ["Arial", "Times New Roman", "Courier New", ...],
282
+ "pid": 12345,
283
+ "queue_id": 140682639937472,
284
+ "queue_length": 0,
285
+ "build_number": "1.0.0"
286
+ }
287
+ ```
288
+
289
+ #### Internal Server Error
290
+
291
+ **Status Code:** 500 Internal Server Error
292
+
293
+ ```json
294
+ {
295
+ "code": 500,
296
+ "id": "request-123",
297
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
298
+ "error": "An unexpected error occurred during the subtitle generation process.",
299
+ "pid": 12345,
300
+ "queue_id": 140682639937472,
301
+ "queue_length": 0,
302
+ "build_number": "1.0.0"
303
+ }
304
+ ```
305
+
306
+ ## 5. Error Handling
307
+
308
+ The endpoint handles the following common errors:
309
+
310
+ - **Missing or Invalid Parameters**: If any required parameters are missing or invalid, a 400 Bad Request error is returned with a descriptive error message.
311
+ - **Font Error**: If the requested font is not available, a 400 Bad Request error is returned with a list of available fonts.
312
+ - **Internal Server Error**: If an unexpected error occurs during the subtitle generation process, a 500 Internal Server Error is returned with an error message.
313
+
314
+ Additionally, the main application context (`app.py`) includes error handling for queue overload. If the maximum queue length (`MAX_QUEUE_LENGTH`) is set and the queue size reaches that limit, a 429 Too Many Requests error is returned with a descriptive message.
315
+
316
+ ## 6. Usage Notes
317
+
318
+ - The `media_url` parameter must be a valid URL pointing to a video or audio file.
319
+ - The `settings` parameter allows for customization of the subtitle appearance and behavior:
320
+ - `style` determines how subtitles are displayed, with options including:
321
+ - `classic`: Regular subtitle with all text displayed at once
322
+ - `karaoke`: Highlights words sequentially in a karaoke style as they're spoken
323
+ - `highlight`: Shows the full subtitle text but highlights each word as it's spoken
324
+ - `underline`: Shows the full subtitle text but underlines each word as it's spoken
325
+ - `word_by_word`: Shows only one word at a time
326
+ - `position` can be used to place subtitles in one of nine positions on the screen
327
+ - `alignment` determines text alignment within the position (left, center, right)
328
+ - `font_family` can be any available system font
329
+ - Color options can be set using hex codes (e.g., "#FFFFFF" for white)
330
+ - The `replace` parameter can be used to perform text replacements in the subtitles (useful for correcting words or censoring content).
331
+ - The `webhook_url` parameter is optional and can be used to receive a notification when the subtitle generation process is complete.
332
+ - The `id` parameter is optional and can be used to identify the request in webhook responses.
333
+ - The `language` parameter is optional and can be used to specify the language of the subtitles for transcription. If not provided, the language will be automatically detected.
334
+ - The `exclude_time_ranges` parameter can be used to specify time ranges to be excluded from subtitle generation.
335
+ - If either `canvas_width` or `canvas_height` is provided, both must be provided and must be greater than 0.
336
+
337
+ ## 7. Common Issues
338
+
339
+ - Providing an invalid or inaccessible `media_url`.
340
+ - Requesting an unavailable font in the `settings` object.
341
+ - Using this endpoint with an audio-only file (e.g., MP3) and not providing both `canvas_width` and `canvas_height`. For audio files, you must specify both dimensions to generate a valid ASS subtitle file.
342
+ - Exceeding the maximum queue length, resulting in a 429 Too Many Requests error.
343
+
344
+ ## 8. Best Practices
345
+
346
+ - Validate the `media_url` parameter before sending the request to ensure it points to a valid and accessible media file.
347
+ - Use the `webhook_url` parameter to receive notifications about the subtitle generation process, rather than polling the API for updates.
348
+ - Provide descriptive and meaningful `id` values to easily identify requests in logs and responses.
349
+ - Use the `replace` parameter judiciously to avoid unintended text replacements in the subtitles.
350
+ - Consider caching the generated ASS files for frequently requested media to improve performance and reduce processing time.
docs/media/media_transcribe.md ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media Transcription API Documentation
2
+
3
+ ## Overview
4
+ The Media Transcription endpoint is part of the v1 API suite, providing audio/video transcription and translation capabilities. This endpoint leverages a queuing system for handling long-running transcription tasks, with webhook support for asynchronous processing. It's integrated into the main Flask application as a Blueprint and supports both direct response and cloud storage options for the transcription results.
5
+
6
+ ## Endpoint
7
+ - **URL**: `/v1/media/transcribe`
8
+ - **Method**: `POST`
9
+ - **Blueprint**: `v1_media_transcribe_bp`
10
+
11
+ ## Request
12
+
13
+ ### Headers
14
+ - `x-api-key`: Required. Authentication key for API access.
15
+ - `Content-Type`: Required. Must be `application/json`.
16
+
17
+ ### Body Parameters
18
+
19
+ #### Required Parameters
20
+ - `media_url` (string)
21
+ - Format: URI
22
+ - Description: URL of the media file to be transcribed
23
+
24
+ #### Optional Parameters
25
+ - `task` (string)
26
+ - Allowed values: `"transcribe"`, `"translate"`
27
+ - Default: `"transcribe"`
28
+ - Description: Specifies whether to transcribe or translate the audio
29
+
30
+ - `include_text` (boolean)
31
+ - Default: `true`
32
+ - Description: Include plain text transcription in the response
33
+
34
+ - `include_srt` (boolean)
35
+ - Default: `false`
36
+ - Description: Include SRT format subtitles in the response
37
+
38
+ - `include_segments` (boolean)
39
+ - Default: `false`
40
+ - Description: Include timestamped segments in the response
41
+
42
+ - `word_timestamps` (boolean)
43
+ - Default: `false`
44
+ - Description: Include timestamps for individual words
45
+
46
+ - `response_type` (string)
47
+ - Allowed values: `"direct"`, `"cloud"`
48
+ - Default: `"direct"`
49
+ - Description: Whether to return results directly or as cloud storage URLs
50
+
51
+ - `language` (string)
52
+ - Optional
53
+ - Description: Source language code for transcription
54
+
55
+ - `webhook_url` (string)
56
+ - Format: URI
57
+ - Description: URL to receive the transcription results asynchronously
58
+
59
+ - `id` (string)
60
+ - Description: Custom identifier for the transcription job
61
+
62
+ - `max_words_per_line` (integer)
63
+ - Minimum: 1
64
+ - Description: Controls the maximum number of words per line in the SRT file. When specified, each segment's text will be split into multiple lines with at most the specified number of words per line.
65
+
66
+ ### Example Request
67
+
68
+ ```bash
69
+ curl -X POST "https://api.example.com/v1/media/transcribe" \
70
+ -H "x-api-key: your_api_key" \
71
+ -H "Content-Type: application/json" \
72
+ -d '{
73
+ "media_url": "https://example.com/media/file.mp3",
74
+ "task": "transcribe",
75
+ "include_text": true,
76
+ "include_srt": true,
77
+ "include_segments": true,
78
+ "response_type": "cloud",
79
+ "webhook_url": "https://your-webhook.com/callback",
80
+ "id": "custom-job-123",
81
+ "max_words_per_line": 5
82
+ }'
83
+ ```
84
+
85
+ ## Response
86
+
87
+ ### Immediate Response (202 Accepted)
88
+ When a webhook URL is provided, the API returns an immediate acknowledgment:
89
+
90
+ ```json
91
+ {
92
+ "code": 202,
93
+ "id": "custom-job-123",
94
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
95
+ "message": "processing",
96
+ "pid": 12345,
97
+ "queue_id": 67890,
98
+ "max_queue_length": "unlimited",
99
+ "queue_length": 1,
100
+ "build_number": "1.0.0"
101
+ }
102
+ ```
103
+
104
+ ### Success Response (via Webhook)
105
+ For direct response_type:
106
+
107
+ ```json
108
+ {
109
+ "endpoint": "/v1/transcribe/media",
110
+ "code": 200,
111
+ "id": "custom-job-123",
112
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
113
+ "response": {
114
+ "text": "Transcribed text content...",
115
+ "srt": "SRT formatted content...",
116
+ "segments": [...],
117
+ "text_url": null,
118
+ "srt_url": null,
119
+ "segments_url": null
120
+ },
121
+ "message": "success",
122
+ "pid": 12345,
123
+ "queue_id": 67890,
124
+ "run_time": 5.234,
125
+ "queue_time": 0.123,
126
+ "total_time": 5.357,
127
+ "queue_length": 0,
128
+ "build_number": "1.0.0"
129
+ }
130
+ ```
131
+
132
+ For cloud response_type:
133
+
134
+ ```json
135
+ {
136
+ "endpoint": "/v1/transcribe/media",
137
+ "code": 200,
138
+ "id": "custom-job-123",
139
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
140
+ "response": {
141
+ "text": null,
142
+ "srt": null,
143
+ "segments": null,
144
+ "text_url": "https://storage.example.com/text.txt",
145
+ "srt_url": "https://storage.example.com/subtitles.srt",
146
+ "segments_url": "https://storage.example.com/segments.json"
147
+ },
148
+ "message": "success",
149
+ "pid": 12345,
150
+ "queue_id": 67890,
151
+ "run_time": 5.234,
152
+ "queue_time": 0.123,
153
+ "total_time": 5.357,
154
+ "queue_length": 0,
155
+ "build_number": "1.0.0"
156
+ }
157
+ ```
158
+
159
+ ### Error Responses
160
+
161
+ #### Queue Full (429 Too Many Requests)
162
+ ```json
163
+ {
164
+ "code": 429,
165
+ "id": "custom-job-123",
166
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
167
+ "message": "MAX_QUEUE_LENGTH (100) reached",
168
+ "pid": 12345,
169
+ "queue_id": 67890,
170
+ "queue_length": 100,
171
+ "build_number": "1.0.0"
172
+ }
173
+ ```
174
+
175
+ #### Server Error (500 Internal Server Error)
176
+ ```json
177
+ {
178
+ "endpoint": "/v1/transcribe/media",
179
+ "code": 500,
180
+ "id": "custom-job-123",
181
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
182
+ "response": null,
183
+ "message": "Error message details",
184
+ "pid": 12345,
185
+ "queue_id": 67890,
186
+ "run_time": 0.123,
187
+ "queue_time": 0.056,
188
+ "total_time": 0.179,
189
+ "queue_length": 1,
190
+ "build_number": "1.0.0"
191
+ }
192
+ ```
193
+
194
+ ## Error Handling
195
+
196
+ ### Common Errors
197
+ - **Invalid API Key**: 401 Unauthorized
198
+ - **Invalid JSON Payload**: 400 Bad Request
199
+ - **Missing Required Fields**: 400 Bad Request
200
+ - **Invalid media_url**: 400 Bad Request
201
+ - **Queue Full**: 429 Too Many Requests
202
+ - **Processing Error**: 500 Internal Server Error
203
+
204
+ ### Validation Errors
205
+ The endpoint performs strict validation of the request payload using JSON Schema. Common validation errors include:
206
+ - Invalid URI format for media_url or webhook_url
207
+ - Invalid task value (must be "transcribe" or "translate")
208
+ - Invalid response_type value (must be "direct" or "cloud")
209
+ - Unknown properties in the request body
210
+
211
+ ## Usage Notes
212
+
213
+ 1. **Webhook Processing**
214
+ - When a webhook_url is provided, the request is processed asynchronously
215
+ - The API returns an immediate 202 response with a job_id
216
+ - Final results are sent to the webhook_url when processing completes
217
+
218
+ 2. **Queue Management**
219
+ - Requests with webhook_url are queued for processing
220
+ - MAX_QUEUE_LENGTH environment variable controls queue size
221
+ - Set MAX_QUEUE_LENGTH to 0 for unlimited queue size
222
+
223
+ 3. **File Management**
224
+ - For cloud response_type, temporary files are automatically cleaned up
225
+ - Results are uploaded to cloud storage before deletion
226
+ - URLs in the response provide access to the stored files
227
+
228
+ 4. **SRT Formatting**
229
+ - The `max_words_per_line` parameter allows control over the maximum number of words per line in the SRT file
230
+ - When specified, each segment's text will be split into multiple lines with at most the specified number of words per line
231
+ - This is useful for creating more readable subtitles with consistent line lengths
232
+
233
+ ## Common Issues
234
+
235
+ 1. **Media Access**
236
+ - Ensure media_url is publicly accessible
237
+ - Verify media file format is supported
238
+ - Check for media file corruption
239
+
240
+ 2. **Webhook Delivery**
241
+ - Ensure webhook_url is publicly accessible
242
+ - Implement webhook endpoint retry logic
243
+ - Monitor webhook endpoint availability
244
+
245
+ 3. **Resource Usage**
246
+ - Large media files may take significant processing time
247
+ - Monitor queue length for production deployments
248
+ - Consider implementing request size limits
249
+
250
+ ## Best Practices
251
+
252
+ 1. **Request Handling**
253
+ - Always provide a unique id for job tracking
254
+ - Implement webhook retry logic
255
+ - Store job_id for result correlation
256
+
257
+ 2. **Resource Management**
258
+ - Monitor queue length in production
259
+ - Implement appropriate timeout handling
260
+ - Use cloud response_type for large files
261
+
262
+ 3. **Error Handling**
263
+ - Implement comprehensive webhook error handling
264
+ - Log job_id with all related operations
265
+ - Monitor processing times and error rates
266
+
267
+ 4. **Security**
268
+ - Use HTTPS for media_url and webhook_url
269
+ - Implement webhook authentication
270
+ - Validate media file types before processing
docs/media/metadata.md ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Media Metadata
2
+
3
+ This endpoint extracts detailed metadata from media files (video, audio, image) including format, duration, codec information, resolution, and bitrates.
4
+
5
+ ## Endpoint
6
+
7
+ `POST /v1/media/metadata`
8
+
9
+ ## Authentication
10
+
11
+ This endpoint requires API authentication. See [Authentication](../toolkit/authenticate.md) for details.
12
+
13
+ ## Request
14
+
15
+ ```json
16
+ {
17
+ "media_url": "https://example.com/media.mp4",
18
+ "webhook_url": "https://example.com/webhook", // Optional
19
+ "id": "custom-id" // Optional
20
+ }
21
+ ```
22
+
23
+ | Parameter | Type | Required | Description |
24
+ |-----------|------|----------|-------------|
25
+ | media_url | string | Yes | URL of the media file to analyze |
26
+ | webhook_url | string | No | URL to receive the processing result |
27
+ | id | string | No | Custom identifier for tracking the request |
28
+
29
+ ## Response
30
+
31
+ **Success (200 OK)**
32
+
33
+ ```json
34
+ {
35
+ "code": 200,
36
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
37
+ "id": "custom-id",
38
+ "response": {
39
+ "filesize": 15679283,
40
+ "filesize_mb": 14.95,
41
+ "duration": 87.46,
42
+ "duration_formatted": "00:01:27.46",
43
+ "format": "mp4,mov,m4a,3gp,3g2,mj2",
44
+ "overall_bitrate": 1438692,
45
+ "overall_bitrate_mbps": 1.44,
46
+ "has_video": true,
47
+ "video_codec": "h264",
48
+ "video_codec_long": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
49
+ "width": 1920,
50
+ "height": 1080,
51
+ "resolution": "1920x1080",
52
+ "fps": 30.0,
53
+ "video_bitrate": 1300000,
54
+ "video_bitrate_mbps": 1.3,
55
+ "pixel_format": "yuv420p",
56
+ "has_audio": true,
57
+ "audio_codec": "aac",
58
+ "audio_codec_long": "AAC (Advanced Audio Coding)",
59
+ "audio_channels": 2,
60
+ "audio_sample_rate": 48000,
61
+ "audio_sample_rate_khz": 48.0,
62
+ "audio_bitrate": 128000,
63
+ "audio_bitrate_kbps": 128
64
+ },
65
+ "message": "success",
66
+ "run_time": 0.542,
67
+ "queue_time": 0,
68
+ "total_time": 0.542,
69
+ "pid": 12345,
70
+ "queue_id": 67890,
71
+ "queue_length": 0,
72
+ "build_number": "123"
73
+ }
74
+ ```
75
+
76
+ For audio-only files, video-related fields will not be included. Similarly, for video files without audio, audio-related fields will not be included.
77
+
78
+ **Queued (202 Accepted)**
79
+
80
+ ```json
81
+ {
82
+ "code": 202,
83
+ "id": "custom-id",
84
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
85
+ "message": "processing",
86
+ "pid": 12345,
87
+ "queue_id": 67890,
88
+ "max_queue_length": "unlimited",
89
+ "queue_length": 0,
90
+ "build_number": "123"
91
+ }
92
+ ```
93
+
94
+ **Error (4xx/5xx)**
95
+
96
+ ```json
97
+ {
98
+ "code": 500,
99
+ "id": "custom-id",
100
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
101
+ "message": "Error extracting metadata: [error details]",
102
+ "pid": 12345,
103
+ "queue_id": 67890,
104
+ "queue_length": 0,
105
+ "build_number": "123"
106
+ }
107
+ ```
108
+
109
+ ## Example
110
+
111
+ ### Request
112
+
113
+ ```bash
114
+ curl -X POST https://api.example.com/v1/media/metadata \
115
+ -H "Content-Type: application/json" \
116
+ -H "Authorization: Bearer your_api_key" \
117
+ -d '{
118
+ "media_url": "https://example.com/sample-video.mp4",
119
+ "webhook_url": "https://your-server.com/webhook"
120
+ }'
121
+ ```
122
+
123
+ ### Response
124
+
125
+ ```json
126
+ {
127
+ "code": 200,
128
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
129
+ "response": {
130
+ "filesize": 15679283,
131
+ "filesize_mb": 14.95,
132
+ "duration": 87.46,
133
+ "duration_formatted": "00:01:27.46",
134
+ "format": "mp4",
135
+ "overall_bitrate": 1438692,
136
+ "overall_bitrate_mbps": 1.44,
137
+ "has_video": true,
138
+ "video_codec": "h264",
139
+ "width": 1920,
140
+ "height": 1080,
141
+ "resolution": "1920x1080",
142
+ "fps": 30.0,
143
+ "video_bitrate": 1300000,
144
+ "video_bitrate_mbps": 1.3,
145
+ "has_audio": true,
146
+ "audio_codec": "aac",
147
+ "audio_channels": 2,
148
+ "audio_sample_rate": 48000,
149
+ "audio_bitrate": 128000,
150
+ "audio_bitrate_kbps": 128
151
+ },
152
+ "message": "success",
153
+ "run_time": 0.542,
154
+ "total_time": 0.542
155
+ }
156
+ ```
docs/media/silence.md ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Silence Detection Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/media/silence` endpoint is part of the Media API and is designed to detect silence intervals in a given media file. It takes a media URL, along with various parameters for configuring the silence detection process, and returns the detected silence intervals. This endpoint fits into the overall API structure as a part of the version 1 (v1) media-related endpoints.
6
+
7
+ ## 2. Endpoint
8
+
9
+ ```
10
+ POST /v1/media/silence
11
+ ```
12
+
13
+ ## 3. Request
14
+
15
+ ### Headers
16
+
17
+ - `x-api-key` (required): The API key for authentication.
18
+
19
+ ### Body Parameters
20
+
21
+ The request body should be a JSON object with the following parameters:
22
+
23
+ - `media_url` (required, string): The URL of the media file to be processed.
24
+ - `start` (optional, string): The start time for the silence detection process, in the format `HH:MM:SS.ms`. If not provided, the process will start from the beginning of the media file.
25
+ - `end` (optional, string): The end time for the silence detection process, in the format `HH:MM:SS.ms`. If not provided, the process will continue until the end of the media file.
26
+ - `noise` (optional, string): The noise threshold for silence detection, in decibels (dB). Default is `-30dB`.
27
+ - `duration` (required, number): The minimum duration (in seconds) for a silence interval to be considered valid.
28
+ - `mono` (optional, boolean): Whether to process the audio as mono (single channel) or not. Default is `true`.
29
+ - `webhook_url` (required, string): The URL to which the response should be sent as a webhook.
30
+ - `id` (required, string): A unique identifier for the request.
31
+
32
+ The `validate_payload` directive in the routes file enforces the following JSON schema for the request body:
33
+
34
+ ```python
35
+ {
36
+ "type": "object",
37
+ "properties": {
38
+ "media_url": {"type": "string", "format": "uri"},
39
+ "start": {"type": "string"},
40
+ "end": {"type": "string"},
41
+ "noise": {"type": "string"},
42
+ "duration": {"type": "number", "minimum": 0.1},
43
+ "mono": {"type": "boolean"},
44
+ "webhook_url": {"type": "string", "format": "uri"},
45
+ "id": {"type": "string"}
46
+ },
47
+ "required": ["media_url", "duration"],
48
+ "additionalProperties": False
49
+ }
50
+ ```
51
+
52
+ ### Example Request
53
+
54
+ ```json
55
+ {
56
+ "media_url": "https://example.com/audio.mp3",
57
+ "start": "00:00:10.0",
58
+ "end": "00:01:00.0",
59
+ "noise": "-25dB",
60
+ "duration": 0.5,
61
+ "mono": false,
62
+ "webhook_url": "https://example.com/webhook",
63
+ "id": "unique-request-id"
64
+ }
65
+ ```
66
+
67
+ ```
68
+ curl -X POST \
69
+ https://api.example.com/v1/media/silence \
70
+ -H 'x-api-key: YOUR_API_KEY' \
71
+ -H 'Content-Type: application/json' \
72
+ -d '{
73
+ "media_url": "https://example.com/audio.mp3",
74
+ "start": "00:00:10.0",
75
+ "end": "00:01:00.0",
76
+ "noise": "-25dB",
77
+ "duration": 0.5,
78
+ "mono": false,
79
+ "webhook_url": "https://example.com/webhook",
80
+ "id": "unique-request-id"
81
+ }'
82
+ ```
83
+
84
+ ## 4. Response
85
+
86
+ ### Success Response
87
+
88
+ The success response will be sent as a webhook to the specified `webhook_url`. The response format follows the general response structure defined in the main application context (`app.py`). Here's an example:
89
+
90
+ ```json
91
+ {
92
+ "endpoint": "/v1/media/silence",
93
+ "code": 200,
94
+ "id": "unique-request-id",
95
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
96
+ "response": [
97
+ {
98
+ "start": 10.5,
99
+ "end": 15.2
100
+ },
101
+ {
102
+ "start": 20.0,
103
+ "end": 25.7
104
+ }
105
+ ],
106
+ "message": "success",
107
+ "pid": 12345,
108
+ "queue_id": 1234567890,
109
+ "run_time": 1.234,
110
+ "queue_time": 0.123,
111
+ "total_time": 1.357,
112
+ "queue_length": 0,
113
+ "build_number": "1.0.0"
114
+ }
115
+ ```
116
+
117
+ ### Error Responses
118
+
119
+ - **400 Bad Request**: This error is returned when the request body is missing or contains invalid parameters. Example response:
120
+
121
+ ```json
122
+ {
123
+ "code": 400,
124
+ "message": "Invalid request payload"
125
+ }
126
+ ```
127
+
128
+ - **401 Unauthorized**: This error is returned when the `x-api-key` header is missing or invalid. Example response:
129
+
130
+ ```json
131
+ {
132
+ "code": 401,
133
+ "message": "Unauthorized"
134
+ }
135
+ ```
136
+
137
+ - **500 Internal Server Error**: This error is returned when an unexpected error occurs during the silence detection process. Example response:
138
+
139
+ ```json
140
+ {
141
+ "code": 500,
142
+ "message": "An error occurred during the silence detection process"
143
+ }
144
+ ```
145
+
146
+ ## 5. Error Handling
147
+
148
+ The endpoint handles the following common errors:
149
+
150
+ - Missing or invalid request parameters: Returns a 400 Bad Request error.
151
+ - Missing or invalid `x-api-key` header: Returns a 401 Unauthorized error.
152
+ - Unexpected exceptions during the silence detection process: Returns a 500 Internal Server Error.
153
+
154
+ The main application context (`app.py`) also includes error handling for situations where the task queue has reached its maximum length (`MAX_QUEUE_LENGTH`). In such cases, a 429 Too Many Requests error is returned.
155
+
156
+ ## 6. Usage Notes
157
+
158
+ - The `media_url` parameter should point to a valid media file that can be processed by the silence detection service.
159
+ - The `start` and `end` parameters are optional and can be used to specify a time range within the media file for silence detection.
160
+ - The `noise` parameter allows you to adjust the noise threshold for silence detection. Lower values (e.g., `-40dB`) will detect more silence intervals, while higher values (e.g., `-20dB`) will detect fewer silence intervals.
161
+ - The `duration` parameter specifies the minimum duration (in seconds) for a silence interval to be considered valid. This can be useful for filtering out very short silence intervals that may not be relevant.
162
+ - The `mono` parameter determines whether the audio should be processed as a single channel (mono) or multiple channels (stereo or surround).
163
+
164
+ ## 7. Common Issues
165
+
166
+ - Providing an invalid or inaccessible `media_url`.
167
+ - Specifying `start` and `end` times that are outside the duration of the media file.
168
+ - Setting the `duration` parameter to an unreasonably low value, which may result in detecting too many short silence intervals.
169
+
170
+ ## 8. Best Practices
171
+
172
+ - Validate the `media_url` parameter to ensure it points to a valid and accessible media file.
173
+ - Consider using the `start` and `end` parameters to focus the silence detection on a specific time range within the media file, if needed.
174
+ - Adjust the `noise` and `duration` parameters based on your specific use case and requirements for silence detection.
175
+ - If you need to process stereo or surround audio, set the `mono` parameter to `false`.
176
+ - Monitor the response from the endpoint to ensure that the silence detection process completed successfully and that the detected silence intervals meet your expectations.
docs/s3/upload.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # S3 Upload API
2
+
3
+ This endpoint allows you to stream a file from a remote URL directly to an S3-compatible storage service without using local disk space.
4
+
5
+ ## Endpoint
6
+
7
+ `POST /v1/s3/upload`
8
+
9
+ ## Authentication
10
+
11
+ This endpoint requires an API key to be provided in the `X-API-Key` header.
12
+
13
+ ## Request Body
14
+
15
+ The request body should be a JSON object with the following properties:
16
+
17
+ | Property | Type | Required | Description |
18
+ |----------|------|----------|-------------|
19
+ | file_url | string | Yes | The URL of the file to upload to S3 |
20
+ | filename | string | No | Custom filename to use for the uploaded file. If not provided, the original filename will be used |
21
+ | public | boolean | No | Whether to make the file publicly accessible. Defaults to `false` |
22
+
23
+ Example request body:
24
+ ```json
25
+ {
26
+ "file_url": "https://example.com/path/to/file.mp4",
27
+ "filename": "custom-name.mp4",
28
+ "public": true
29
+ }
30
+ ```
31
+
32
+ ## Response
33
+
34
+ The response will be a JSON object with the following properties:
35
+
36
+ | Property | Type | Description |
37
+ |----------|------|-------------|
38
+ | url | string | The URL of the uploaded file. For public files, this is a direct URL. For private files, this is a pre-signed URL that will expire after 1 hour |
39
+ | filename | string | The filename of the uploaded file |
40
+ | bucket | string | The name of the S3 bucket where the file was uploaded |
41
+ | public | boolean | Whether the file is publicly accessible |
42
+
43
+ Example response:
44
+ ```json
45
+ {
46
+ "url": "https://bucket-name.s3.region.amazonaws.com/custom-name.mp4",
47
+ "filename": "custom-name.mp4",
48
+ "bucket": "bucket-name",
49
+ "public": true
50
+ }
51
+ ```
52
+
53
+ ## Error Handling
54
+
55
+ If an error occurs, the response will include an error message with an appropriate HTTP status code.
56
+
57
+ ## Technical Details
58
+
59
+ This endpoint uses the S3-compatible multipart upload API to stream the file directly from the source URL to S3 without saving it locally. This allows for efficient transfer of large files with minimal memory usage.
60
+
61
+ The implementation:
62
+ 1. Streams the file from the source URL in chunks
63
+ 2. Uploads each chunk to S3 as a part of a multipart upload
64
+ 3. Completes the multipart upload once all parts are uploaded
65
+
66
+ This approach supports resumable uploads and can handle large files efficiently.
docs/toolkit/authenticate.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Authenticate Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/toolkit/authenticate` endpoint is a part of the `v1_toolkit_auth` blueprint in the API structure. Its purpose is to authenticate requests by verifying the provided API key against a predefined value. This endpoint serves as a gatekeeper, ensuring that only authorized clients can access the API's resources.
6
+
7
+ ## 2. Endpoint
8
+
9
+ - URL Path: `/v1/toolkit/authenticate`
10
+ - HTTP Method: `GET`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `X-API-Key` (required): The API key used for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ This endpoint does not require any request body parameters.
21
+
22
+ ### Example Request
23
+
24
+ ```bash
25
+ curl -X GET -H "X-API-Key: YOUR_API_KEY" http://localhost:8080/v1/toolkit/authenticate
26
+ ```
27
+
28
+ ## 4. Response
29
+
30
+ ### Success Response
31
+
32
+ If the provided API key matches the predefined value, the endpoint will return a 200 OK status code with the following response:
33
+
34
+ ```json
35
+ {
36
+ "code": 200,
37
+ "endpoint": "/authenticate",
38
+ "id": null,
39
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
40
+ "message": "success",
41
+ "pid": 12345,
42
+ "queue_id": 1234567890,
43
+ "queue_length": 0,
44
+ "response": "Authorized",
45
+ "run_time": 0.001,
46
+ "total_time": 0.001,
47
+ "queue_time": 0,
48
+ "build_number": "1.0.0"
49
+ }
50
+ ```
51
+
52
+ ### Error Responses
53
+
54
+ If the provided API key is invalid or missing, the endpoint will return a 401 Unauthorized status code with the following response:
55
+
56
+ ```json
57
+ {
58
+ "code": 401,
59
+ "endpoint": "/authenticate",
60
+ "id": null,
61
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
62
+ "message": "Unauthorized",
63
+ "pid": 12345,
64
+ "queue_id": 1234567890,
65
+ "queue_length": 0,
66
+ "response": null,
67
+ "run_time": 0.001,
68
+ "total_time": 0.001,
69
+ "queue_time": 0,
70
+ "build_number": "1.0.0"
71
+ }
72
+ ```
73
+
74
+ ## 5. Error Handling
75
+
76
+ The main error that can occur with this endpoint is providing an invalid or missing API key. In this case, the endpoint will return a 401 Unauthorized status code with an appropriate error message.
77
+
78
+ ## 6. Usage Notes
79
+
80
+ - This endpoint is designed to be used as a gatekeeper for the API, ensuring that only authorized clients can access the API's resources.
81
+ - The API key should be kept secure and should not be shared with unauthorized parties.
82
+
83
+ ## 7. Common Issues
84
+
85
+ - Forgetting to include the `X-API-Key` header in the request.
86
+ - Using an invalid or expired API key.
87
+
88
+ ## 8. Best Practices
89
+
90
+ - Rotate API keys periodically to enhance security.
91
+ - Store API keys securely and avoid committing them to version control systems.
92
+ - Consider implementing additional security measures, such as rate limiting or IP whitelisting, to further protect the API.
docs/toolkit/job_status.md ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Job Status Endpoint Documentation
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/toolkit/job/status` endpoint is part of the Toolkit API and is used to retrieve the status of a specific job. It fits into the overall API structure as a utility endpoint for monitoring and managing jobs submitted to the various media processing endpoints.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/toolkit/job/status`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following parameter:
21
+
22
+ - `job_id` (string, required): The unique identifier of the job for which the status is requested.
23
+
24
+ The `validate_payload` directive in the routes file enforces the following JSON schema for the request body:
25
+
26
+ ```python
27
+ {
28
+ "type": "object",
29
+ "properties": {
30
+ "job_id": {
31
+ "type": "string"
32
+ }
33
+ },
34
+ "required": ["job_id"],
35
+ }
36
+ ```
37
+
38
+ ### Example Request
39
+
40
+ ```json
41
+ {
42
+ "job_id": "e6d7f3c0-9c9f-4b8a-b7c3-f0e3c9f6b9d7"
43
+ }
44
+ ```
45
+
46
+ ```bash
47
+ curl -X POST \
48
+ -H "x-api-key: YOUR_API_KEY" \
49
+ -H "Content-Type: application/json" \
50
+ -d '{"job_id": "e6d7f3c0-9c9f-4b8a-b7c3-f0e3c9f6b9d7"}' \
51
+ http://your-api-endpoint/v1/toolkit/job/status
52
+ ```
53
+
54
+ ## 4. Response
55
+
56
+ ### Success Response
57
+
58
+ The success response will contain the job status file content directly, as shown in the example response from `app.py`:
59
+
60
+ ```json
61
+ {
62
+ "endpoint": "/v1/toolkit/job/status",
63
+ "code": 200,
64
+ "id": null,
65
+ "job_id": "e6d7f3c0-9c9f-4b8a-b7c3-f0e3c9f6b9d7",
66
+ "response": {
67
+ "job_status": "done",
68
+ "job_id": "e6d7f3c0-9c9f-4b8a-b7c3-f0e3c9f6b9d7",
69
+ "queue_id": 140368864456064,
70
+ "process_id": 123456,
71
+ "response": {
72
+ "endpoint": "/v1/media/transcribe",
73
+ "code": 200,
74
+ "id": "transcribe_job_123",
75
+ "job_id": "e6d7f3c0-9c9f-4b8a-b7c3-f0e3c9f6b9d7",
76
+ "response": "Transcription completed successfully.",
77
+ "message": "success",
78
+ "pid": 123456,
79
+ "queue_id": 140368864456064,
80
+ "run_time": 5.234,
81
+ "queue_time": 1.123,
82
+ "total_time": 6.357,
83
+ "queue_length": 0,
84
+ "build_number": "1.0.0"
85
+ }
86
+ },
87
+ "message": "success",
88
+ "pid": 123456,
89
+ "queue_id": 140368864456064,
90
+ "run_time": 0.001,
91
+ "queue_time": 0.0,
92
+ "total_time": 0.001,
93
+ "queue_length": 0,
94
+ "build_number": "1.0.0"
95
+ }
96
+ ```
97
+
98
+ ### Error Responses
99
+
100
+ - **404 Not Found**: If the job with the provided `job_id` is not found, the response will be:
101
+
102
+ ```json
103
+ {
104
+ "error": "Job not found",
105
+ "job_id": "e6d7f3c0-9c9f-4b8a-b7c3-f0e3c9f6b9d7"
106
+ }
107
+ ```
108
+
109
+ - **500 Internal Server Error**: If an unexpected error occurs while retrieving the job status, the response will be:
110
+
111
+ ```json
112
+ {
113
+ "error": "Failed to retrieve job status: <error_message>",
114
+ "code": 500
115
+ }
116
+ ```
117
+
118
+ ## 5. Error Handling
119
+
120
+ - **Missing or Invalid Parameters**: If the `job_id` parameter is missing or invalid, the request will be rejected with a 400 Bad Request error.
121
+ - **Job Not Found**: If the job with the provided `job_id` is not found, a 404 Not Found error will be returned.
122
+ - **Unexpected Errors**: Any unexpected errors that occur during the retrieval of the job status will result in a 500 Internal Server Error response.
123
+
124
+ The main application context (`app.py`) includes error handling for queue overflow situations, where a 429 Too Many Requests error is returned if the maximum queue length is reached.
125
+
126
+ ## 6. Usage Notes
127
+
128
+ - Ensure that you have a valid API key for authentication.
129
+ - The `job_id` parameter must be a valid UUID string representing an existing job.
130
+ - This endpoint does not perform any media processing; it only retrieves the status of a previously submitted job.
131
+
132
+ ## 7. Common Issues
133
+
134
+ - Providing an invalid or non-existent `job_id`.
135
+ - Attempting to retrieve the status of a job that has already been processed and removed from the system.
136
+
137
+ ## 8. Best Practices
138
+
139
+ - Use this endpoint to monitor the progress of long-running jobs or to check the status of completed jobs.
140
+ - Implement proper error handling in your client application to handle different error scenarios, such as job not found or unexpected errors.
141
+ - Consider rate-limiting or implementing a queue system on the client side to avoid overwhelming the API with too many requests.
docs/toolkit/jobs_status.md ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Get All Jobs Status
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/toolkit/jobs/status` endpoint is part of the Toolkit API and is used to retrieve the status of all jobs within a specified time range. This endpoint fits into the overall API structure by providing a way to monitor and manage the jobs that have been submitted to the system. It is a part of the `v1_toolkit_jobs_status_bp` blueprint, which is registered in the main `app.py` file.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/toolkit/jobs/status`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ - `since_seconds` (optional, number): The number of seconds to look back for jobs. If not provided, the default value is 600 seconds (10 minutes).
21
+
22
+ The JSON payload is completely optional. If no payload is provided or if the payload is empty, the endpoint will use the default value of 600 seconds.
23
+
24
+ ### Example Request
25
+
26
+ ```json
27
+ {
28
+ "since_seconds": 3600
29
+ }
30
+ ```
31
+
32
+ Or with no body:
33
+
34
+ ```bash
35
+ curl -X POST \
36
+ -H "x-api-key: YOUR_API_KEY" \
37
+ -H "Content-Type: application/json" \
38
+ http://your-api-url/v1/toolkit/jobs/status
39
+ ```
40
+
41
+ With body:
42
+
43
+ ```bash
44
+ curl -X POST \
45
+ -H "x-api-key: YOUR_API_KEY" \
46
+ -H "Content-Type: application/json" \
47
+ -d '{"since_seconds": 3600}' \
48
+ http://your-api-url/v1/toolkit/jobs/status
49
+ ```
50
+
51
+ ## 4. Response
52
+
53
+ ### Success Response
54
+
55
+ The response will be a JSON object containing the job statuses for all jobs within the specified time range. The response format follows the general response structure defined in `app.py`.
56
+
57
+ ```json
58
+ {
59
+ "code": 200,
60
+ "id": null,
61
+ "job_id": "job_id_value",
62
+ "response": {
63
+ "job_id_1": "job_status_1",
64
+ "job_id_2": "job_status_2",
65
+ ...
66
+ },
67
+ "message": "success",
68
+ "run_time": 0.123,
69
+ "queue_time": 0,
70
+ "total_time": 0.123,
71
+ "pid": 12345,
72
+ "queue_id": 1234567890,
73
+ "queue_length": 0,
74
+ "build_number": "1.0.0"
75
+ }
76
+ ```
77
+
78
+ ### Error Responses
79
+
80
+ - **404 Not Found**: If the jobs directory is not found.
81
+
82
+ ```json
83
+ {
84
+ "code": 404,
85
+ "id": null,
86
+ "job_id": "job_id_value",
87
+ "response": null,
88
+ "message": "Jobs directory not found",
89
+ "run_time": 0.123,
90
+ "queue_time": 0,
91
+ "total_time": 0.123,
92
+ "pid": 12345,
93
+ "queue_id": 1234567890,
94
+ "queue_length": 0,
95
+ "build_number": "1.0.0"
96
+ }
97
+ ```
98
+
99
+ - **500 Internal Server Error**: If an exception occurs while retrieving the job statuses.
100
+
101
+ ```json
102
+ {
103
+ "code": 500,
104
+ "id": null,
105
+ "job_id": "job_id_value",
106
+ "response": null,
107
+ "message": "Failed to retrieve job statuses: Error message",
108
+ "run_time": 0.123,
109
+ "queue_time": 0,
110
+ "total_time": 0.123,
111
+ "pid": 12345,
112
+ "queue_id": 1234567890,
113
+ "queue_length": 0,
114
+ "build_number": "1.0.0"
115
+ }
116
+ ```
117
+
118
+ ## 5. Error Handling
119
+
120
+ - Missing or invalid `x-api-key` header: The `authenticate` decorator will return a 401 Unauthorized error.
121
+ - Jobs directory not found: The endpoint will return a 404 Not Found error if the jobs directory is not found.
122
+ - Exception during job status retrieval: The endpoint will return a 500 Internal Server Error if an exception occurs while retrieving the job statuses.
123
+
124
+ The main `app.py` file includes error handling for queue overflow (429 Too Many Requests) and logging of job statuses (queued, running, done) using the `log_job_status` function.
125
+
126
+ ## 6. Usage Notes
127
+
128
+ - This endpoint is useful for monitoring the status of jobs submitted to the system, especially when dealing with long-running or queued jobs.
129
+ - The `since_seconds` parameter can be adjusted to retrieve job statuses within a specific time range, allowing for more targeted monitoring.
130
+
131
+ ## 7. Common Issues
132
+
133
+ - Providing an invalid `x-api-key` header will result in an authentication error.
134
+ - If the jobs directory is not found or an exception occurs during job status retrieval, the endpoint will return an error.
135
+
136
+ ## 8. Best Practices
137
+
138
+ - Always include the `x-api-key` header with a valid API key for authentication.
139
+ - Monitor the job statuses regularly to keep track of the progress and completion of submitted jobs.
140
+ - Adjust the `since_seconds` parameter based on your monitoring requirements to retrieve job statuses within a specific time range.
141
+ - Implement error handling and logging mechanisms to track and troubleshoot any issues that may arise.
docs/toolkit/test.md ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # NCA Toolkit Test API Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/toolkit/test` endpoint is a part of the NCA Toolkit API and is designed to test the API setup. It creates a temporary file, uploads it to cloud storage, and then returns the upload URL. This endpoint serves as a simple test to ensure that the API is properly configured and can perform basic file operations and cloud storage interactions.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/toolkit/test`
10
+ **HTTP Method:** `GET`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ This endpoint does not require any request body parameters.
21
+
22
+ ### Example Request
23
+
24
+ ```bash
25
+ curl -X GET \
26
+ https://your-api-url.com/v1/toolkit/test \
27
+ -H 'x-api-key: your-api-key'
28
+ ```
29
+
30
+ ## 4. Response
31
+
32
+ ### Success Response
33
+
34
+ ```json
35
+ {
36
+ "endpoint": "/v1/toolkit/test",
37
+ "code": 200,
38
+ "id": null,
39
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
40
+ "response": "https://cloud-storage.com/success.txt",
41
+ "message": "success",
42
+ "pid": 12345,
43
+ "queue_id": 67890,
44
+ "run_time": 0.123,
45
+ "queue_time": 0.0,
46
+ "total_time": 0.123,
47
+ "queue_length": 0,
48
+ "build_number": "1.0.0"
49
+ }
50
+ ```
51
+
52
+ ### Error Responses
53
+
54
+ **Status Code: 401 Unauthorized**
55
+
56
+ ```json
57
+ {
58
+ "code": 401,
59
+ "message": "Unauthorized: Invalid or missing API key"
60
+ }
61
+ ```
62
+
63
+ **Status Code: 500 Internal Server Error**
64
+
65
+ ```json
66
+ {
67
+ "code": 500,
68
+ "message": "An error occurred while processing the request"
69
+ }
70
+ ```
71
+
72
+ ## 5. Error Handling
73
+
74
+ - **Missing or Invalid API Key (401 Unauthorized)**: If the `x-api-key` header is missing or invalid, the API will return a 401 Unauthorized error.
75
+ - **Internal Server Error (500)**: If an unexpected error occurs during the file creation, upload, or any other operation, the API will return a 500 Internal Server Error with the error message.
76
+
77
+ ## 6. Usage Notes
78
+
79
+ This endpoint is primarily used for testing purposes and does not require any specific input parameters. It can be called to verify that the API is set up correctly and can perform basic operations.
80
+
81
+ ## 7. Common Issues
82
+
83
+ - Incorrect or missing API key: Ensure that the `x-api-key` header is included with a valid API key.
84
+ - Temporary file creation or upload issues: If there are any issues with creating or uploading the temporary file, the API will return an error.
85
+
86
+ ## 8. Best Practices
87
+
88
+ - Use this endpoint during the initial setup and testing phase of the API integration to ensure that the API is configured correctly.
89
+ - Regularly test the API setup using this endpoint to catch any potential issues or configuration changes that may affect the API's functionality.
docs/video/caption_video.md ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Video Captioning Endpoint (v1)
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/video/caption` endpoint is part of the Video API and is responsible for adding captions to a video file. It accepts a video URL, caption text, and various styling options for the captions. The endpoint utilizes the `process_captioning_v1` service to generate a captioned video file, which is then uploaded to cloud storage, and the cloud URL is returned in the response.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL:** `/v1/video/caption`
10
+ **Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key`: Required. The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+
22
+ - `video_url` (string, required): The URL of the video file to be captioned.
23
+ - `captions` (string, optional): Can be one of the following:
24
+ - Raw caption text to be added to the video
25
+ - URL to an SRT subtitle file
26
+ - URL to an ASS subtitle file
27
+ - If not provided, the system will automatically generate captions by transcribing the audio from the video
28
+ - `settings` (object, optional): An object containing various styling options for the captions. See the schema below for available options.
29
+ - `replace` (array, optional): An array of objects with `find` and `replace` properties, specifying text replacements to be made in the captions.
30
+ - `webhook_url` (string, optional): A URL to receive a webhook notification when the captioning process is complete.
31
+ - `id` (string, optional): An identifier for the request.
32
+ - `language` (string, optional): The language code for the captions (e.g., "en", "fr"). Defaults to "auto".
33
+ - `exclude_time_ranges` (array, optional): List of time ranges to skip when adding captions. Each item must be an object with:
34
+ - `start`: (string, required) The start time of the excluded range, as a string timecode in `hh:mm:ss.ms` format (e.g., `00:01:23.456`).
35
+ - `end`: (string, required) The end time, as a string timecode in `hh:mm:ss.ms` format, which must be strictly greater than `start`.
36
+ If either value is not a valid timecode string, or if `end` is not greater than `start`, the request will return an error.
37
+
38
+ #### Settings Schema
39
+
40
+ ```json
41
+ {
42
+ "type": "object",
43
+ "properties": {
44
+ "line_color": {"type": "string"},
45
+ "word_color": {"type": "string"},
46
+ "outline_color": {"type": "string"},
47
+ "all_caps": {"type": "boolean"},
48
+ "max_words_per_line": {"type": "integer"},
49
+ "x": {"type": "integer"},
50
+ "y": {"type": "integer"},
51
+ "position": {
52
+ "type": "string",
53
+ "enum": [
54
+ "bottom_left", "bottom_center", "bottom_right",
55
+ "middle_left", "middle_center", "middle_right",
56
+ "top_left", "top_center", "top_right"
57
+ ]
58
+ },
59
+ "alignment": {
60
+ "type": "string",
61
+ "enum": ["left", "center", "right"]
62
+ },
63
+ "font_family": {"type": "string"},
64
+ "font_size": {"type": "integer"},
65
+ "bold": {"type": "boolean"},
66
+ "italic": {"type": "boolean"},
67
+ "underline": {"type": "boolean"},
68
+ "strikeout": {"type": "boolean"},
69
+ "style": {
70
+ "type": "string",
71
+ "enum": [
72
+ "classic", // Regular captioning with all text displayed at once
73
+ "karaoke", // Highlights words sequentially in a karaoke style
74
+ "highlight", // Shows full text but highlights the current word
75
+ "underline", // Shows full text but underlines the current word
76
+ "word_by_word" // Shows one word at a time
77
+ ]
78
+ },
79
+ "outline_width": {"type": "integer"},
80
+ "spacing": {"type": "integer"},
81
+ "angle": {"type": "integer"},
82
+ "shadow_offset": {"type": "integer"}
83
+ },
84
+ "additionalProperties": false
85
+ }
86
+ ```
87
+
88
+ ### Example Requests
89
+
90
+ #### Example 1: Basic Automatic Captioning
91
+ ```json
92
+ {
93
+ "video_url": "https://example.com/video.mp4"
94
+ }
95
+ ```
96
+ This minimal request will automatically transcribe the video and add white captions at the bottom center.
97
+
98
+ #### Example 2: Custom Text with Styling
99
+ ```json
100
+ {
101
+ "video_url": "https://example.com/video.mp4",
102
+ "captions": "This is a sample caption text.",
103
+ "settings": {
104
+ "style": "classic",
105
+ "line_color": "#FFFFFF",
106
+ "outline_color": "#000000",
107
+ "position": "bottom_center",
108
+ "alignment": "center",
109
+ "font_family": "Arial",
110
+ "font_size": 24,
111
+ "bold": true
112
+ }
113
+ }
114
+ ```
115
+
116
+ #### Example 3: Karaoke-Style Captions with Advanced Options
117
+ ```json
118
+ {
119
+ "video_url": "https://example.com/video.mp4",
120
+ "settings": {
121
+ "line_color": "#FFFFFF",
122
+ "word_color": "#FFFF00",
123
+ "outline_color": "#000000",
124
+ "all_caps": false,
125
+ "max_words_per_line": 10,
126
+ "position": "bottom_center",
127
+ "alignment": "center",
128
+ "font_family": "Arial",
129
+ "font_size": 24,
130
+ "bold": false,
131
+ "italic": false,
132
+ "style": "karaoke",
133
+ "outline_width": 2,
134
+ "shadow_offset": 2
135
+ },
136
+ "replace": [
137
+ {
138
+ "find": "um",
139
+ "replace": ""
140
+ },
141
+ {
142
+ "find": "like",
143
+ "replace": ""
144
+ }
145
+ ],
146
+ "webhook_url": "https://example.com/webhook",
147
+ "id": "request-123",
148
+ "language": "en"
149
+ }
150
+ ```
151
+
152
+ #### Example 4: Using an External Subtitle File
153
+ ```json
154
+ {
155
+ "video_url": "https://example.com/video.mp4",
156
+ "captions": "https://example.com/subtitles.srt",
157
+ "settings": {
158
+ "line_color": "#FFFFFF",
159
+ "outline_color": "#000000",
160
+ "position": "bottom_center",
161
+ "font_family": "Arial",
162
+ "font_size": 24
163
+ }
164
+ }
165
+ ```
166
+
167
+ #### Example 5: Excluding Time Ranges from Captioning
168
+ ```json
169
+ {
170
+ "video_url": "https://example.com/video.mp4",
171
+ "settings": {
172
+ "style": "classic",
173
+ "line_color": "#FFFFFF",
174
+ "outline_color": "#000000",
175
+ "position": "bottom_center",
176
+ "font_family": "Arial",
177
+ "font_size": 24
178
+ },
179
+ "exclude_time_ranges": [
180
+ { "start": "00:00:10.000", "end": "00:00:20.000" },
181
+ { "start": "00:00:30.000", "end": "00:00:40.000" }
182
+ ]
183
+ }
184
+ ```
185
+
186
+ ```bash
187
+ curl -X POST \
188
+ -H "x-api-key: YOUR_API_KEY" \
189
+ -H "Content-Type: application/json" \
190
+ -d '{
191
+ "video_url": "https://example.com/video.mp4",
192
+ "settings": {
193
+ "line_color": "#FFFFFF",
194
+ "word_color": "#FFFF00",
195
+ "outline_color": "#000000",
196
+ "all_caps": false,
197
+ "max_words_per_line": 10,
198
+ "position": "bottom_center",
199
+ "alignment": "center",
200
+ "font_family": "Arial",
201
+ "font_size": 24,
202
+ "style": "karaoke",
203
+ "outline_width": 2
204
+ },
205
+ "replace": [
206
+ {
207
+ "find": "um",
208
+ "replace": ""
209
+ }
210
+ ],
211
+ "id": "custom-request-id"
212
+ }' \
213
+ https://your-api-endpoint.com/v1/video/caption
214
+ ```
215
+
216
+ ## 4. Response
217
+
218
+ ### Success Response
219
+
220
+ The response will be a JSON object with the following properties:
221
+
222
+ - `code` (integer): The HTTP status code (200 for success).
223
+ - `id` (string): The request identifier, if provided in the request.
224
+ - `job_id` (string): A unique identifier for the job.
225
+ - `response` (string): The cloud URL of the captioned video file.
226
+ - `message` (string): A success message.
227
+ - `pid` (integer): The process ID of the worker that processed the request.
228
+ - `queue_id` (integer): The ID of the queue used for processing the request.
229
+ - `run_time` (float): The time taken to process the request (in seconds).
230
+ - `queue_time` (float): The time the request spent in the queue (in seconds).
231
+ - `total_time` (float): The total time taken for the request (in seconds).
232
+ - `queue_length` (integer): The current length of the processing queue.
233
+ - `build_number` (string): The build number of the application.
234
+
235
+ Example:
236
+
237
+ ```json
238
+ {
239
+ "code": 200,
240
+ "id": "request-123",
241
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
242
+ "response": "https://cloud.example.com/captioned-video.mp4",
243
+ "message": "success",
244
+ "pid": 12345,
245
+ "queue_id": 140682639937472,
246
+ "run_time": 5.234,
247
+ "queue_time": 0.012,
248
+ "total_time": 5.246,
249
+ "queue_length": 0,
250
+ "build_number": "1.0.0"
251
+ }
252
+ ```
253
+
254
+ ### Error Responses
255
+
256
+ #### Missing or Invalid Parameters
257
+
258
+ **Status Code:** 400 Bad Request
259
+
260
+ ```json
261
+ {
262
+ "code": 400,
263
+ "id": "request-123",
264
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
265
+ "message": "Missing or invalid parameters",
266
+ "pid": 12345,
267
+ "queue_id": 140682639937472,
268
+ "queue_length": 0,
269
+ "build_number": "1.0.0"
270
+ }
271
+ ```
272
+
273
+ #### Font Error
274
+
275
+ **Status Code:** 400 Bad Request
276
+
277
+ ```json
278
+ {
279
+ "code": 400,
280
+ "error": "The requested font 'InvalidFont' is not available. Please choose from the available fonts.",
281
+ "available_fonts": ["Arial", "Times New Roman", "Courier New", ...],
282
+ "pid": 12345,
283
+ "queue_id": 140682639937472,
284
+ "queue_length": 0,
285
+ "build_number": "1.0.0"
286
+ }
287
+ ```
288
+
289
+ #### Internal Server Error
290
+
291
+ **Status Code:** 500 Internal Server Error
292
+
293
+ ```json
294
+ {
295
+ "code": 500,
296
+ "id": "request-123",
297
+ "job_id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
298
+ "error": "An unexpected error occurred during the captioning process.",
299
+ "pid": 12345,
300
+ "queue_id": 140682639937472,
301
+ "queue_length": 0,
302
+ "build_number": "1.0.0"
303
+ }
304
+ ```
305
+
306
+ ## 5. Error Handling
307
+
308
+ The endpoint handles the following common errors:
309
+
310
+ - **Missing or Invalid Parameters**: If any required parameters are missing or invalid, a 400 Bad Request error is returned with a descriptive error message.
311
+ - **Font Error**: If the requested font is not available, a 400 Bad Request error is returned with a list of available fonts.
312
+ - **Internal Server Error**: If an unexpected error occurs during the captioning process, a 500 Internal Server Error is returned with an error message.
313
+
314
+ Additionally, the main application context (`app.py`) includes error handling for queue overload. If the maximum queue length (`MAX_QUEUE_LENGTH`) is set and the queue size reaches that limit, a 429 Too Many Requests error is returned with a descriptive message.
315
+
316
+ ## 6. Usage Notes
317
+
318
+ - The `video_url` parameter must be a valid URL pointing to a video file (MP4, MOV, etc.).
319
+ - The `captions` parameter is optional and can be used in multiple ways:
320
+ - If not provided, the endpoint will automatically transcribe the audio and generate captions
321
+ - If provided as plain text, the text will be used as captions for the entire video
322
+ - If provided as a URL to an SRT or ASS subtitle file, the system will use that file for captioning
323
+ - For SRT files, only 'classic' style is supported
324
+ - For ASS files, the original styling will be preserved
325
+ - The `settings` parameter allows for customization of the caption appearance and behavior:
326
+ - `style` determines how captions are displayed, with options including:
327
+ - `classic`: Regular captioning with all text displayed at once
328
+ - `karaoke`: Highlights words sequentially in a karaoke style as they're spoken
329
+ - `highlight`: Shows the full caption text but highlights each word as it's spoken
330
+ - `underline`: Shows the full caption text but underlines each word as it's spoken
331
+ - `word_by_word`: Shows only one word at a time
332
+ - `position` can be used to place captions in one of nine positions on the screen
333
+ - `alignment` determines text alignment within the position (left, center, right)
334
+ - `font_family` can be any available system font
335
+ - Color options can be set using hex codes (e.g., "#FFFFFF" for white)
336
+ - The `replace` parameter can be used to perform text replacements in the captions (useful for correcting words or censoring content).
337
+ - The `webhook_url` parameter is optional and can be used to receive a notification when the captioning process is complete.
338
+ - The `id` parameter is optional and can be used to identify the request in webhook responses.
339
+ - The `language` parameter is optional and can be used to specify the language of the captions for transcription. If not provided, the language will be automatically detected.
340
+ - The `exclude_time_ranges` parameter can be used to specify time ranges to be excluded from captioning.
341
+
342
+ ## 7. Common Issues
343
+
344
+ - Providing an invalid or inaccessible `video_url`.
345
+ - Requesting an unavailable font in the `settings` object.
346
+ - Exceeding the maximum queue length, resulting in a 429 Too Many Requests error.
347
+
348
+ ## 8. Best Practices
349
+
350
+ - Validate the `video_url` parameter before sending the request to ensure it points to a valid and accessible video file.
351
+ - Use the `webhook_url` parameter to receive notifications about the captioning process, rather than polling the API for updates.
352
+ - Provide descriptive and meaningful `id` values to easily identify requests in logs and responses.
353
+ - Use the `replace` parameter judiciously to avoid unintended text replacements in the captions.
354
+ - Consider caching the captioned video files for frequently requested videos to improve performance and reduce processing time.
docs/video/concatenate.md ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Video Concatenation Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/video/concatenate` endpoint is a part of the Video API and is responsible for combining multiple video files into a single video file. This endpoint fits into the overall API structure as a part of the version 1 (v1) routes, specifically under the `/v1/video` namespace.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/video/concatenate`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+
22
+ - `video_urls` (required, array of objects): An array of video URLs to be concatenated. Each object in the array must have a `video_url` property (string, URI format) containing the URL of the video file.
23
+ - `webhook_url` (optional, string, URI format): The URL to which the response should be sent as a webhook.
24
+ - `id` (optional, string): An identifier for the request.
25
+
26
+ The `validate_payload` decorator in the routes file enforces the following JSON schema for the request body:
27
+
28
+ ```json
29
+ {
30
+ "type": "object",
31
+ "properties": {
32
+ "video_urls": {
33
+ "type": "array",
34
+ "items": {
35
+ "type": "object",
36
+ "properties": {
37
+ "video_url": {"type": "string", "format": "uri"}
38
+ },
39
+ "required": ["video_url"]
40
+ },
41
+ "minItems": 1
42
+ },
43
+ "webhook_url": {"type": "string", "format": "uri"},
44
+ "id": {"type": "string"}
45
+ },
46
+ "required": ["video_urls"],
47
+ "additionalProperties": False
48
+ }
49
+ ```
50
+
51
+ ### Example Request
52
+
53
+ ```json
54
+ {
55
+ "video_urls": [
56
+ {"video_url": "https://example.com/video1.mp4"},
57
+ {"video_url": "https://example.com/video2.mp4"},
58
+ {"video_url": "https://example.com/video3.mp4"}
59
+ ],
60
+ "webhook_url": "https://example.com/webhook",
61
+ "id": "request-123"
62
+ }
63
+ ```
64
+
65
+ ```bash
66
+ curl -X POST \
67
+ -H "x-api-key: YOUR_API_KEY" \
68
+ -H "Content-Type: application/json" \
69
+ -d '{
70
+ "video_urls": [
71
+ {"video_url": "https://example.com/video1.mp4"},
72
+ {"video_url": "https://example.com/video2.mp4"},
73
+ {"video_url": "https://example.com/video3.mp4"}
74
+ ],
75
+ "webhook_url": "https://example.com/webhook",
76
+ "id": "request-123"
77
+ }' \
78
+ https://your-api-endpoint.com/v1/video/concatenate
79
+ ```
80
+
81
+ ## 4. Response
82
+
83
+ ### Success Response
84
+
85
+ The success response follows the general response format defined in the `app.py` file. Here's an example:
86
+
87
+ ```json
88
+ {
89
+ "endpoint": "/v1/video/concatenate",
90
+ "code": 200,
91
+ "id": "request-123",
92
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
93
+ "response": "https://cloud-storage.example.com/combined-video.mp4",
94
+ "message": "success",
95
+ "pid": 12345,
96
+ "queue_id": 6789,
97
+ "run_time": 10.234,
98
+ "queue_time": 2.345,
99
+ "total_time": 12.579,
100
+ "queue_length": 0,
101
+ "build_number": "1.0.0"
102
+ }
103
+ ```
104
+
105
+ The `response` field contains the URL of the combined video file uploaded to cloud storage.
106
+
107
+ ### Error Responses
108
+
109
+ - **400 Bad Request**: Returned when the request body is missing or invalid.
110
+
111
+ ```json
112
+ {
113
+ "code": 400,
114
+ "message": "Invalid request payload"
115
+ }
116
+ ```
117
+
118
+ - **401 Unauthorized**: Returned when the `x-api-key` header is missing or invalid.
119
+
120
+ ```json
121
+ {
122
+ "code": 401,
123
+ "message": "Unauthorized"
124
+ }
125
+ ```
126
+
127
+ - **429 Too Many Requests**: Returned when the maximum queue length is reached.
128
+
129
+ ```json
130
+ {
131
+ "code": 429,
132
+ "id": "request-123",
133
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
134
+ "message": "MAX_QUEUE_LENGTH (100) reached",
135
+ "pid": 12345,
136
+ "queue_id": 6789,
137
+ "queue_length": 100,
138
+ "build_number": "1.0.0"
139
+ }
140
+ ```
141
+
142
+ - **500 Internal Server Error**: Returned when an unexpected error occurs during the video concatenation process.
143
+
144
+ ```json
145
+ {
146
+ "code": 500,
147
+ "message": "An error occurred during video concatenation"
148
+ }
149
+ ```
150
+
151
+ ## 5. Error Handling
152
+
153
+ The endpoint handles the following common errors:
154
+
155
+ - **Missing or invalid request body**: If the request body is missing or does not conform to the expected JSON schema, a 400 Bad Request error is returned.
156
+ - **Missing or invalid API key**: If the `x-api-key` header is missing or invalid, a 401 Unauthorized error is returned.
157
+ - **Queue length exceeded**: If the maximum queue length is reached (determined by the `MAX_QUEUE_LENGTH` environment variable), a 429 Too Many Requests error is returned.
158
+ - **Unexpected errors during video concatenation**: If an unexpected error occurs during the video concatenation process, a 500 Internal Server Error is returned with the error message.
159
+
160
+ The main application context (`app.py`) also includes error handling for the task queue. If the queue length exceeds the `MAX_QUEUE_LENGTH` limit, the request is rejected with a 429 Too Many Requests error.
161
+
162
+ ## 6. Usage Notes
163
+
164
+ - The video files to be concatenated must be accessible via the provided URLs.
165
+ - The order of the video files in the `video_urls` array determines the order in which they will be concatenated.
166
+ - If the `webhook_url` parameter is provided, the response will be sent as a webhook to the specified URL.
167
+ - The `id` parameter can be used to identify the request in the response.
168
+
169
+ ## 7. Common Issues
170
+
171
+ - Providing invalid or inaccessible video URLs.
172
+ - Exceeding the maximum queue length, which can lead to requests being rejected with a 429 Too Many Requests error.
173
+ - Encountering unexpected errors during the video concatenation process, which can result in a 500 Internal Server Error.
174
+
175
+ ## 8. Best Practices
176
+
177
+ - Validate the video URLs before sending the request to ensure they are accessible and in the correct format.
178
+ - Monitor the queue length and adjust the `MAX_QUEUE_LENGTH` value accordingly to prevent requests from being rejected due to a full queue.
179
+ - Implement retry mechanisms for handling temporary errors or failures during the video concatenation process.
180
+ - Provide meaningful and descriptive `id` values to easily identify requests in the response.
docs/video/cut.md ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Video Cut Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/video/cut` endpoint is part of the Video API and allows users to cut specified segments from a video file with optional encoding settings. This endpoint fits into the overall API structure as a part of the version 1 (`v1`) routes, specifically under the `video` category.
6
+
7
+ ## 2. Endpoint
8
+
9
+ ```
10
+ POST /v1/video/cut
11
+ ```
12
+
13
+ ## 3. Request
14
+
15
+ ### Headers
16
+
17
+ - `x-api-key` (required): The API key for authentication.
18
+
19
+ ### Body Parameters
20
+
21
+ The request body must be a JSON object with the following properties:
22
+
23
+ - `video_url` (required, string): The URL of the video file to be cut.
24
+ - `cuts` (required, array of objects): An array of cut segments, where each object has the following properties:
25
+ - `start` (required, string): The start time of the cut segment in the format `hh:mm:ss.ms`.
26
+ - `end` (required, string): The end time of the cut segment in the format `hh:mm:ss.ms`.
27
+ - `video_codec` (optional, string): The video codec to use for encoding the output video. Default is `libx264`.
28
+ - `video_preset` (optional, string): The video preset to use for encoding the output video. Default is `medium`.
29
+ - `video_crf` (optional, number): The Constant Rate Factor (CRF) value for video encoding. Must be between 0 and 51. Default is 23.
30
+ - `audio_codec` (optional, string): The audio codec to use for encoding the output video. Default is `aac`.
31
+ - `audio_bitrate` (optional, string): The audio bitrate to use for encoding the output video. Default is `128k`.
32
+ - `webhook_url` (optional, string): The URL to receive a webhook notification when the job is completed.
33
+ - `id` (optional, string): A unique identifier for the request.
34
+
35
+ ### Example Request
36
+
37
+ ```json
38
+ {
39
+ "video_url": "https://example.com/video.mp4",
40
+ "cuts": [
41
+ {
42
+ "start": "00:00:10.000",
43
+ "end": "00:00:20.000"
44
+ },
45
+ {
46
+ "start": "00:00:30.000",
47
+ "end": "00:00:40.000"
48
+ }
49
+ ],
50
+ "video_codec": "libx264",
51
+ "video_preset": "medium",
52
+ "video_crf": 23,
53
+ "audio_codec": "aac",
54
+ "audio_bitrate": "128k",
55
+ "webhook_url": "https://example.com/webhook",
56
+ "id": "unique-request-id"
57
+ }
58
+ ```
59
+
60
+ ```bash
61
+ curl -X POST \
62
+ https://api.example.com/v1/video/cut \
63
+ -H 'x-api-key: YOUR_API_KEY' \
64
+ -H 'Content-Type: application/json' \
65
+ -d '{
66
+ "video_url": "https://example.com/video.mp4",
67
+ "cuts": [
68
+ {
69
+ "start": "00:00:10.000",
70
+ "end": "00:00:20.000"
71
+ },
72
+ {
73
+ "start": "00:00:30.000",
74
+ "end": "00:00:40.000"
75
+ }
76
+ ],
77
+ "video_codec": "libx264",
78
+ "video_preset": "medium",
79
+ "video_crf": 23,
80
+ "audio_codec": "aac",
81
+ "audio_bitrate": "128k",
82
+ "webhook_url": "https://example.com/webhook",
83
+ "id": "unique-request-id"
84
+ }'
85
+ ```
86
+
87
+ ## 4. Response
88
+
89
+ ### Success Response
90
+
91
+ The response follows the general response format defined in the main application context (`app.py`). Here's an example of a successful response:
92
+
93
+ ```json
94
+ {
95
+ "endpoint": "/v1/video/cut",
96
+ "code": 200,
97
+ "id": "unique-request-id",
98
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
99
+ "response": "https://example.com/processed-video.mp4",
100
+ "message": "success",
101
+ "pid": 12345,
102
+ "queue_id": 6789,
103
+ "run_time": 5.234,
104
+ "queue_time": 0.123,
105
+ "total_time": 5.357,
106
+ "queue_length": 0,
107
+ "build_number": "1.0.0"
108
+ }
109
+ ```
110
+
111
+ The `response` field contains the URL of the processed video file.
112
+
113
+ ### Error Responses
114
+
115
+ - **400 Bad Request**
116
+
117
+ ```json
118
+ {
119
+ "code": 400,
120
+ "message": "Invalid request payload"
121
+ }
122
+ ```
123
+
124
+ This error occurs when the request payload is missing required fields or contains invalid data.
125
+
126
+ - **401 Unauthorized**
127
+
128
+ ```json
129
+ {
130
+ "code": 401,
131
+ "message": "Invalid API key"
132
+ }
133
+ ```
134
+
135
+ This error occurs when the provided `x-api-key` header is missing or invalid.
136
+
137
+ - **429 Too Many Requests**
138
+
139
+ ```json
140
+ {
141
+ "code": 429,
142
+ "id": "unique-request-id",
143
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
144
+ "message": "MAX_QUEUE_LENGTH (100) reached",
145
+ "pid": 12345,
146
+ "queue_id": 6789,
147
+ "queue_length": 100,
148
+ "build_number": "1.0.0"
149
+ }
150
+ ```
151
+
152
+ This error occurs when the maximum queue length has been reached, and the request cannot be processed immediately.
153
+
154
+ - **500 Internal Server Error**
155
+
156
+ ```json
157
+ {
158
+ "code": 500,
159
+ "message": "An error occurred during video processing"
160
+ }
161
+ ```
162
+
163
+ This error occurs when an unexpected error occurs during the video processing or encoding.
164
+
165
+ ## 5. Error Handling
166
+
167
+ The endpoint handles the following common errors:
168
+
169
+ - **Missing or invalid request parameters**: If any required parameters are missing or invalid, the endpoint returns a 400 Bad Request error with an appropriate error message.
170
+ - **Invalid API key**: If the provided `x-api-key` header is missing or invalid, the endpoint returns a 401 Unauthorized error.
171
+ - **Queue limit reached**: If the maximum queue length has been reached, the endpoint returns a 429 Too Many Requests error with the current queue length and the maximum queue length.
172
+ - **Unexpected errors during video processing**: If an unexpected error occurs during the video processing or encoding, the endpoint returns a 500 Internal Server Error with a generic error message.
173
+
174
+ The main application context (`app.py`) also includes error handling for the queue system and webhook notifications.
175
+
176
+ ## 6. Usage Notes
177
+
178
+ - The `video_url` parameter must be a valid URL that points to a video file accessible by the server.
179
+ - The `cuts` parameter must be an array of objects, where each object represents a cut segment with a start and end time in the format `hh:mm:ss.ms`.
180
+ - The optional encoding parameters (`video_codec`, `video_preset`, `video_crf`, `audio_codec`, `audio_bitrate`) allow you to customize the encoding settings for the output video file.
181
+ - If the `webhook_url` parameter is provided, the server will send a webhook notification to the specified URL when the job is completed.
182
+ - The `id` parameter can be used to associate the request with a unique identifier for tracking purposes.
183
+
184
+ ## 7. Common Issues
185
+
186
+ - Providing an invalid or inaccessible `video_url`.
187
+ - Specifying overlapping or invalid cut segments in the `cuts` parameter.
188
+ - Providing invalid encoding settings that are not supported by the server.
189
+ - Reaching the maximum queue length, which can cause requests to be rejected with a 429 Too Many Requests error.
190
+
191
+ ## 8. Best Practices
192
+
193
+ - Validate the `video_url` parameter before sending the request to ensure it points to a valid and accessible video file.
194
+ - Ensure that the cut segments in the `cuts` parameter are correctly formatted and do not overlap or exceed the duration of the video.
195
+ - Use the optional encoding parameters judiciously, as they can impact the processing time and output video quality.
196
+ - Implement retry mechanisms for handling 429 Too Many Requests errors, as the queue length may fluctuate over time.
197
+ - Monitor the webhook notifications or poll the server for job status updates to track the progress of long-running jobs.
docs/video/split.md ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Video Split Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/video/split` endpoint is part of the Video API and is used to split a video file into multiple segments based on specified start and end times. This endpoint fits into the overall API structure as a part of the version 1 (`v1`) routes, specifically under the `video` category.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/video/split`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ The request body must be a JSON object with the following properties:
21
+
22
+ - `video_url` (required, string): The URL of the video file to be split.
23
+ - `splits` (required, array of objects): An array of objects specifying the start and end times for each split. Each object must have the following properties:
24
+ - `start` (required, string): The start time of the split in the format `hh:mm:ss.ms`.
25
+ - `end` (required, string): The end time of the split in the format `hh:mm:ss.ms`.
26
+ - `video_codec` (optional, string): The video codec to use for encoding the split videos. Default is `libx264`.
27
+ - `video_preset` (optional, string): The video preset to use for encoding the split videos. Default is `medium`.
28
+ - `video_crf` (optional, number): The Constant Rate Factor (CRF) value for video encoding. Must be between 0 and 51. Default is 23.
29
+ - `audio_codec` (optional, string): The audio codec to use for encoding the split videos. Default is `aac`.
30
+ - `audio_bitrate` (optional, string): The audio bitrate to use for encoding the split videos. Default is `128k`.
31
+ - `webhook_url` (optional, string): The URL to receive a webhook notification when the split operation is complete.
32
+ - `id` (optional, string): A unique identifier for the request.
33
+
34
+ ### Example Request
35
+
36
+ ```json
37
+ {
38
+ "video_url": "https://example.com/video.mp4",
39
+ "splits": [
40
+ {
41
+ "start": "00:00:10.000",
42
+ "end": "00:00:20.000"
43
+ },
44
+ {
45
+ "start": "00:00:30.000",
46
+ "end": "00:00:40.000"
47
+ }
48
+ ],
49
+ "video_codec": "libx264",
50
+ "video_preset": "medium",
51
+ "video_crf": 23,
52
+ "audio_codec": "aac",
53
+ "audio_bitrate": "128k",
54
+ "webhook_url": "https://example.com/webhook",
55
+ "id": "unique-request-id"
56
+ }
57
+ ```
58
+
59
+ ```bash
60
+ curl -X POST \
61
+ https://api.example.com/v1/video/split \
62
+ -H 'x-api-key: YOUR_API_KEY' \
63
+ -H 'Content-Type: application/json' \
64
+ -d '{
65
+ "video_url": "https://example.com/video.mp4",
66
+ "splits": [
67
+ {
68
+ "start": "00:00:10.000",
69
+ "end": "00:00:20.000"
70
+ },
71
+ {
72
+ "start": "00:00:30.000",
73
+ "end": "00:00:40.000"
74
+ }
75
+ ],
76
+ "video_codec": "libx264",
77
+ "video_preset": "medium",
78
+ "video_crf": 23,
79
+ "audio_codec": "aac",
80
+ "audio_bitrate": "128k",
81
+ "webhook_url": "https://example.com/webhook",
82
+ "id": "unique-request-id"
83
+ }'
84
+ ```
85
+
86
+ ## 4. Response
87
+
88
+ ### Success Response
89
+
90
+ The success response follows the general response format specified in `app.py`. Here's an example:
91
+
92
+ ```json
93
+ {
94
+ "endpoint": "/v1/video/split",
95
+ "code": 200,
96
+ "id": "unique-request-id",
97
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
98
+ "response": [
99
+ {
100
+ "file_url": "https://example.com/split-1.mp4"
101
+ },
102
+ {
103
+ "file_url": "https://example.com/split-2.mp4"
104
+ }
105
+ ],
106
+ "message": "success",
107
+ "pid": 12345,
108
+ "queue_id": 6789,
109
+ "run_time": 5.234,
110
+ "queue_time": 0.123,
111
+ "total_time": 5.357,
112
+ "queue_length": 0,
113
+ "build_number": "1.0.0"
114
+ }
115
+ ```
116
+
117
+ The `response` field contains an array of objects, each representing a split video file. Each object has a `file_url` property containing the URL of the split video file.
118
+
119
+ ### Error Responses
120
+
121
+ - **400 Bad Request**: Returned when the request payload is missing or invalid.
122
+ - **401 Unauthorized**: Returned when the `x-api-key` header is missing or invalid.
123
+ - **429 Too Many Requests**: Returned when the maximum queue length has been reached.
124
+ - **500 Internal Server Error**: Returned when an unexpected error occurs during the video split process.
125
+
126
+ Example error response:
127
+
128
+ ```json
129
+ {
130
+ "code": 400,
131
+ "id": "unique-request-id",
132
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
133
+ "message": "Invalid request payload: 'splits' is a required property",
134
+ "pid": 12345,
135
+ "queue_id": 6789,
136
+ "queue_length": 2,
137
+ "build_number": "1.0.0"
138
+ }
139
+ ```
140
+
141
+ ## 5. Error Handling
142
+
143
+ The endpoint handles the following common errors:
144
+
145
+ - Missing or invalid request parameters: Returns a 400 Bad Request error with a descriptive error message.
146
+ - Authentication failure: Returns a 401 Unauthorized error if the `x-api-key` header is missing or invalid.
147
+ - Queue length exceeded: Returns a 429 Too Many Requests error if the maximum queue length has been reached.
148
+ - Unexpected exceptions: Returns a 500 Internal Server Error with the exception message.
149
+
150
+ The main application context (`app.py`) also includes error handling for queue length limits and webhook notifications.
151
+
152
+ ## 6. Usage Notes
153
+
154
+ - The `video_url` parameter must be a valid URL pointing to a video file.
155
+ - The `splits` array must contain at least one object specifying the start and end times for a split.
156
+ - The start and end times must be in the format `hh:mm:ss.ms` (hours:minutes:seconds.milliseconds).
157
+ - The `video_codec`, `video_preset`, `video_crf`, `audio_codec`, and `audio_bitrate` parameters are optional and can be used to customize the encoding settings for the split videos.
158
+ - If the `webhook_url` parameter is provided, a webhook notification will be sent to the specified URL when the split operation is complete.
159
+ - The `id` parameter is optional and can be used to uniquely identify the request.
160
+
161
+ ## 7. Common Issues
162
+
163
+ - Providing an invalid or inaccessible `video_url`.
164
+ - Specifying overlapping or invalid start and end times in the `splits` array.
165
+ - Exceeding the maximum queue length, which can result in a 429 Too Many Requests error.
166
+
167
+ ## 8. Best Practices
168
+
169
+ - Validate the `video_url` parameter before sending the request to ensure it points to a valid video file.
170
+ - Ensure that the start and end times in the `splits` array are correctly formatted and do not overlap.
171
+ - Consider using the `webhook_url` parameter to receive notifications about the completion of the split operation, especially for long-running or asynchronous requests.
172
+ - Implement retry mechanisms and error handling in your client application to handle potential errors and failures.
173
+ - Monitor the queue length and adjust the `MAX_QUEUE_LENGTH` environment variable as needed to prevent excessive queuing and potential timeouts.
docs/video/thumbnail.md ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Video Thumbnail Generation API
2
+
3
+ ## Overview
4
+
5
+ The `/v1/video/thumbnail` endpoint allows users to extract a thumbnail image from a specific timestamp in a video. This endpoint is part of the video processing capabilities of the API, which includes other features like video concatenation and captioning. The endpoint processes the request asynchronously using a queue system, uploads the generated thumbnail to cloud storage, and returns the URL of the uploaded image.
6
+
7
+ ## Endpoint
8
+
9
+ - **URL**: `/v1/video/thumbnail`
10
+ - **Method**: `POST`
11
+
12
+ ## Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key`: Required. Your API authentication key.
17
+
18
+ ### Body Parameters
19
+
20
+ | Parameter | Type | Required | Description |
21
+ |-----------|------|----------|-------------|
22
+ | `video_url` | string (URI format) | Yes | URL of the video from which to extract the thumbnail |
23
+ | `second` | number (minimum: 0) | No | Timestamp in seconds at which to extract the thumbnail (defaults to 0) |
24
+ | `webhook_url` | string (URI format) | No | URL to receive the processing result asynchronously |
25
+ | `id` | string | No | Custom identifier for tracking the request |
26
+
27
+ ### Example Request
28
+
29
+ ```json
30
+ {
31
+ "video_url": "https://example.com/video.mp4",
32
+ "second": 30,
33
+ "webhook_url": "https://your-service.com/webhook",
34
+ "id": "custom-request-123"
35
+ }
36
+ ```
37
+
38
+ ### Example cURL Command
39
+
40
+ ```bash
41
+ curl -X POST \
42
+ https://api.example.com/v1/video/thumbnail \
43
+ -H 'Content-Type: application/json' \
44
+ -H 'x-api-key: your-api-key' \
45
+ -d '{
46
+ "video_url": "https://example.com/video.mp4",
47
+ "second": 30,
48
+ "webhook_url": "https://your-service.com/webhook",
49
+ "id": "custom-request-123"
50
+ }'
51
+ ```
52
+
53
+ ## Response
54
+
55
+ ### Immediate Response (Status Code: 202)
56
+
57
+ When a webhook URL is provided, the API immediately returns a 202 Accepted response and processes the request asynchronously:
58
+
59
+ ```json
60
+ {
61
+ "code": 202,
62
+ "id": "custom-request-123",
63
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
64
+ "message": "processing",
65
+ "pid": 12345,
66
+ "queue_id": 67890,
67
+ "max_queue_length": "unlimited",
68
+ "queue_length": 1,
69
+ "build_number": "1.0.0"
70
+ }
71
+ ```
72
+
73
+ ### Success Response (Status Code: 200)
74
+
75
+ When no webhook URL is provided or when the webhook is called after processing:
76
+
77
+ ```json
78
+ {
79
+ "code": 200,
80
+ "id": "custom-request-123",
81
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
82
+ "response": "https://storage.example.com/thumbnails/video-thumbnail-123.jpg",
83
+ "message": "success",
84
+ "run_time": 1.234,
85
+ "queue_time": 0.567,
86
+ "total_time": 1.801,
87
+ "pid": 12345,
88
+ "queue_id": 67890,
89
+ "queue_length": 0,
90
+ "build_number": "1.0.0"
91
+ }
92
+ ```
93
+
94
+ ### Error Responses
95
+
96
+ #### Invalid Request (Status Code: 400)
97
+
98
+ ```json
99
+ {
100
+ "code": 400,
101
+ "message": "Invalid request: 'video_url' is a required property",
102
+ "job_id": "550e8400-e29b-41d4-a716-446655440000"
103
+ }
104
+ ```
105
+
106
+ #### Queue Full (Status Code: 429)
107
+
108
+ ```json
109
+ {
110
+ "code": 429,
111
+ "id": "custom-request-123",
112
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
113
+ "message": "MAX_QUEUE_LENGTH (100) reached",
114
+ "pid": 12345,
115
+ "queue_id": 67890,
116
+ "queue_length": 100,
117
+ "build_number": "1.0.0"
118
+ }
119
+ ```
120
+
121
+ #### Server Error (Status Code: 500)
122
+
123
+ ```json
124
+ {
125
+ "code": 500,
126
+ "id": "custom-request-123",
127
+ "job_id": "550e8400-e29b-41d4-a716-446655440000",
128
+ "message": "Failed to download video from provided URL",
129
+ "pid": 12345,
130
+ "queue_id": 67890,
131
+ "queue_length": 0,
132
+ "build_number": "1.0.0"
133
+ }
134
+ ```
135
+
136
+ ## Error Handling
137
+
138
+ The endpoint handles various error scenarios:
139
+
140
+ - **Missing Required Parameters**: Returns a 400 error if `video_url` is missing.
141
+ - **Invalid Parameter Format**: Returns a 400 error if parameters don't match the expected format (e.g., invalid URLs).
142
+ - **Queue Capacity**: Returns a 429 error if the processing queue is full.
143
+ - **Processing Errors**: Returns a 500 error if there are issues during thumbnail extraction or upload.
144
+
145
+ ## Usage Notes
146
+
147
+ 1. **Asynchronous Processing**: For long-running operations, provide a `webhook_url` to receive the result asynchronously.
148
+ 2. **Timestamp Selection**: Choose an appropriate `second` value to capture a meaningful frame from the video.
149
+ 3. **Request Tracking**: Use the `id` parameter to track your requests across your systems.
150
+ 4. **Queue Management**: The API uses a queue system with configurable maximum length (set by the `MAX_QUEUE_LENGTH` environment variable).
151
+
152
+ ## Common Issues
153
+
154
+ 1. **Inaccessible Video URLs**: Ensure the video URL is publicly accessible or has proper authentication.
155
+ 2. **Invalid Timestamp**: If the specified second exceeds the video duration, the API may use the last frame or return an error.
156
+ 3. **Webhook Failures**: If your webhook endpoint is unavailable, you won't receive the processing result.
157
+ 4. **Large Videos**: Processing very large videos may take longer and could time out.
158
+
159
+ ## Best Practices
160
+
161
+ 1. **Use Webhooks for Long Videos**: Always use webhooks when processing large videos to avoid HTTP timeout issues.
162
+ 2. **Optimize Thumbnail Selection**: Choose meaningful timestamps for thumbnails (e.g., after intro sequences).
163
+ 3. **Error Handling**: Implement proper error handling in your application to manage API errors gracefully.
164
+ 4. **Rate Limiting**: Monitor the queue length in responses to avoid overwhelming the service.
165
+ 5. **Idempotent Requests**: Use the `id` parameter to make requests idempotent and avoid duplicate processing.
docs/video/trim.md ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Video Trim Endpoint
2
+
3
+ ## 1. Overview
4
+
5
+ The `/v1/video/trim` endpoint is part of the Video API and allows users to trim a video by removing specified portions from the beginning and/or end. It also provides optional encoding settings to control the output video quality. This endpoint fits into the overall API structure as a part of the version 1 (`v1`) routes, specifically under the `video` category.
6
+
7
+ ## 2. Endpoint
8
+
9
+ **URL Path:** `/v1/video/trim`
10
+ **HTTP Method:** `POST`
11
+
12
+ ## 3. Request
13
+
14
+ ### Headers
15
+
16
+ - `x-api-key` (required): The API key for authentication.
17
+
18
+ ### Body Parameters
19
+
20
+ - `video_url` (required, string): The URL of the video file to be trimmed.
21
+ - `start` (optional, string): The start time for trimming in the format `hh:mm:ss` or `mm:ss`.
22
+ - `end` (optional, string): The end time for trimming in the format `hh:mm:ss` or `mm:ss`.
23
+ - `video_codec` (optional, string): The video codec to be used for encoding the output video. Default is `libx264`.
24
+ - `video_preset` (optional, string): The video preset to be used for encoding the output video. Default is `medium`.
25
+ - `video_crf` (optional, number): The Constant Rate Factor (CRF) value for video encoding, ranging from 0 to 51. Default is 23.
26
+ - `audio_codec` (optional, string): The audio codec to be used for encoding the output video. Default is `aac`.
27
+ - `audio_bitrate` (optional, string): The audio bitrate to be used for encoding the output video. Default is `128k`.
28
+ - `webhook_url` (optional, string): The URL to receive a webhook notification upon completion of the task.
29
+ - `id` (optional, string): A unique identifier for the request.
30
+
31
+ The `validate_payload` directive in the routes file ensures that the request payload adheres to the specified schema, which includes the required and optional parameters, their data types, and any additional constraints.
32
+
33
+ ### Example Request
34
+
35
+ ```json
36
+ {
37
+ "video_url": "https://example.com/video.mp4",
38
+ "start": "00:01:00",
39
+ "end": "00:03:00",
40
+ "video_codec": "libx264",
41
+ "video_preset": "faster",
42
+ "video_crf": 28,
43
+ "audio_codec": "aac",
44
+ "audio_bitrate": "128k",
45
+ "webhook_url": "https://example.com/webhook",
46
+ "id": "unique-request-id"
47
+ }
48
+ ```
49
+
50
+ ```bash
51
+ curl -X POST \
52
+ https://api.example.com/v1/video/trim \
53
+ -H 'x-api-key: YOUR_API_KEY' \
54
+ -H 'Content-Type: application/json' \
55
+ -d '{
56
+ "video_url": "https://example.com/video.mp4",
57
+ "start": "00:01:00",
58
+ "end": "00:03:00",
59
+ "video_codec": "libx264",
60
+ "video_preset": "faster",
61
+ "video_crf": 28,
62
+ "audio_codec": "aac",
63
+ "audio_bitrate": "128k",
64
+ "webhook_url": "https://example.com/webhook",
65
+ "id": "unique-request-id"
66
+ }'
67
+ ```
68
+
69
+ ## 4. Response
70
+
71
+ ### Success Response
72
+
73
+ The success response follows the general response structure defined in the `app.py` file. Here's an example:
74
+
75
+ ```json
76
+ {
77
+ "endpoint": "/v1/video/trim",
78
+ "code": 200,
79
+ "id": "unique-request-id",
80
+ "job_id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6",
81
+ "response": "https://example.com/trimmed-video.mp4",
82
+ "message": "success",
83
+ "pid": 12345,
84
+ "queue_id": 6789,
85
+ "run_time": 5.234,
86
+ "queue_time": 0.123,
87
+ "total_time": 5.357,
88
+ "queue_length": 0,
89
+ "build_number": "1.0.0"
90
+ }
91
+ ```
92
+
93
+ ### Error Responses
94
+
95
+ - **400 Bad Request**: Returned when the request payload is missing or contains invalid parameters.
96
+
97
+ ```json
98
+ {
99
+ "code": 400,
100
+ "message": "Invalid request payload"
101
+ }
102
+ ```
103
+
104
+ - **401 Unauthorized**: Returned when the `x-api-key` header is missing or invalid.
105
+
106
+ ```json
107
+ {
108
+ "code": 401,
109
+ "message": "Unauthorized"
110
+ }
111
+ ```
112
+
113
+ - **500 Internal Server Error**: Returned when an unexpected error occurs during the video trimming process.
114
+
115
+ ```json
116
+ {
117
+ "code": 500,
118
+ "message": "An error occurred during the video trimming process"
119
+ }
120
+ ```
121
+
122
+ ## 5. Error Handling
123
+
124
+ The endpoint handles common errors such as missing or invalid parameters by returning appropriate HTTP status codes and error messages. The `validate_payload` decorator ensures that the request payload adheres to the specified schema, and any violations will result in a 400 Bad Request error.
125
+
126
+ The main application context (`app.py`) includes error handling for the task queue. If the maximum queue length is reached, the endpoint will return a 429 Too Many Requests error with a corresponding message.
127
+
128
+ ## 6. Usage Notes
129
+
130
+ - The `start` and `end` parameters are optional, but at least one of them must be provided to perform the trimming operation.
131
+ - The `video_codec`, `video_preset`, `video_crf`, `audio_codec`, and `audio_bitrate` parameters are optional and allow users to customize the encoding settings for the output video.
132
+ - The `webhook_url` parameter is optional and can be used to receive a notification when the task is completed.
133
+ - The `id` parameter is optional and can be used to uniquely identify the request.
134
+
135
+ ## 7. Common Issues
136
+
137
+ - Providing an invalid or inaccessible `video_url`.
138
+ - Specifying invalid or unsupported values for the encoding parameters (`video_codec`, `video_preset`, `video_crf`, `audio_codec`, `audio_bitrate`).
139
+ - Encountering issues with the video trimming process due to unsupported video formats or corrupted files.
140
+
141
+ ## 8. Best Practices
142
+
143
+ - Validate the `video_url` parameter to ensure it points to a valid and accessible video file.
144
+ - Use appropriate encoding settings based on the desired output quality and file size requirements.
145
+ - Implement error handling and retry mechanisms for failed requests or network issues.
146
+ - Monitor the task queue length and adjust the `MAX_QUEUE_LENGTH` value accordingly to prevent overloading the system.
147
+ - Implement rate limiting or throttling mechanisms to prevent abuse or excessive requests.
routes/audio_mixing.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint
20
+ from app_utils import *
21
+ import logging
22
+ from services.audio_mixing import process_audio_mixing
23
+ from services.authentication import authenticate
24
+ from services.cloud_storage import upload_file
25
+
26
+ audio_mixing_bp = Blueprint('audio_mixing', __name__)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ @audio_mixing_bp.route('/audio-mixing', methods=['POST'])
30
+ @authenticate
31
+ @validate_payload({
32
+ "type": "object",
33
+ "properties": {
34
+ "video_url": {"type": "string", "format": "uri"},
35
+ "audio_url": {"type": "string", "format": "uri"},
36
+ "video_vol": {"type": "number", "minimum": 0, "maximum": 100},
37
+ "audio_vol": {"type": "number", "minimum": 0, "maximum": 100},
38
+ "output_length": {"type": "string", "enum": ["video", "audio"]},
39
+ "webhook_url": {"type": "string", "format": "uri"},
40
+ "id": {"type": "string"}
41
+ },
42
+ "required": ["video_url", "audio_url"],
43
+ "additionalProperties": False
44
+ })
45
+ @queue_task_wrapper(bypass_queue=False)
46
+ def audio_mixing(job_id, data):
47
+ video_url = data.get('video_url')
48
+ audio_url = data.get('audio_url')
49
+ video_vol = data.get('video_vol', 100)
50
+ audio_vol = data.get('audio_vol', 100)
51
+ output_length = data.get('output_length', 'video')
52
+ webhook_url = data.get('webhook_url')
53
+ id = data.get('id')
54
+
55
+ logger.info(f"Job {job_id}: Received audio mixing request for {video_url} and {audio_url}")
56
+
57
+ try:
58
+ # Process audio and video mixing
59
+ output_filename = process_audio_mixing(
60
+ video_url, audio_url, video_vol, audio_vol, output_length, job_id, webhook_url
61
+ )
62
+
63
+ # Upload the mixed file using the unified upload_file() method
64
+ cloud_url = upload_file(output_filename)
65
+
66
+ logger.info(f"Job {job_id}: Mixed media uploaded to cloud storage: {cloud_url}")
67
+
68
+ # Return the cloud URL for the uploaded file
69
+ return cloud_url, "/audio-mixing", 200
70
+
71
+ except Exception as e:
72
+ logger.error(f"Job {job_id}: Error during audio mixing process - {str(e)}")
73
+ return str(e), "/audio-mixing", 500
routes/authenticate.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint, request, jsonify, current_app
20
+ from app_utils import *
21
+ from functools import wraps
22
+ import os
23
+
24
+ auth_bp = Blueprint('auth', __name__)
25
+
26
+ API_KEY = os.environ.get('API_KEY')
27
+
28
+ @auth_bp.route('/authenticate', methods=['GET'])
29
+ @queue_task_wrapper(bypass_queue=True)
30
+ def authenticate_endpoint(**kwargs):
31
+ api_key = request.headers.get('X-API-Key')
32
+ if api_key == API_KEY:
33
+ return "Authorized", "/authenticate", 200
34
+ else:
35
+ return "Unauthorized", "/authenticate", 401
routes/caption_video.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint, current_app
20
+ from app_utils import *
21
+ import logging
22
+ from services.caption_video import process_captioning
23
+ from services.authentication import authenticate
24
+ from services.cloud_storage import upload_file
25
+ import os
26
+
27
+ caption_bp = Blueprint('caption', __name__)
28
+ logger = logging.getLogger(__name__)
29
+
30
+ @caption_bp.route('/caption-video', methods=['POST'])
31
+ @authenticate
32
+ @validate_payload({
33
+ "type": "object",
34
+ "properties": {
35
+ "video_url": {"type": "string", "format": "uri"},
36
+ "srt": {"type": "string"},
37
+ "ass": {"type": "string"},
38
+ "options": {
39
+ "type": "array",
40
+ "items": {
41
+ "type": "object",
42
+ "properties": {
43
+ "option": {"type": "string"},
44
+ "value": {} # Allow any type for value
45
+ },
46
+ "required": ["option", "value"]
47
+ }
48
+ },
49
+ "webhook_url": {"type": "string", "format": "uri"},
50
+ "id": {"type": "string"}
51
+ },
52
+ "required": ["video_url"],
53
+ "oneOf": [
54
+ {"required": ["srt"]},
55
+ {"required": ["ass"]}
56
+ ],
57
+ "additionalProperties": False
58
+ })
59
+ @queue_task_wrapper(bypass_queue=False)
60
+ def caption_video(job_id, data):
61
+ video_url = data['video_url']
62
+ caption_srt = data.get('srt')
63
+ caption_ass = data.get('ass')
64
+ options = data.get('options', [])
65
+ webhook_url = data.get('webhook_url')
66
+ id = data.get('id')
67
+
68
+ logger.info(f"Job {job_id}: Received captioning request for {video_url}")
69
+ logger.info(f"Job {job_id}: Options received: {options}")
70
+
71
+ if caption_ass is not None:
72
+ captions = caption_ass
73
+ caption_type = "ass"
74
+ else:
75
+ captions = caption_srt
76
+ caption_type = "srt"
77
+
78
+ try:
79
+ output_filename = process_captioning(video_url, captions, caption_type, options, job_id)
80
+ logger.info(f"Job {job_id}: Captioning process completed successfully")
81
+
82
+ # Upload the captioned video using the unified upload_file() method
83
+ cloud_url = upload_file(output_filename)
84
+
85
+ logger.info(f"Job {job_id}: Captioned video uploaded to cloud storage: {cloud_url}")
86
+
87
+ # Return the cloud URL for the uploaded file
88
+ return cloud_url, "/caption-video", 200
89
+
90
+ except Exception as e:
91
+ logger.error(f"Job {job_id}: Error during captioning process - {str(e)}", exc_info=True)
92
+ return str(e), "/caption-video", 500
routes/combine_videos.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint
20
+ from app_utils import *
21
+ import logging
22
+ from services.ffmpeg_toolkit import process_video_combination
23
+ from services.authentication import authenticate
24
+ from services.cloud_storage import upload_file
25
+
26
+ combine_bp = Blueprint('combine', __name__)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ @combine_bp.route('/combine-videos', methods=['POST'])
30
+ @authenticate
31
+ @validate_payload({
32
+ "type": "object",
33
+ "properties": {
34
+ "video_urls": {
35
+ "type": "array",
36
+ "items": {
37
+ "type": "object",
38
+ "properties": {
39
+ "video_url": {"type": "string", "format": "uri"}
40
+ },
41
+ "required": ["video_url"]
42
+ },
43
+ "minItems": 1
44
+ },
45
+ "webhook_url": {"type": "string", "format": "uri"},
46
+ "id": {"type": "string"}
47
+ },
48
+ "required": ["video_urls"],
49
+ "additionalProperties": False
50
+ })
51
+ @queue_task_wrapper(bypass_queue=False)
52
+ def combine_videos(job_id, data):
53
+ media_urls = data['video_urls']
54
+ webhook_url = data.get('webhook_url')
55
+ id = data.get('id')
56
+
57
+ logger.info(f"Job {job_id}: Received combine-videos request for {len(media_urls)} videos")
58
+
59
+ try:
60
+ output_file = process_video_combination(media_urls, job_id)
61
+ logger.info(f"Job {job_id}: Video combination process completed successfully")
62
+
63
+ cloud_url = upload_file(output_file)
64
+ logger.info(f"Job {job_id}: Combined video uploaded to cloud storage: {cloud_url}")
65
+
66
+ return cloud_url, "/combine-videos", 200
67
+
68
+ except Exception as e:
69
+ logger.error(f"Job {job_id}: Error during video combination process - {str(e)}")
70
+ return str(e), "/combine-videos", 500
routes/extract_keyframes.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint
20
+ from app_utils import *
21
+ import logging
22
+ from services.extract_keyframes import process_keyframe_extraction
23
+ from services.authentication import authenticate
24
+ from services.cloud_storage import upload_file
25
+
26
+ extract_keyframes_bp = Blueprint('extract_keyframes', __name__)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ @extract_keyframes_bp.route('/extract-keyframes', methods=['POST'])
30
+ @authenticate
31
+ @validate_payload({
32
+ "type": "object",
33
+ "properties": {
34
+ "video_url": {"type": "string", "format": "uri"},
35
+ "webhook_url": {"type": "string", "format": "uri"},
36
+ "id": {"type": "string"}
37
+ },
38
+ "required": ["video_url"],
39
+ "additionalProperties": False
40
+ })
41
+ @queue_task_wrapper(bypass_queue=False)
42
+ def extract_keyframes(job_id, data):
43
+ video_url = data.get('video_url')
44
+ webhook_url = data.get('webhook_url')
45
+ id = data.get('id')
46
+
47
+ logger.info(f"Job {job_id}: Received keyframe extraction request for {video_url}")
48
+
49
+ try:
50
+ # Process keyframe extraction
51
+ image_paths = process_keyframe_extraction(video_url, job_id)
52
+
53
+ # Upload each extracted keyframe and collect the cloud URLs
54
+ image_urls = []
55
+ for image_path in image_paths:
56
+ cloud_url = upload_file(image_path)
57
+ image_urls.append({"image_url": cloud_url})
58
+
59
+ logger.info(f"Job {job_id}: Keyframes uploaded to cloud storage")
60
+
61
+ # Return the URLs of the uploaded keyframes
62
+ return {"image_urls": image_urls}, "/extract-keyframes", 200
63
+
64
+ except Exception as e:
65
+ logger.error(f"Job {job_id}: Error during keyframe extraction - {str(e)}")
66
+ return str(e), "/extract-keyframes", 500
routes/gdrive_upload.py ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ import os
20
+ import logging
21
+ from flask import Blueprint, request, jsonify
22
+ import threading
23
+ import requests
24
+ import uuid
25
+ import json
26
+ from google.oauth2.service_account import Credentials
27
+ from google.auth.transport.requests import Request
28
+ from datetime import datetime
29
+ import time
30
+ import psutil
31
+ from services.authentication import authenticate
32
+ from app_utils import validate_payload, queue_task_wrapper
33
+
34
+ # Configure logging
35
+ logging.basicConfig(level=logging.INFO)
36
+ logger = logging.getLogger(__name__)
37
+
38
+ # Define the blueprint
39
+ gdrive_upload_bp = Blueprint('gdrive_upload', __name__)
40
+
41
+ # Environment variables
42
+ GCP_SA_CREDENTIALS = os.getenv('GCP_SA_CREDENTIALS')
43
+ GDRIVE_USER = os.getenv('GDRIVE_USER')
44
+
45
+ # Class to track upload progress
46
+ class UploadProgress:
47
+ def __init__(self, job_id, total_size):
48
+ self.job_id = job_id
49
+ self.total_size = total_size
50
+ self.bytes_uploaded = 0
51
+ self.start_time = time.time()
52
+ self.lock = threading.Lock()
53
+ self.last_logged_percentage = 0
54
+ self.last_logged_resource_percentage = 0 # For memory/disk logging every 5%
55
+
56
+ # Global list to keep track of active uploads
57
+ active_uploads = []
58
+ uploads_lock = threading.Lock()
59
+
60
+ def get_access_token():
61
+ """
62
+ Retrieves an access token for Google APIs using service account credentials.
63
+ """
64
+ credentials_info = json.loads(GCP_SA_CREDENTIALS)
65
+ credentials = Credentials.from_service_account_info(
66
+ credentials_info,
67
+ scopes=['https://www.googleapis.com/auth/drive']
68
+ )
69
+ delegated_credentials = credentials.with_subject(GDRIVE_USER)
70
+ if not delegated_credentials.valid or delegated_credentials.expired:
71
+ delegated_credentials.refresh(Request())
72
+ access_token = delegated_credentials.token
73
+ return access_token
74
+
75
+ def initiate_resumable_upload(filename, folder_id, mime_type='application/octet-stream'):
76
+ """
77
+ Initiates a resumable upload session with Google Drive and returns the upload URL.
78
+ """
79
+ url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable'
80
+ headers = {
81
+ 'Authorization': f'Bearer {get_access_token()}',
82
+ 'Content-Type': 'application/json; charset=UTF-8',
83
+ 'X-Upload-Content-Type': mime_type
84
+ }
85
+ metadata = {
86
+ 'name': filename,
87
+ 'parents': [folder_id]
88
+ }
89
+ response = requests.post(url, headers=headers, data=json.dumps(metadata))
90
+ response.raise_for_status()
91
+ upload_url = response.headers['Location']
92
+ return upload_url
93
+
94
+ def upload_file_in_chunks(file_url, upload_url, total_size, job_id, chunk_size):
95
+ """
96
+ Uploads the file to Google Drive in chunks by streaming data directly from the source URL.
97
+ """
98
+ bytes_uploaded = 0
99
+ max_retries = 5
100
+ retry_delay = 5 # seconds
101
+
102
+ progress = UploadProgress(job_id, total_size)
103
+
104
+ # Add progress to active_uploads
105
+ with uploads_lock:
106
+ active_uploads.append(progress)
107
+
108
+ try:
109
+ with requests.get(file_url, stream=True) as r:
110
+ r.raise_for_status()
111
+ iterator = r.iter_content(chunk_size=chunk_size)
112
+ for chunk in iterator:
113
+ if chunk:
114
+ for attempt in range(max_retries):
115
+ start = bytes_uploaded
116
+ end = bytes_uploaded + len(chunk) - 1
117
+ content_range = f'bytes {start}-{end}/{total_size}'
118
+ headers = {
119
+ 'Content-Length': str(len(chunk)),
120
+ 'Content-Range': content_range,
121
+ }
122
+ try:
123
+ upload_response = requests.put(
124
+ upload_url,
125
+ headers=headers,
126
+ data=chunk
127
+ )
128
+ if upload_response.status_code in (200, 201):
129
+ # Upload complete
130
+ logger.info(f"Job {job_id}: Upload complete.")
131
+ with progress.lock:
132
+ progress.bytes_uploaded = end + 1
133
+ return upload_response.json()['id']
134
+ elif upload_response.status_code == 308:
135
+ # Resumable upload incomplete
136
+ bytes_uploaded = end + 1
137
+ with progress.lock:
138
+ progress.bytes_uploaded = bytes_uploaded
139
+ break # Break retry loop and continue with next chunk
140
+ else:
141
+ # Handle unexpected status codes
142
+ logger.error(f"Job {job_id}: Unexpected status code: {upload_response.status_code}")
143
+ raise Exception(f"Upload failed with status code {upload_response.status_code}")
144
+ except requests.exceptions.RequestException as e:
145
+ logger.error(f"Job {job_id}: Network error during upload: {e}")
146
+ if attempt < max_retries - 1:
147
+ logger.info(f"Job {job_id}: Retrying upload chunk after {retry_delay} seconds...")
148
+ time.sleep(retry_delay)
149
+ continue
150
+ else:
151
+ logger.error(f"Job {job_id}: Max retries reached. Upload failed.")
152
+ raise
153
+ else:
154
+ # If we exhausted retries, exit the function
155
+ raise Exception("Failed to upload chunk after multiple retries.")
156
+ finally:
157
+ # Remove progress from active_uploads
158
+ with uploads_lock:
159
+ if progress in active_uploads:
160
+ active_uploads.remove(progress)
161
+
162
+ @gdrive_upload_bp.route('/gdrive-upload', methods=['POST'])
163
+ @authenticate
164
+ @validate_payload({
165
+ "type": "object",
166
+ "properties": {
167
+ "file_url": {"type": "string", "format": "uri"},
168
+ "filename": {"type": "string"},
169
+ "folder_id": {"type": "string"},
170
+ "mime_type": {"type": "string"},
171
+ "chunk_size": {"type": "integer", "minimum": 1},
172
+ "webhook_url": {"type": "string", "format": "uri"},
173
+ "id": {"type": "string"}
174
+ },
175
+ "required": ["file_url", "filename", "folder_id"],
176
+ "additionalProperties": False
177
+ })
178
+ @queue_task_wrapper(bypass_queue=False)
179
+ def gdrive_upload(job_id, data):
180
+ logger.info(f"Processing Job ID: {job_id}")
181
+
182
+ if not GDRIVE_USER:
183
+ logger.error("GDRIVE_USER environment variable is not set")
184
+ return "GDRIVE_USER environment variable is not set", "/gdrive-upload", 400
185
+
186
+ try:
187
+ file_url = data['file_url']
188
+ filename = data['filename']
189
+ folder_id = data['folder_id']
190
+ mime_type = data.get('mime_type', 'application/octet-stream')
191
+ chunk_size = data.get('chunk_size', 5 * 1024 * 1024) # Default to 5 MB
192
+
193
+ # Get the total size of the file
194
+ try:
195
+ head_response = requests.head(file_url, allow_redirects=True, timeout=30)
196
+ head_response.raise_for_status()
197
+ total_size = int(head_response.headers.get('Content-Length', 0))
198
+
199
+ get_response = requests.get(file_url, stream=True, timeout=30)
200
+ get_response.raise_for_status()
201
+ total_size = int(get_response.headers.get('Content-Length', 0))
202
+ if total_size == 0:
203
+ raise ValueError("Content-Length header is missing or zero")
204
+ except requests.exceptions.RequestException as e:
205
+ logger.error(f"Job {job_id}: Error accessing file URL: {str(e)}")
206
+ return f"Error accessing file URL: {str(e)}", "/gdrive-upload", 500
207
+ except ValueError as e:
208
+ logger.error(f"Job {job_id}: {str(e)}")
209
+ return f"Unable to determine file size: {str(e)}", "/gdrive-upload", 500
210
+
211
+ logger.info(f"Job {job_id}: File size determined: {total_size} bytes")
212
+
213
+ # Initiate upload session
214
+ upload_url = initiate_resumable_upload(filename, folder_id, mime_type)
215
+ logger.info(f"Job {job_id}: Resumable upload session initiated with chunk size {chunk_size} bytes.")
216
+
217
+ # Upload file in chunks
218
+ file_id = upload_file_in_chunks(file_url, upload_url, total_size, job_id, chunk_size)
219
+
220
+ return file_id, "/gdrive-upload", 200
221
+
222
+ except Exception as e:
223
+ logger.error(f"Job {job_id}: Error during processing - {str(e)}")
224
+ return str(e), "/gdrive-upload", 500
225
+
226
+ def log_system_resources():
227
+ """
228
+ Logs system resource usage and upload progress at regular intervals.
229
+ """
230
+ while True:
231
+ # Get memory and disk usage
232
+ memory_info = psutil.virtual_memory()
233
+ disk_info = psutil.disk_usage('/')
234
+
235
+ with uploads_lock:
236
+ for progress in active_uploads:
237
+ with progress.lock:
238
+ # Calculate the percentage uploaded
239
+ percentage = (progress.bytes_uploaded / progress.total_size) * 100 if progress.total_size > 0 else 0
240
+ elapsed_time = time.time() - progress.start_time
241
+
242
+ # Log upload progress every 1%
243
+ if int(percentage) >= progress.last_logged_percentage + 1:
244
+ progress.last_logged_percentage = int(percentage)
245
+ logger.info(
246
+ f"Job {progress.job_id}: Uploaded {progress.bytes_uploaded} of {progress.total_size} bytes "
247
+ f"({percentage:.2f}%), Elapsed Time: {int(elapsed_time)} seconds"
248
+ )
249
+
250
+ # Log system resource usage every 5%
251
+ if int(percentage) >= progress.last_logged_resource_percentage + 5:
252
+ progress.last_logged_resource_percentage = int(percentage)
253
+ current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
254
+ logger.info(f"[{current_time}] Memory Usage: {memory_info.percent}% used")
255
+ logger.info(f"[{current_time}] Disk Usage: {disk_info.percent}% used")
256
+
257
+ # Sleep for 1 second before the next update
258
+ time.sleep(1)
259
+
260
+ # Start the resource logging in a separate thread
261
+ resource_logging_thread = threading.Thread(
262
+ target=log_system_resources,
263
+ daemon=True
264
+ )
265
+ resource_logging_thread.start()
routes/image_to_video.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint
20
+ from app_utils import *
21
+ import logging
22
+ from services.image_to_video import process_image_to_video
23
+ from services.authentication import authenticate
24
+ from services.cloud_storage import upload_file
25
+
26
+ image_to_video_bp = Blueprint('image_to_video', __name__)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ @image_to_video_bp.route('/image-to-video', methods=['POST'])
30
+ @authenticate
31
+ @validate_payload({
32
+ "type": "object",
33
+ "properties": {
34
+ "image_url": {"type": "string", "format": "uri"},
35
+ "length": {"type": "number", "minimum": 1, "maximum": 60},
36
+ "frame_rate": {"type": "integer", "minimum": 15, "maximum": 60},
37
+ "zoom_speed": {"type": "number", "minimum": 0, "maximum": 100},
38
+ "webhook_url": {"type": "string", "format": "uri"},
39
+ "id": {"type": "string"}
40
+ },
41
+ "required": ["image_url"],
42
+ "additionalProperties": False
43
+ })
44
+ @queue_task_wrapper(bypass_queue=False)
45
+ def image_to_video(job_id, data):
46
+ image_url = data.get('image_url')
47
+ length = data.get('length', 5)
48
+ frame_rate = data.get('frame_rate', 30)
49
+ zoom_speed = data.get('zoom_speed', 3) / 100
50
+ webhook_url = data.get('webhook_url')
51
+ id = data.get('id')
52
+
53
+ logger.info(f"Job {job_id}: Received image to video request for {image_url}")
54
+
55
+ try:
56
+ # Process image to video conversion
57
+ output_filename = process_image_to_video(
58
+ image_url, length, frame_rate, zoom_speed, job_id, webhook_url
59
+ )
60
+
61
+ # Upload the resulting file using the unified upload_file() method
62
+ cloud_url = upload_file(output_filename)
63
+
64
+ # Log the successful upload
65
+ logger.info(f"Job {job_id}: Converted video uploaded to cloud storage: {cloud_url}")
66
+
67
+ # Return the cloud URL for the uploaded file
68
+ return cloud_url, "/image-to-video", 200
69
+
70
+ except Exception as e:
71
+ logger.error(f"Job {job_id}: Error processing image to video: {str(e)}", exc_info=True)
72
+ return str(e), "/image-to-video", 500
routes/media_to_mp3.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ # routes/media_to_mp3.py
20
+ from flask import Blueprint, current_app
21
+ from app_utils import *
22
+ import logging
23
+ from services.ffmpeg_toolkit import process_conversion
24
+ from services.authentication import authenticate
25
+ from services.cloud_storage import upload_file
26
+ import os
27
+
28
+ convert_bp = Blueprint('convert', __name__)
29
+ logger = logging.getLogger(__name__)
30
+
31
+ @convert_bp.route('/media-to-mp3', methods=['POST'])
32
+ @authenticate
33
+ @validate_payload({
34
+ "type": "object",
35
+ "properties": {
36
+ "media_url": {"type": "string", "format": "uri"},
37
+ "webhook_url": {"type": "string", "format": "uri"},
38
+ "id": {"type": "string"},
39
+ "bitrate": {"type": "string", "pattern": "^[0-9]+k$"}
40
+ },
41
+ "required": ["media_url"],
42
+ "additionalProperties": False
43
+ })
44
+ @queue_task_wrapper(bypass_queue=False)
45
+ def convert_media_to_mp3(job_id, data):
46
+ media_url = data['media_url']
47
+ webhook_url = data.get('webhook_url')
48
+ id = data.get('id')
49
+ bitrate = data.get('bitrate', '128k')
50
+
51
+ logger.info(f"Job {job_id}: Received media-to-mp3 request for media URL: {media_url}")
52
+
53
+ try:
54
+ output_file = process_conversion(media_url, job_id, bitrate)
55
+ logger.info(f"Job {job_id}: Media conversion process completed successfully")
56
+
57
+ cloud_url = upload_file(output_file)
58
+ logger.info(f"Job {job_id}: Converted media uploaded to cloud storage: {cloud_url}")
59
+
60
+ return cloud_url, "/media-to-mp3", 200
61
+
62
+ except Exception as e:
63
+ logger.error(f"Job {job_id}: Error during media conversion process - {str(e)}")
64
+ return str(e), "/media-to-mp3", 500
routes/transcribe_media.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint
20
+ from app_utils import *
21
+ import logging
22
+ import os
23
+ from services.transcription import process_transcription
24
+ from services.authentication import authenticate
25
+ from services.cloud_storage import upload_file
26
+
27
+ transcribe_bp = Blueprint('transcribe', __name__)
28
+ logger = logging.getLogger(__name__)
29
+
30
+ @transcribe_bp.route('/transcribe-media', methods=['POST'])
31
+ @authenticate
32
+ @validate_payload({
33
+ "type": "object",
34
+ "properties": {
35
+ "media_url": {"type": "string", "format": "uri"},
36
+ "output": {"type": "string", "enum": ["transcript", "srt", "vtt", "ass"]},
37
+ "webhook_url": {"type": "string", "format": "uri"},
38
+ "max_chars": {"type": "integer"},
39
+ "id": {"type": "string"}
40
+ },
41
+ "required": ["media_url"],
42
+ "additionalProperties": False
43
+ })
44
+ @queue_task_wrapper(bypass_queue=False)
45
+ def transcribe(job_id, data):
46
+ media_url = data['media_url']
47
+ output = data.get('output', 'transcript')
48
+ webhook_url = data.get('webhook_url')
49
+ max_chars = data.get('max_chars', 56)
50
+ id = data.get('id')
51
+
52
+ logger.info(f"Job {job_id}: Received transcription request for {media_url}")
53
+
54
+ try:
55
+ result = process_transcription(media_url, output, max_chars)
56
+ logger.info(f"Job {job_id}: Transcription process completed successfully")
57
+
58
+ # If the result is a file path, upload it using the unified upload_file() method
59
+ if output in ['srt', 'vtt', 'ass']:
60
+ cloud_url = upload_file(result)
61
+ os.remove(result) # Remove the temporary file after uploading
62
+ return cloud_url, "/transcribe-media", 200
63
+ else:
64
+ return result, "/transcribe-media", 200
65
+
66
+ except Exception as e:
67
+ logger.error(f"Job {job_id}: Error during transcription process - {str(e)}")
68
+ return str(e), "/transcribe-media", 500
routes/v1/audio/concatenate.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint
2
+ from app_utils import *
3
+ import logging
4
+ from services.v1.audio.concatenate import process_audio_concatenate
5
+ from services.authentication import authenticate
6
+ from services.cloud_storage import upload_file
7
+
8
+ v1_audio_concatenate_bp = Blueprint("v1_audio_concatenate", __name__)
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ @v1_audio_concatenate_bp.route("/v1/audio/concatenate", methods=["POST"])
13
+ @authenticate
14
+ @validate_payload(
15
+ {
16
+ "type": "object",
17
+ "properties": {
18
+ "audio_urls": {
19
+ "type": "array",
20
+ "items": {
21
+ "type": "object",
22
+ "properties": {"audio_url": {"type": "string", "format": "uri"}},
23
+ "required": ["audio_url"],
24
+ },
25
+ "minItems": 1,
26
+ },
27
+ "webhook_url": {"type": "string", "format": "uri"},
28
+ "id": {"type": "string"},
29
+ },
30
+ "required": ["audio_urls"],
31
+ "additionalProperties": False,
32
+ }
33
+ )
34
+ @queue_task_wrapper(bypass_queue=False)
35
+ def combine_audio(job_id, data):
36
+ media_urls = data["audio_urls"]
37
+ webhook_url = data.get("webhook_url")
38
+ id = data.get("id")
39
+
40
+ logger.info(
41
+ f"Job {job_id}: Received combine-audio request for {len(media_urls)} audio files"
42
+ )
43
+
44
+ try:
45
+ output_file = process_audio_concatenate(media_urls, job_id)
46
+ logger.info(f"Job {job_id}: Audio combination process completed successfully")
47
+
48
+ cloud_url = upload_file(output_file)
49
+ logger.info(
50
+ f"Job {job_id}: Combined audio uploaded to cloud storage: {cloud_url}"
51
+ )
52
+
53
+ return cloud_url, "/v1/audio/concatenate", 200
54
+
55
+ except Exception as e:
56
+ logger.error(f"Job {job_id}: Error during audio combination process - {str(e)}")
57
+ return str(e), "/v1/audio/concatenate", 500
routes/v1/code/execute/execute_python.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ import os
20
+ import logging
21
+ from flask import Blueprint, request
22
+ from services.authentication import authenticate
23
+ from app_utils import validate_payload, queue_task_wrapper
24
+ import subprocess
25
+ import tempfile
26
+ import json
27
+ import textwrap
28
+
29
+ v1_code_execute_bp = Blueprint('v1_code_execute', __name__)
30
+ logger = logging.getLogger(__name__)
31
+
32
+ @v1_code_execute_bp.route('/v1/code/execute/python', methods=['POST'])
33
+ @authenticate
34
+ @validate_payload({
35
+ "type": "object",
36
+ "properties": {
37
+ "code": {"type": "string"},
38
+ "timeout": {"type": "integer", "minimum": 1, "maximum": 300},
39
+ "webhook_url": {"type": "string", "format": "uri"},
40
+ "id": {"type": "string"}
41
+ },
42
+ "required": ["code"],
43
+ "additionalProperties": False
44
+ })
45
+ @queue_task_wrapper(bypass_queue=False)
46
+ def execute_python(job_id, data):
47
+ logger.info(f"Job {job_id}: Received Python code execution request")
48
+
49
+ try:
50
+ code = data['code']
51
+ timeout = data.get('timeout', 30)
52
+
53
+ # Indent user code
54
+ indented_code = textwrap.indent(code, ' ')
55
+
56
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as temp_file:
57
+ template = '''import sys
58
+ import json
59
+ from io import StringIO
60
+ import contextlib
61
+
62
+ @contextlib.contextmanager
63
+ def capture_output():
64
+ stdout, stderr = StringIO(), StringIO()
65
+ old_out, old_err = sys.stdout, sys.stderr
66
+ try:
67
+ sys.stdout, sys.stderr = stdout, stderr
68
+ yield stdout, stderr
69
+ finally:
70
+ sys.stdout, sys.stderr = old_out, old_err
71
+
72
+ def execute_code():
73
+ {}
74
+
75
+ with capture_output() as (stdout, stderr):
76
+ try:
77
+ result_value = execute_code()
78
+ except Exception as e:
79
+ print(f"Error: {{str(e)}}", file=sys.stderr)
80
+ result_value = None
81
+
82
+ result = {{
83
+ 'stdout': stdout.getvalue(),
84
+ 'stderr': stderr.getvalue(),
85
+ 'return_value': result_value
86
+ }}
87
+ print(json.dumps(result))
88
+ '''
89
+
90
+ final_code = template.format(indented_code)
91
+ temp_file.write(final_code)
92
+ temp_file.flush()
93
+
94
+ # Log the generated code for debugging
95
+ logger.debug(f"Generated code:\n{final_code}")
96
+
97
+ try:
98
+ result = subprocess.run(
99
+ ['python3', temp_file.name],
100
+ capture_output=True,
101
+ text=True,
102
+ timeout=timeout
103
+ )
104
+
105
+ try:
106
+ output = json.loads(result.stdout)
107
+ if result.returncode != 0 or output['stderr']:
108
+ return {
109
+ 'error': output['stderr'] or 'Execution failed',
110
+ 'stdout': output['stdout'],
111
+ 'exit_code': result.returncode
112
+ }, '/v1/code/execute/python', 400
113
+
114
+ return {
115
+ 'result': output['return_value'],
116
+ 'stdout': output['stdout'],
117
+ 'stderr': output['stderr'],
118
+ 'exit_code': result.returncode
119
+ }, '/v1/code/execute/python', 200
120
+
121
+ except json.JSONDecodeError:
122
+ return {
123
+ 'error': 'Failed to parse execution result',
124
+ 'stdout': result.stdout,
125
+ 'stderr': result.stderr,
126
+ 'exit_code': result.returncode
127
+ }, '/v1/code/execute/python', 500
128
+
129
+ except subprocess.TimeoutExpired:
130
+ return {"error": f"Execution timed out after {timeout} seconds"}, '/v1/code/execute/python', 408
131
+ except subprocess.SubprocessError as e:
132
+ return {"error": f"Execution failed: {str(e)}"}, '/v1/code/execute/python', 500
133
+
134
+ except Exception as e:
135
+ logger.error(f"Job {job_id}: Error executing Python code: {str(e)}")
136
+ return {"error": str(e)}, '/v1/code/execute/python', 500
137
+
138
+ finally:
139
+ if 'temp_file' in locals():
140
+ os.unlink(temp_file.name)
routes/v1/ffmpeg/ffmpeg_compose.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ import os
20
+ import logging
21
+ from flask import Blueprint, request, jsonify
22
+ from app_utils import *
23
+ from services.v1.ffmpeg.ffmpeg_compose import process_ffmpeg_compose
24
+ from services.authentication import authenticate
25
+ from services.cloud_storage import upload_file
26
+
27
+ v1_ffmpeg_compose_bp = Blueprint('v1_ffmpeg_compose', __name__)
28
+ logger = logging.getLogger(__name__)
29
+
30
+ @v1_ffmpeg_compose_bp.route('/v1/ffmpeg/compose', methods=['POST'])
31
+ @authenticate
32
+ @validate_payload({
33
+ "type": "object",
34
+ "properties": {
35
+ "inputs": {
36
+ "type": "array",
37
+ "items": {
38
+ "type": "object",
39
+ "properties": {
40
+ "file_url": {"type": "string", "format": "uri"},
41
+ "options": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "object",
45
+ "properties": {
46
+ "option": {"type": "string"},
47
+ "argument": {"type": ["string", "number", "null"]}
48
+ },
49
+ "required": ["option"]
50
+ }
51
+ }
52
+ },
53
+ "required": ["file_url"]
54
+ },
55
+ "minItems": 1
56
+ },
57
+ "filters": {
58
+ "type": "array",
59
+ "items": {
60
+ "type": "object",
61
+ "properties": {
62
+ "filter": {"type": "string"}
63
+ },
64
+ "required": ["filter"]
65
+ }
66
+ },
67
+ "outputs": {
68
+ "type": "array",
69
+ "items": {
70
+ "type": "object",
71
+ "properties": {
72
+ "options": {
73
+ "type": "array",
74
+ "items": {
75
+ "type": "object",
76
+ "properties": {
77
+ "option": {"type": "string"},
78
+ "argument": {"type": ["string", "number", "null"]}
79
+ },
80
+ "required": ["option"]
81
+ }
82
+ }
83
+ },
84
+ "required": ["options"]
85
+ },
86
+ "minItems": 1
87
+ },
88
+ "global_options": {
89
+ "type": "array",
90
+ "items": {
91
+ "type": "object",
92
+ "properties": {
93
+ "option": {"type": "string"},
94
+ "argument": {"type": ["string", "number", "null"]}
95
+ },
96
+ "required": ["option"]
97
+ }
98
+ },
99
+ "metadata": {
100
+ "type": "object",
101
+ "properties": {
102
+ "thumbnail": {"type": "boolean"},
103
+ "filesize": {"type": "boolean"},
104
+ "duration": {"type": "boolean"},
105
+ "bitrate": {"type": "boolean"},
106
+ "encoder": {"type": "boolean"}
107
+ }
108
+ },
109
+ "webhook_url": {"type": "string", "format": "uri"},
110
+ "id": {"type": "string"}
111
+ },
112
+ "required": ["inputs", "outputs"],
113
+ "additionalProperties": False
114
+ })
115
+ @queue_task_wrapper(bypass_queue=False)
116
+ def ffmpeg_api(job_id, data):
117
+ logger.info(f"Job {job_id}: Received flexible FFmpeg request")
118
+
119
+ try:
120
+ output_filenames, metadata = process_ffmpeg_compose(data, job_id)
121
+
122
+ # Upload output files to GCP and create result array
123
+ output_urls = []
124
+ for i, output_filename in enumerate(output_filenames):
125
+ if os.path.exists(output_filename):
126
+ upload_url = upload_file(output_filename)
127
+ output_info = {"file_url": upload_url}
128
+
129
+ if metadata and i < len(metadata):
130
+ output_metadata = metadata[i]
131
+ if 'thumbnail' in output_metadata:
132
+ thumbnail_path = output_metadata['thumbnail']
133
+ if os.path.exists(thumbnail_path):
134
+ thumbnail_url = upload_file(thumbnail_path)
135
+ del output_metadata['thumbnail']
136
+ output_metadata['thumbnail_url'] = thumbnail_url
137
+ os.remove(thumbnail_path) # Clean up local thumbnail file
138
+ output_info.update(output_metadata)
139
+
140
+ output_urls.append(output_info)
141
+ os.remove(output_filename) # Clean up local output file after upload
142
+ else:
143
+ raise Exception(f"Expected output file {output_filename} not found")
144
+
145
+ return output_urls, "/v1/ffmpeg/compose", 200
146
+
147
+ except Exception as e:
148
+ logger.error(f"Job {job_id}: Error processing FFmpeg request - {str(e)}")
149
+ return str(e), "/v1/ffmpeg/compose", 500
routes/v1/image/convert/image_to_video.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ from flask import Blueprint
20
+ from app_utils import *
21
+ import logging
22
+ from services.v1.image.convert.image_to_video import process_image_to_video
23
+ from services.authentication import authenticate
24
+ from services.cloud_storage import upload_file
25
+
26
+ v1_image_convert_video_bp = Blueprint('v1_image_convert_video', __name__)
27
+ logger = logging.getLogger(__name__)
28
+
29
+ @v1_image_convert_video_bp.route('/v1/image/convert/video', methods=['POST'])
30
+ @v1_image_convert_video_bp.route('/v1/image/transform/video', methods=['POST']) #depleft for backwards compatibility, do not use.
31
+ @authenticate
32
+ @validate_payload({
33
+ "type": "object",
34
+ "properties": {
35
+ "image_url": {"type": "string", "format": "uri"},
36
+ "length": {"type": "number", "minimum": 0.1, "maximum": 400},
37
+ "frame_rate": {"type": "integer", "minimum": 15, "maximum": 60},
38
+ "zoom_speed": {"type": "number", "minimum": 0, "maximum": 100},
39
+ "webhook_url": {"type": "string", "format": "uri"},
40
+ "id": {"type": "string"}
41
+ },
42
+ "required": ["image_url"],
43
+ "additionalProperties": False
44
+ })
45
+ @queue_task_wrapper(bypass_queue=False)
46
+ def image_to_video(job_id, data):
47
+ image_url = data.get('image_url')
48
+ length = data.get('length', 5)
49
+ frame_rate = data.get('frame_rate', 30)
50
+ zoom_speed = data.get('zoom_speed', 3) / 100
51
+ webhook_url = data.get('webhook_url')
52
+ id = data.get('id')
53
+
54
+ logger.info(f"Job {job_id}: Received image to video request for {image_url}")
55
+
56
+ try:
57
+ # Process image to video conversion
58
+ output_filename = process_image_to_video(
59
+ image_url, length, frame_rate, zoom_speed, job_id, webhook_url
60
+ )
61
+
62
+ # Upload the resulting file using the unified upload_file() method
63
+ cloud_url = upload_file(output_filename)
64
+
65
+ # Log the successful upload
66
+ logger.info(f"Job {job_id}: Converted video uploaded to cloud storage: {cloud_url}")
67
+
68
+ # Return the cloud URL for the uploaded file
69
+ return cloud_url, "/v1/image/convert/video", 200
70
+
71
+ except Exception as e:
72
+ logger.error(f"Job {job_id}: Error processing image to video: {str(e)}", exc_info=True)
73
+ return str(e), "/v1/image/convert/video", 500
routes/v1/image/screenshot_webpage.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+ # Author: Harrison Fisher (https://github.com/HarrisonFisher)
19
+ # Date: April 2025
20
+ # Created new route: /v1/playwright/screenshot
21
+
22
+ from flask import Blueprint, request, jsonify
23
+ from app_utils import *
24
+ from app_utils import validate_payload, queue_task_wrapper
25
+ import logging
26
+ import os
27
+ from services.v1.image.screenshot_webpage import take_screenshot
28
+ from services.authentication import authenticate
29
+ from services.cloud_storage import upload_file
30
+ from playwright.sync_api import sync_playwright
31
+ from io import BytesIO
32
+
33
+
34
+ v1_image_screenshot_webpage_bp = Blueprint('v1_image_screenshot_webpage', __name__)
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ @v1_image_screenshot_webpage_bp.route('/v1/image/screenshot/webpage', methods=['POST'])
39
+ @authenticate
40
+ @validate_payload({
41
+ "type": "object",
42
+ "properties": {
43
+ "url": {"type": "string", "format": "uri"},
44
+ "html": {"type": "string"},
45
+ "viewport_width": {"type": "integer", "minimum": 1},
46
+ "viewport_height": {"type": "integer", "minimum": 1},
47
+ "full_page": {"type": "boolean", "default": False},
48
+ "format": {"type": "string", "enum": ["png", "jpeg"], "default": "png"},
49
+ "delay": {"type": "integer", "minimum": 0},
50
+ "device_scale_factor": {"type": "number", "minimum": 0.1},
51
+ "user_agent": {"type": "string"},
52
+ "cookies": {
53
+ "type": "array",
54
+ "items": {
55
+ "type": "object",
56
+ "required": ["name", "value", "domain"],
57
+ "properties": {
58
+ "name": {"type": "string"},
59
+ "value": {"type": "string"},
60
+ "domain": {"type": "string"},
61
+ "path": {"type": "string", "default": "/"},
62
+ },
63
+ "additionalProperties": True
64
+ }
65
+ },
66
+ "headers": {
67
+ "type": "object",
68
+ "additionalProperties": {"type": "string"}
69
+ },
70
+ "quality": {"type": "integer", "minimum": 0, "maximum": 100},
71
+ "clip": {
72
+ "type": "object",
73
+ "required": ["x", "y", "width", "height"],
74
+ "properties": {
75
+ "x": {"type": "number"},
76
+ "y": {"type": "number"},
77
+ "width": {"type": "number", "exclusiveMinimum": 0},
78
+ "height": {"type": "number", "exclusiveMinimum": 0}
79
+ }
80
+ },
81
+ "timeout": {"type": "integer", "minimum": 100},
82
+ "wait_until": {"type": "string", "enum": ["load", "domcontentloaded", "networkidle", "networkidle2"], "default": "load"},
83
+ "wait_for_selector": {"type": "string"},
84
+ "emulate": {
85
+ "type": "object",
86
+ "properties": {
87
+ "color_scheme": {
88
+ "type": "string",
89
+ "enum": ["light", "dark"]
90
+ }
91
+ }
92
+ },
93
+ "omit_background": {"type": "boolean", "default": False},
94
+ "selector": {"type": "string"},
95
+ "js": {"type": "string"},
96
+ "css": {"type": "string"},
97
+ "webhook_url": {"type": "string", "format": "uri"},
98
+ "id": {"type": "string"}
99
+ },
100
+ "oneOf": [
101
+ {"required": ["url"]},
102
+ {"required": ["html"]}
103
+ ],
104
+ "not": {"required": ["url", "html"]},
105
+ "additionalProperties": False
106
+ })
107
+ @queue_task_wrapper(bypass_queue=False)
108
+ def screenshot(job_id, data):
109
+ logger.info(f"Job {job_id}: Received screenshot request for {data.get('url')}")
110
+ try:
111
+ screenshot_io = take_screenshot(data, job_id)
112
+ if isinstance(screenshot_io, dict) and 'error' in screenshot_io:
113
+ logger.error(f"Job {job_id}: Screenshot error: {screenshot_io['error']}")
114
+ return {"error": screenshot_io['error']}, "/v1/image/screenshot/webpage", 400
115
+ format = data.get("format", "png")
116
+ temp_file_path = f"{job_id}_screenshot.{format}"
117
+ with open(temp_file_path, "wb") as temp_file:
118
+ temp_file.write(screenshot_io.read())
119
+ cloud_url = upload_file(temp_file_path)
120
+ os.remove(temp_file_path)
121
+ logger.info(f"Job {job_id}: Screenshot successfully processed and uploaded.")
122
+ return cloud_url, "/v1/image/screenshot/webpage", 200
123
+ except Exception as e:
124
+ logger.error(f"Job {job_id}: Error processing screenshot: {str(e)}", exc_info=True)
125
+ return {"error": str(e)}, "/v1/image/screenshot/webpage", 500
routes/v1/media/convert/media_convert.py ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+ from flask import Blueprint, jsonify
18
+ from app_utils import validate_payload, queue_task_wrapper
19
+ import logging
20
+ from services.v1.media.convert.media_convert import process_media_convert
21
+ from services.authentication import authenticate
22
+ from services.cloud_storage import upload_file
23
+ import os
24
+
25
+ v1_media_convert_bp = Blueprint('v1_media_convert', __name__)
26
+ logger = logging.getLogger(__name__)
27
+
28
+ @v1_media_convert_bp.route('/v1/media/convert', methods=['POST'])
29
+ @authenticate
30
+ @validate_payload({
31
+ "type": "object",
32
+ "properties": {
33
+ "media_url": {"type": "string", "format": "uri"},
34
+ "format": {"type": "string"},
35
+ "video_codec": {"type": "string"},
36
+ "video_preset": {"type": "string"},
37
+ "video_crf": {"type": "number", "minimum": 0, "maximum": 51},
38
+ "audio_codec": {"type": "string"},
39
+ "audio_bitrate": {"type": "string"},
40
+ "webhook_url": {"type": "string", "format": "uri"},
41
+ "id": {"type": "string"}
42
+ },
43
+ "required": ["media_url", "format"],
44
+ "additionalProperties": False
45
+ })
46
+ @queue_task_wrapper(bypass_queue=False)
47
+ def convert_media_format(job_id, data):
48
+ media_url = data['media_url']
49
+ output_format = data['format']
50
+ video_codec = data.get('video_codec', 'libx264')
51
+ video_preset = data.get('video_preset', 'medium')
52
+ video_crf = data.get('video_crf', 23)
53
+ audio_codec = data.get('audio_codec', 'aac')
54
+ audio_bitrate = data.get('audio_bitrate', '128k')
55
+ webhook_url = data.get('webhook_url')
56
+ id = data.get('id')
57
+
58
+ logger.info(f"Job {job_id}: Received media conversion request for media URL: {media_url} to format: {output_format}")
59
+
60
+ try:
61
+ output_file = process_media_convert(
62
+ media_url,
63
+ job_id,
64
+ output_format,
65
+ video_codec,
66
+ video_preset,
67
+ video_crf,
68
+ audio_codec,
69
+ audio_bitrate,
70
+ webhook_url
71
+ )
72
+ logger.info(f"Job {job_id}: Media format conversion completed successfully")
73
+
74
+ cloud_url = upload_file(output_file)
75
+ logger.info(f"Job {job_id}: Converted media uploaded to cloud storage: {cloud_url}")
76
+
77
+ return cloud_url, "/v1/media/convert", 200
78
+
79
+ except Exception as e:
80
+ logger.error(f"Job {job_id}: Error during media conversion process - {str(e)}")
81
+ return {"error": str(e)}, "/v1/media/convert", 500
routes/v1/media/convert/media_to_mp3.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) 2025 Stephen G. Pope
2
+ #
3
+ # This program is free software; you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation; either version 2 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License along
14
+ # with this program; if not, write to the Free Software Foundation, Inc.,
15
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
+
17
+
18
+
19
+ # routes/media_to_mp3.py
20
+ from flask import Blueprint, current_app
21
+ from app_utils import *
22
+ import logging
23
+ from services.v1.media.convert.media_to_mp3 import process_media_to_mp3
24
+ from services.authentication import authenticate
25
+ from services.cloud_storage import upload_file
26
+ import os
27
+
28
+ v1_media_convert_mp3_bp = Blueprint('v1_media_convert_mp3', __name__)
29
+ logger = logging.getLogger(__name__)
30
+
31
+ @v1_media_convert_mp3_bp.route('/v1/media/convert/mp3', methods=['POST'])
32
+ @v1_media_convert_mp3_bp.route('/v1/media/transform/mp3', methods=['POST']) #depleft for backwards compatibility, do not use.
33
+ @authenticate
34
+ @validate_payload({
35
+ "type": "object",
36
+ "properties": {
37
+ "media_url": {"type": "string", "format": "uri"},
38
+ "webhook_url": {"type": "string", "format": "uri"},
39
+ "id": {"type": "string"},
40
+ "bitrate": {"type": "string", "pattern": "^[0-9]+k$"},
41
+ "sample_rate": {"type": "number"}
42
+ },
43
+ "required": ["media_url"],
44
+ "additionalProperties": False
45
+ })
46
+ @queue_task_wrapper(bypass_queue=False)
47
+ def convert_media_to_mp3(job_id, data):
48
+ media_url = data['media_url']
49
+ webhook_url = data.get('webhook_url')
50
+ id = data.get('id')
51
+ bitrate = data.get('bitrate', '128k')
52
+ sample_rate = data.get('sample_rate')
53
+
54
+ logger.info(f"Job {job_id}: Received media-to-mp3 request for media URL: {media_url}")
55
+
56
+ try:
57
+ output_file = process_media_to_mp3(media_url, job_id, bitrate, sample_rate)
58
+ logger.info(f"Job {job_id}: Media conversion process completed successfully")
59
+
60
+ cloud_url = upload_file(output_file)
61
+ logger.info(f"Job {job_id}: Converted media uploaded to cloud storage: {cloud_url}")
62
+
63
+ return cloud_url, "/v1/media/transform/mp3", 200
64
+
65
+ except Exception as e:
66
+ logger.error(f"Job {job_id}: Error during media conversion process - {str(e)}")
67
+ return str(e), "/v1/media/transform/mp3", 500
routes/v1/media/download.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint
2
+ from app_utils import *
3
+ import logging
4
+ import os
5
+ import yt_dlp
6
+ import tempfile
7
+ from werkzeug.utils import secure_filename
8
+ import uuid
9
+ from services.cloud_storage import upload_file
10
+ from services.authentication import authenticate
11
+ from services.file_management import download_file
12
+ from urllib.parse import quote, urlparse
13
+
14
+ v1_media_download_bp = Blueprint('v1_media_download', __name__)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ @v1_media_download_bp.route('/v1/BETA/media/download', methods=['POST'])
18
+ @authenticate
19
+ @validate_payload({
20
+ "type": "object",
21
+ "properties": {
22
+ "media_url": {"type": "string", "format": "uri"},
23
+ "webhook_url": {"type": "string", "format": "uri"},
24
+ "id": {"type": "string"},
25
+ "cookie": {"type": "string", "description": "Path to cookie file, URL to cookie file, or cookie string in Netscape format"},
26
+ "cloud_upload": {"type": "boolean"},
27
+ "format": {
28
+ "type": "object",
29
+ "properties": {
30
+ "quality": {"type": "string"},
31
+ "format_id": {"type": "string"},
32
+ "resolution": {"type": "string"},
33
+ "video_codec": {"type": "string"},
34
+ "audio_codec": {"type": "string"}
35
+ }
36
+ },
37
+ "audio": {
38
+ "type": "object",
39
+ "properties": {
40
+ "extract": {"type": "boolean"},
41
+ "format": {"type": "string"},
42
+ "quality": {"type": "string"}
43
+ }
44
+ },
45
+ "thumbnails": {
46
+ "type": "object",
47
+ "properties": {
48
+ "download": {"type": "boolean"},
49
+ "download_all": {"type": "boolean"},
50
+ "formats": {"type": "array", "items": {"type": "string"}},
51
+ "convert": {"type": "boolean"},
52
+ "embed_in_audio": {"type": "boolean"}
53
+ }
54
+ },
55
+ "subtitles": {
56
+ "type": "object",
57
+ "properties": {
58
+ "download": {"type": "boolean"},
59
+ "languages": {"type": "array", "items": {"type": "string"}},
60
+ "formats": {"type": "array", "items": {"type": "string"}}
61
+ }
62
+ },
63
+ "download": {
64
+ "type": "object",
65
+ "properties": {
66
+ "max_filesize": {"type": "integer"},
67
+ "rate_limit": {"type": "string"},
68
+ "retries": {"type": "integer"}
69
+ }
70
+ }
71
+ },
72
+ "required": ["media_url"],
73
+ "additionalProperties": False
74
+ })
75
+ @queue_task_wrapper(bypass_queue=False)
76
+ def download_media(job_id, data):
77
+ media_url = data['media_url']
78
+ cookie = data.get('cookie')
79
+
80
+ format_options = data.get('format', {})
81
+ audio_options = data.get('audio', {})
82
+ thumbnail_options = data.get('thumbnails', {})
83
+ subtitle_options = data.get('subtitles', {})
84
+ download_options = data.get('download', {})
85
+
86
+ logger.info(f"Job {job_id}: Received download request for {media_url}")
87
+
88
+ try:
89
+ # Create a temporary directory for downloads
90
+ with tempfile.TemporaryDirectory() as temp_dir:
91
+ # Configure yt-dlp options
92
+ ydl_opts = {
93
+ 'format': 'best', # Download best quality
94
+ 'outtmpl': os.path.join(temp_dir, '%(title)s.%(ext)s'),
95
+ 'quiet': True,
96
+ 'no_warnings': True,
97
+ 'download': data.get('cloud_upload', True)
98
+ }
99
+
100
+ # Add cookies if provided
101
+ if cookie:
102
+ if os.path.isfile(cookie):
103
+ ydl_opts['cookiefile'] = cookie
104
+ elif urlparse(cookie).scheme in ('http', 'https'):
105
+ # If cookie is a URL, download it first
106
+ ydl_opts['cookiefile'] = download_file(cookie, temp_dir)
107
+ else:
108
+ # If cookie is a string, write it to a temporary file
109
+ cookie_file = os.path.join(temp_dir, 'cookies.txt')
110
+ with open(cookie_file, 'w') as f:
111
+ f.write(cookie)
112
+ ydl_opts['cookiefile'] = cookie_file
113
+
114
+ # Add format options if specified
115
+ if format_options:
116
+ format_str = []
117
+ if format_options.get('quality'):
118
+ format_str.append(format_options['quality'])
119
+ if format_options.get('format_id'):
120
+ format_str.append(format_options['format_id'])
121
+ if format_options.get('resolution'):
122
+ format_str.append(format_options['resolution'])
123
+ if format_options.get('video_codec'):
124
+ format_str.append(format_options['video_codec'])
125
+ if format_options.get('audio_codec'):
126
+ format_str.append(format_options['audio_codec'])
127
+ if format_str:
128
+ ydl_opts['format'] = '+'.join(format_str)
129
+
130
+ # Add audio options if specified
131
+ if audio_options:
132
+ if audio_options.get('extract'):
133
+ ydl_opts['extract_audio'] = True
134
+ if audio_options.get('format'):
135
+ ydl_opts['audio_format'] = audio_options['format']
136
+ if audio_options.get('quality'):
137
+ ydl_opts['audio_quality'] = audio_options['quality']
138
+
139
+ # Add thumbnail options if specified
140
+ if thumbnail_options:
141
+ ydl_opts['writesubtitles'] = thumbnail_options.get('download', False)
142
+ ydl_opts['writeallsubtitles'] = thumbnail_options.get('download_all', False)
143
+ if thumbnail_options.get('formats'):
144
+ ydl_opts['subtitleslangs'] = thumbnail_options['formats']
145
+ ydl_opts['convert_thumbnails'] = thumbnail_options.get('convert', False)
146
+ ydl_opts['embed_thumbnail_in_audio'] = thumbnail_options.get('embed_in_audio', False)
147
+
148
+ # Add subtitle options if specified
149
+ if subtitle_options:
150
+ ydl_opts['writesubtitles'] = subtitle_options.get('download', False)
151
+ if subtitle_options.get('languages'):
152
+ ydl_opts['subtitleslangs'] = subtitle_options['languages']
153
+ if subtitle_options.get('formats'):
154
+ ydl_opts['subtitlesformat'] = subtitle_options['formats']
155
+
156
+ # Add download options if specified
157
+ if download_options:
158
+ if download_options.get('max_filesize'):
159
+ ydl_opts['max_filesize'] = download_options['max_filesize']
160
+ if download_options.get('rate_limit'):
161
+ ydl_opts['limit_rate'] = download_options['rate_limit']
162
+ if download_options.get('retries'):
163
+ ydl_opts['retries'] = download_options['retries']
164
+
165
+ # Download the media
166
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
167
+ info = ydl.extract_info(media_url, download=data.get('cloud_upload', True))
168
+
169
+ if not data.get('cloud_upload', True):
170
+ media_url = info['url']
171
+ else:
172
+ filename = ydl.prepare_filename(info)
173
+ # Upload to cloud storage
174
+ media_url = upload_file(filename)
175
+ # Clean up the temporary file
176
+ os.remove(filename)
177
+
178
+ # Prepare response
179
+ response = {
180
+ "media": {
181
+ "media_url": media_url,
182
+ "title": info.get('title'),
183
+ "format_id": info.get('format_id'),
184
+ "ext": info.get('ext'),
185
+ "resolution": info.get('resolution'),
186
+ "filesize": info.get('filesize'),
187
+ "width": info.get('width'),
188
+ "height": info.get('height'),
189
+ "fps": info.get('fps'),
190
+ "video_codec": info.get('vcodec'),
191
+ "audio_codec": info.get('acodec'),
192
+ "upload_date": info.get('upload_date'),
193
+ "duration": info.get('duration'),
194
+ "view_count": info.get('view_count'),
195
+ "uploader": info.get('uploader'),
196
+ "uploader_id": info.get('uploader_id'),
197
+ "description": info.get('description')
198
+ }
199
+ }
200
+
201
+ # Add thumbnails if available and requested
202
+ if info.get('thumbnails') and thumbnail_options.get('download', False):
203
+ response["thumbnails"] = []
204
+ for thumbnail in info['thumbnails']:
205
+ if thumbnail.get('url'):
206
+ try:
207
+ # Download the thumbnail first
208
+ thumbnail_path = download_file(thumbnail['url'], temp_dir)
209
+ # Upload to cloud storage
210
+ thumbnail_url = upload_file(thumbnail_path)
211
+ # Clean up the temporary thumbnail file
212
+ os.remove(thumbnail_path)
213
+
214
+ response["thumbnails"].append({
215
+ "id": thumbnail.get('id', 'default'),
216
+ "image_url": thumbnail_url,
217
+ "width": thumbnail.get('width'),
218
+ "height": thumbnail.get('height'),
219
+ "original_format": thumbnail.get('ext'),
220
+ "converted": thumbnail.get('converted', False)
221
+ })
222
+ except Exception as e:
223
+ logger.error(f"Error processing thumbnail: {str(e)}")
224
+ continue
225
+
226
+ return response, "/v1/media/download", 200
227
+
228
+ except Exception as e:
229
+ logger.error(f"Job {job_id}: Error during download process - {str(e)}")
230
+ return str(e), "/v1/media/download", 500