├── LICENSE
├── README.md
└── Hardcode_subtitles_on_video.ipynb
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Anil Chandra Naidu Matcha
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Free Media Tools
2 | A list of free media tools for use without login and running completely in browser
3 |
4 | ### Open-source code youtube tutorial -> https://youtu.be/VbDLNjoKdt4
5 |
6 | ## Free Video Tools
7 |
8 | Here are some of the video tools available:
9 |
10 | - [Compress Video](https://tools.vadoo.tv/compress-video)
11 | - [Cut Video](https://tools.vadoo.tv/cut-video)
12 | - [Add subtitle to video](https://tools.vadoo.tv/add-subtitle-to-video) -> [Code](https://github.com/Anil-matcha/Free-Video-Tools/blob/main/Hardcode_subtitles_on_video.ipynb)
13 | - [Add audio to video](https://tools.vadoo.tv/add-audio-to-video)
14 | - [Add image to video](https://tools.vadoo.tv/add-image-to-video)
15 | - [Change Video Speed](https://tools.vadoo.tv/change-video-speed)
16 | - [Resize Video](https://tools.vadoo.tv/resize-video)
17 | - [Join multiple video](https://tools.vadoo.tv/merge-video)
18 | - [MP4 converter](https://tools.vadoo.tv/mp4-converter)
19 | - [AVI converter](https://tools.vadoo.tv/avi-converter)
20 | - [MKV converter](https://tools.vadoo.tv/mkv-converter)
21 | - [MOV converter](https://tools.vadoo.tv/mov-converter)
22 | - [WEBM converter](https://tools.vadoo.tv/webm-converter)
23 | - [WMV converter](https://tools.vadoo.tv/wmv-converter)
24 |
25 | ## Free Audio Tools
26 |
27 | Here are some of the audio tools available:
28 |
29 | - [Join multiple audio](https://tools.vadoo.tv/merge-audio)
30 | - [Change audio speed](https://tools.vadoo.tv/change-audio-speed)
31 | - [Compress audio](https://tools.vadoo.tv/compress-audio)
32 | - [Cut audio](https://tools.vadoo.tv/cut-audio)
33 | - [Merge audio](https://tools.vadoo.tv/merge-audio)
34 | - [Add image to audio](https://tools.vadoo.tv/add-image-to-audio)
35 | - [Convert to MP3](https://tools.vadoo.tv/audio-converter/mp3-converter)
36 | - [Convert to AIFF](https://tools.vadoo.tv/audio-converter/aiff-converter)
37 | - [Convert to ALAC](https://tools.vadoo.tv/audio-converter/alac-converter)
38 | - [Convert to AMR](https://tools.vadoo.tv/audio-converter/amr-converter)
39 | - [Convert to FLAC](https://tools.vadoo.tv/audio-converter/flac-converter)
40 | - [Convert to M4A](https://tools.vadoo.tv/audio-converter/m4a-converter)
41 | - [Convert to M4R](https://tools.vadoo.tv/audio-converter/m4r-converter)
42 | - [Convert to OGG](https://tools.vadoo.tv/audio-converter/ogg-converter)
43 | - [Convert to OPUS](https://tools.vadoo.tv/audio-converter/opus-converter)
44 | - [Convert to WAV](https://tools.vadoo.tv/audio-converter/wav-converter)
45 | - [Convert to WMA](https://tools.vadoo.tv/audio-converter/wma-converter)
46 |
47 |
48 | ## Contributing
49 |
50 | We welcome contributions! Please open an issue or submit a pull request if you have any improvements or new features to add.
51 |
52 | ## License
53 |
54 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for more details.
55 |
--------------------------------------------------------------------------------
/Hardcode_subtitles_on_video.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "colab": {
6 | "provenance": [],
7 | "authorship_tag": "ABX9TyMXhFPGEH2vgmn5vdAmLI2L",
8 | "include_colab_link": true
9 | },
10 | "kernelspec": {
11 | "name": "python3",
12 | "display_name": "Python 3"
13 | },
14 | "language_info": {
15 | "name": "python"
16 | }
17 | },
18 | "cells": [
19 | {
20 | "cell_type": "markdown",
21 | "metadata": {
22 | "id": "view-in-github",
23 | "colab_type": "text"
24 | },
25 | "source": [
26 | "
"
27 | ]
28 | },
29 | {
30 | "cell_type": "markdown",
31 | "source": [
32 | "Step 1: Install Required Libraries"
33 | ],
34 | "metadata": {
35 | "id": "JkNVzDFHZ6kh"
36 | }
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": 1,
41 | "metadata": {
42 | "colab": {
43 | "base_uri": "https://localhost:8080/"
44 | },
45 | "id": "WJdBemqCP8Mo",
46 | "outputId": "a19c0c5e-d4a5-4c72-ae28-cca888adc9f7"
47 | },
48 | "outputs": [
49 | {
50 | "output_type": "stream",
51 | "name": "stdout",
52 | "text": [
53 | "Requirement already satisfied: moviepy in /usr/local/lib/python3.10/dist-packages (1.0.3)\n",
54 | "Collecting pysrt\n",
55 | " Downloading pysrt-1.1.2.tar.gz (104 kB)\n",
56 | "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m104.4/104.4 kB\u001b[0m \u001b[31m2.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
57 | "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
58 | "Requirement already satisfied: decorator<5.0,>=4.0.2 in /usr/local/lib/python3.10/dist-packages (from moviepy) (4.4.2)\n",
59 | "Requirement already satisfied: tqdm<5.0,>=4.11.2 in /usr/local/lib/python3.10/dist-packages (from moviepy) (4.66.4)\n",
60 | "Requirement already satisfied: requests<3.0,>=2.8.1 in /usr/local/lib/python3.10/dist-packages (from moviepy) (2.31.0)\n",
61 | "Requirement already satisfied: proglog<=1.0.0 in /usr/local/lib/python3.10/dist-packages (from moviepy) (0.1.10)\n",
62 | "Requirement already satisfied: numpy>=1.17.3 in /usr/local/lib/python3.10/dist-packages (from moviepy) (1.25.2)\n",
63 | "Requirement already satisfied: imageio<3.0,>=2.5 in /usr/local/lib/python3.10/dist-packages (from moviepy) (2.31.6)\n",
64 | "Requirement already satisfied: imageio-ffmpeg>=0.2.0 in /usr/local/lib/python3.10/dist-packages (from moviepy) (0.5.1)\n",
65 | "Requirement already satisfied: chardet in /usr/local/lib/python3.10/dist-packages (from pysrt) (5.2.0)\n",
66 | "Requirement already satisfied: pillow<10.1.0,>=8.3.2 in /usr/local/lib/python3.10/dist-packages (from imageio<3.0,>=2.5->moviepy) (9.4.0)\n",
67 | "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from imageio-ffmpeg>=0.2.0->moviepy) (67.7.2)\n",
68 | "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests<3.0,>=2.8.1->moviepy) (3.3.2)\n",
69 | "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests<3.0,>=2.8.1->moviepy) (3.7)\n",
70 | "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests<3.0,>=2.8.1->moviepy) (2.0.7)\n",
71 | "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests<3.0,>=2.8.1->moviepy) (2024.6.2)\n",
72 | "Building wheels for collected packages: pysrt\n",
73 | " Building wheel for pysrt (setup.py) ... \u001b[?25l\u001b[?25hdone\n",
74 | " Created wheel for pysrt: filename=pysrt-1.1.2-py3-none-any.whl size=13443 sha256=f195fb1c3e52a37f2fab3195aa9c3cd0b7df8ab71b4bb9dc16ca401cddb882ba\n",
75 | " Stored in directory: /root/.cache/pip/wheels/30/7f/e8/55de9a9b07302d9e7fe47c27910e3bea0c48536153e74bd7e6\n",
76 | "Successfully built pysrt\n",
77 | "Installing collected packages: pysrt\n",
78 | "Successfully installed pysrt-1.1.2\n"
79 | ]
80 | }
81 | ],
82 | "source": [
83 | "!pip install moviepy pysrt"
84 | ]
85 | },
86 | {
87 | "cell_type": "markdown",
88 | "source": [
89 | "Setup ImageMagick"
90 | ],
91 | "metadata": {
92 | "id": "wqbSGHmWaBaX"
93 | }
94 | },
95 | {
96 | "cell_type": "code",
97 | "source": [
98 | "!apt install imagemagick &> /dev/null\n",
99 | "!sed -i '///' /etc/ImageMagick-6/policy.xml\n",
101 | "!sudo sed -i 's///' /etc/ImageMagick-6/policy.xml"
102 | ],
103 | "metadata": {
104 | "id": "tUOo_gMcQ-xS"
105 | },
106 | "execution_count": 2,
107 | "outputs": []
108 | },
109 | {
110 | "cell_type": "markdown",
111 | "source": [
112 | "Step 2: Import Libraries\n",
113 | "\n",
114 | "Let's import the libraries we need.\n",
115 | "\n"
116 | ],
117 | "metadata": {
118 | "id": "tnJ1vALaaGMh"
119 | }
120 | },
121 | {
122 | "cell_type": "code",
123 | "source": [
124 | "import moviepy.editor as mp\n",
125 | "import pysrt\n",
126 | "from IPython.display import HTML"
127 | ],
128 | "metadata": {
129 | "id": "M8wWTR6GaJb1"
130 | },
131 | "execution_count": 14,
132 | "outputs": []
133 | },
134 | {
135 | "cell_type": "markdown",
136 | "source": [
137 | "Step 3: Define Functions for Adding Subtitles\n",
138 | "\n",
139 | "3.1 Function: srt_to_moviepy_subtitles\n",
140 | "\n",
141 | "This function converts an SRT subtitle file into moviepy text clips and overlays them onto the video."
142 | ],
143 | "metadata": {
144 | "id": "b2oTW_d0aPxj"
145 | }
146 | },
147 | {
148 | "cell_type": "code",
149 | "source": [
150 | "def srt_to_moviepy_subtitles(srt_file, video_clip):\n",
151 | " subs = pysrt.open(srt_file)\n",
152 | " subtitle_clips = []\n",
153 | "\n",
154 | " for sub in subs:\n",
155 | " start_time = sub.start.to_time()\n",
156 | " end_time = sub.end.to_time()\n",
157 | " start_seconds = start_time.hour * 3600 + start_time.minute * 60 + start_time.second + start_time.microsecond / 1e6\n",
158 | " end_seconds = end_time.hour * 3600 + end_time.minute * 60 + end_time.second + end_time.microsecond / 1e6\n",
159 | " duration = end_seconds - start_seconds\n",
160 | "\n",
161 | " # Formatting text to handle newlines properly\n",
162 | " formatted_text = sub.text.replace('\\n', ' ')\n",
163 | "\n",
164 | " # Create a text clip with a black background and white text\n",
165 | " text_clip = (mp.TextClip(formatted_text, fontsize=24, color='yellow', bg_color='black', method='caption', size=(video_clip.w - 20, None))\n",
166 | " .set_start(start_seconds)\n",
167 | " .set_duration(duration)\n",
168 | " .set_position(('center', video_clip.h - 50))) # Position near the bottom with padding\n",
169 | "\n",
170 | " # Add the text clip to the list of subtitle clips\n",
171 | " subtitle_clips.append(text_clip)\n",
172 | "\n",
173 | " return mp.CompositeVideoClip([video_clip] + subtitle_clips)"
174 | ],
175 | "metadata": {
176 | "id": "q3HQUtNfaTW9"
177 | },
178 | "execution_count": 15,
179 | "outputs": []
180 | },
181 | {
182 | "cell_type": "markdown",
183 | "source": [
184 | "Explanation:\n",
185 | "\n",
186 | "pysrt.open(srt_file): Opens the subtitle file and parses it.\n",
187 | "\n",
188 | "start_time and end_time: Convert subtitle start and end times to Python datetime objects.\n",
189 | "\n",
190 | "start_seconds and end_seconds: Calculate subtitle start and end times in seconds.\n",
191 | "\n",
192 | "formatted_text: Replace newlines in the subtitle text with spaces.\n",
193 | "\n",
194 | "mp.TextClip: Creates a video clip containing text.\n",
195 | "\n",
196 | "Parameters:\n",
197 | "- fontsize: Font size of the text.\n",
198 | "- color: Color of the text.\n",
199 | "- bg_color: Background color of the text clip.\n",
200 | "- method='caption': Text rendering method.\n",
201 | "- size: Size of the text clip. Here it is adjusted to fit within the video frame width.\n",
202 | "- set_start: Sets the start time of the subtitle clip.\n",
203 | "- set_duration: Sets how long the subtitle will be displayed.\n",
204 | "- set_position: Sets the position of the text clip on the video frame.\n",
205 | "\n",
206 | "mp.CompositeVideoClip: Combines the original video clip with the subtitle clips."
207 | ],
208 | "metadata": {
209 | "id": "B6jo_odpaYx7"
210 | }
211 | },
212 | {
213 | "cell_type": "markdown",
214 | "source": [
215 | "3.2 Function: burn_subtitles\n",
216 | "\n",
217 | "This function applies the subtitles to a video file and saves the output."
218 | ],
219 | "metadata": {
220 | "id": "sBQ9JylaapHJ"
221 | }
222 | },
223 | {
224 | "cell_type": "code",
225 | "source": [
226 | "def burn_subtitles(video_file, srt_file, output_file):\n",
227 | " video_clip = mp.VideoFileClip(video_file)\n",
228 | " video_with_subs = srt_to_moviepy_subtitles(srt_file, video_clip)\n",
229 | " video_with_subs.write_videofile(output_file, codec='libx264', audio_codec='aac')"
230 | ],
231 | "metadata": {
232 | "id": "X77xxN2YarhY"
233 | },
234 | "execution_count": 16,
235 | "outputs": []
236 | },
237 | {
238 | "cell_type": "markdown",
239 | "source": [
240 | "Explanation:\n",
241 | "\n",
242 | "- mp.VideoFileClip(video_file): Loads the video file into a moviepy video clip.\n",
243 | "- srt_to_moviepy_subtitles: Calls the previously defined function to add subtitles to the video.\n",
244 | "- write_videofile: Writes the final video with subtitles to a file.\n",
245 | "\n",
246 | "Parameters:\n",
247 | " - codec='libx264': Specifies the video codec.\n",
248 | " - audio_codec='aac': Specifies the audio codec."
249 | ],
250 | "metadata": {
251 | "id": "JVPf73csazPJ"
252 | }
253 | },
254 | {
255 | "cell_type": "markdown",
256 | "source": [
257 | "### Upload input files"
258 | ],
259 | "metadata": {
260 | "id": "RXNx3sLQbIIN"
261 | }
262 | },
263 | {
264 | "cell_type": "code",
265 | "source": [
266 | "from google.colab import files\n",
267 | "uploaded_files = files.upload()\n",
268 | "\n",
269 | "# Save uploaded files to disk\n",
270 | "video_file_path = None\n",
271 | "srt_file_path = None\n",
272 | "\n",
273 | "for file_name in uploaded_files.keys():\n",
274 | " if file_name.endswith('.mp4'):\n",
275 | " video_file_path = file_name\n",
276 | " with open(file_name, 'wb') as f:\n",
277 | " f.write(uploaded_files[file_name])\n",
278 | " elif file_name.endswith('.srt'):\n",
279 | " srt_file_path = file_name\n",
280 | " with open(file_name, 'wb') as f:\n",
281 | " f.write(uploaded_files[file_name])\n",
282 | "\n",
283 | "if video_file_path and srt_file_path:\n",
284 | " print(f\"Uploaded video file: {video_file_path}\")\n",
285 | " print(f\"Uploaded subtitle file: {srt_file_path}\")\n",
286 | "else:\n",
287 | " raise ValueError(\"Please upload both a .mp4 video file and a .srt subtitle file.\")"
288 | ],
289 | "metadata": {
290 | "colab": {
291 | "base_uri": "https://localhost:8080/",
292 | "height": 145
293 | },
294 | "id": "JLiy6QPnQCUW",
295 | "outputId": "ec8b6c50-2ae0-4dd7-9027-29a65bd55069"
296 | },
297 | "execution_count": 23,
298 | "outputs": [
299 | {
300 | "output_type": "display_data",
301 | "data": {
302 | "text/plain": [
303 | ""
304 | ],
305 | "text/html": [
306 | "\n",
307 | " \n",
309 | " \n",
313 | " "
489 | ]
490 | },
491 | "metadata": {}
492 | },
493 | {
494 | "output_type": "stream",
495 | "name": "stdout",
496 | "text": [
497 | "Saving input.mp4 to input.mp4\n",
498 | "Saving input.srt to input.srt\n",
499 | "Uploaded video file: input.mp4\n",
500 | "Uploaded subtitle file: input.srt\n"
501 | ]
502 | }
503 | ]
504 | },
505 | {
506 | "cell_type": "markdown",
507 | "source": [
508 | "### Generate and download output"
509 | ],
510 | "metadata": {
511 | "id": "357uE8PncCPx"
512 | }
513 | },
514 | {
515 | "cell_type": "code",
516 | "source": [
517 | "output_file_name = 'output.mp4'\n",
518 | "burn_subtitles(video_file_path, srt_file_path, output_file_name)\n",
519 | "\n",
520 | "# Provide a link to download the output file\n",
521 | "files.download(output_file_name)"
522 | ],
523 | "metadata": {
524 | "colab": {
525 | "base_uri": "https://localhost:8080/",
526 | "height": 216
527 | },
528 | "id": "XXIa9vOVZOUo",
529 | "outputId": "b983a561-ef37-466d-dae8-0d1a138488bf"
530 | },
531 | "execution_count": 24,
532 | "outputs": [
533 | {
534 | "output_type": "stream",
535 | "name": "stdout",
536 | "text": [
537 | "Moviepy - Building video output.mp4.\n",
538 | "MoviePy - Writing audio in outputTEMP_MPY_wvf_snd.mp4\n"
539 | ]
540 | },
541 | {
542 | "output_type": "stream",
543 | "name": "stderr",
544 | "text": []
545 | },
546 | {
547 | "output_type": "stream",
548 | "name": "stdout",
549 | "text": [
550 | "MoviePy - Done.\n",
551 | "Moviepy - Writing video output.mp4\n",
552 | "\n"
553 | ]
554 | },
555 | {
556 | "output_type": "stream",
557 | "name": "stderr",
558 | "text": [
559 | "t: 99%|█████████▉| 866/873 [00:16<00:00, 70.20it/s, now=None]WARNING:py.warnings:/usr/local/lib/python3.10/dist-packages/moviepy/video/io/ffmpeg_reader.py:123: UserWarning: Warning: in file input.mp4, 641280 bytes wanted but 0 bytes read,at frame 872/873, at time 34.88/34.90 sec. Using the last valid frame instead.\n",
560 | " warnings.warn(\"Warning: in file %s, \"%(self.filename)+\n",
561 | "\n"
562 | ]
563 | },
564 | {
565 | "output_type": "stream",
566 | "name": "stdout",
567 | "text": [
568 | "Moviepy - Done !\n",
569 | "Moviepy - video ready output.mp4\n"
570 | ]
571 | },
572 | {
573 | "output_type": "display_data",
574 | "data": {
575 | "text/plain": [
576 | ""
577 | ],
578 | "application/javascript": [
579 | "\n",
580 | " async function download(id, filename, size) {\n",
581 | " if (!google.colab.kernel.accessAllowed) {\n",
582 | " return;\n",
583 | " }\n",
584 | " const div = document.createElement('div');\n",
585 | " const label = document.createElement('label');\n",
586 | " label.textContent = `Downloading \"${filename}\": `;\n",
587 | " div.appendChild(label);\n",
588 | " const progress = document.createElement('progress');\n",
589 | " progress.max = size;\n",
590 | " div.appendChild(progress);\n",
591 | " document.body.appendChild(div);\n",
592 | "\n",
593 | " const buffers = [];\n",
594 | " let downloaded = 0;\n",
595 | "\n",
596 | " const channel = await google.colab.kernel.comms.open(id);\n",
597 | " // Send a message to notify the kernel that we're ready.\n",
598 | " channel.send({})\n",
599 | "\n",
600 | " for await (const message of channel.messages) {\n",
601 | " // Send a message to notify the kernel that we're ready.\n",
602 | " channel.send({})\n",
603 | " if (message.buffers) {\n",
604 | " for (const buffer of message.buffers) {\n",
605 | " buffers.push(buffer);\n",
606 | " downloaded += buffer.byteLength;\n",
607 | " progress.value = downloaded;\n",
608 | " }\n",
609 | " }\n",
610 | " }\n",
611 | " const blob = new Blob(buffers, {type: 'application/binary'});\n",
612 | " const a = document.createElement('a');\n",
613 | " a.href = window.URL.createObjectURL(blob);\n",
614 | " a.download = filename;\n",
615 | " div.appendChild(a);\n",
616 | " a.click();\n",
617 | " div.remove();\n",
618 | " }\n",
619 | " "
620 | ]
621 | },
622 | "metadata": {}
623 | },
624 | {
625 | "output_type": "display_data",
626 | "data": {
627 | "text/plain": [
628 | ""
629 | ],
630 | "application/javascript": [
631 | "download(\"download_82d10352-fbb7-4d30-a5c3-11fcc93142cd\", \"output.mp4\", 1866895)"
632 | ]
633 | },
634 | "metadata": {}
635 | }
636 | ]
637 | }
638 | ]
639 | }
--------------------------------------------------------------------------------