pyschopoodle commited on
Commit
c095744
·
verified ·
1 Parent(s): 60b5f25

final commit

Browse files
Files changed (6) hide show
  1. .env +1 -0
  2. .gitattributes +34 -35
  3. .gitignore +7 -0
  4. README.md +14 -12
  5. app.py +192 -0
  6. requirements.txt +11 -0
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ ASSEMBLYAI_API_KEY="335c7fb7f8544ec58299700d43d8a592"
.gitattributes CHANGED
@@ -1,35 +1,34 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+
2
+ *.7z filter=lfs diff=lfs merge=lfs -text
3
+ *.arrow filter=lfs diff=lfs merge=lfs -text
4
+ *.bin filter=lfs diff=lfs merge=lfs -text
5
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
6
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
7
+ *.ftz filter=lfs diff=lfs merge=lfs -text
8
+ *.gz filter=lfs diff=lfs merge=lfs -text
9
+ *.h5 filter=lfs diff=lfs merge=lfs -text
10
+ *.joblib filter=lfs diff=lfs merge=lfs -text
11
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
12
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
13
+ *.model filter=lfs diff=lfs merge=lfs -text
14
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
15
+ *.npy filter=lfs diff=lfs merge=lfs -text
16
+ *.npz filter=lfs diff=lfs merge=lfs -text
17
+ *.onnx filter=lfs diff=lfs merge=lfs -text
18
+ *.ot filter=lfs diff=lfs merge=lfs -text
19
+ *.parquet filter=lfs diff=lfs merge=lfs -text
20
+ *.pb filter=lfs diff=lfs merge=lfs -text
21
+ *.pickle filter=lfs diff=lfs merge=lfs -text
22
+ *.pkl filter=lfs diff=lfs merge=lfs -text
23
+ *.pt filter=lfs diff=lfs merge=lfs -text
24
+ *.pth filter=lfs diff=lfs merge=lfs -text
25
+ *.rar filter=lfs diff=lfs merge=lfs -text
26
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tflite filter=lfs diff=lfs merge=lfs -text
29
+ *.tgz filter=lfs diff=lfs merge=lfs -text
30
+ *.wasm filter=lfs diff=lfs merge=lfs -text
31
+ *.xz filter=lfs diff=lfs merge=lfs -text
32
+ *.zip filter=lfs diff=lfs merge=lfs -text
33
+ *.zst filter=lfs diff=lfs merge=lfs -text
34
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
 
.gitignore ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ .venv
2
+ myenv
3
+ .env
4
+ extracted_audio.mp3
5
+ subtitle.srt
6
+ test.ipynb
7
+ video.mp4
README.md CHANGED
@@ -1,12 +1,14 @@
1
- ---
2
- title: Vidoes Subtitles Generator
3
- emoji: 🐠
4
- colorFrom: indigo
5
- colorTo: red
6
- sdk: gradio
7
- sdk_version: 5.20.1
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
+ ---
2
+ title: Video Subtitle Generator
3
+ emoji: 🎬
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: gradio
7
+ sdk_version: 4.0.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ A tool to automatically generate and add subtitles to videos in multiple languages.
14
+ Upload your own video or provide a YouTube URL.
app.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import tempfile
3
+ import gradio as gr
4
+ import moviepy.editor as mp
5
+ import subprocess
6
+ import assemblyai as aai
7
+ import yt_dlp
8
+ import shutil
9
+ import time
10
+ from pathlib import Path
11
+ from dotenv import load_dotenv
12
+
13
+ # Load environment variables from .env file
14
+ load_dotenv()
15
+
16
+ # AssemblyAI API settings
17
+ API_KEY = os.getenv("ASSEMBLYAI_API_KEY", "335c7fb7f8544ec58299700d43d8a592")
18
+ aai.settings.api_key = API_KEY
19
+
20
+ # Step 1: Download YouTube video if URL is provided
21
+ def download_youtube(youtube_url, output_path):
22
+ ydl_opts = {
23
+ 'format': 'best[ext=mp4]',
24
+ 'outtmpl': output_path,
25
+ }
26
+
27
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
28
+ ydl.download([youtube_url])
29
+
30
+ return output_path
31
+
32
+ # Step 2: Extract audio from video
33
+ def extract_audio(video_path, audio_path):
34
+ video = mp.VideoFileClip(video_path)
35
+ video.audio.write_audiofile(audio_path, verbose=False, logger=None)
36
+ return audio_path
37
+
38
+ # Step 3: Transcribe the audio with AssemblyAI
39
+ def transcribe_audio(audio_path):
40
+ transcriber = aai.Transcriber()
41
+ transcript = transcriber.transcribe(audio_path)
42
+
43
+ if transcript.status == aai.TranscriptStatus.error:
44
+ return None, f"Transcription Error: {transcript.error}"
45
+
46
+ subtitle_path = os.path.join(tempfile.gettempdir(), "subtitle.srt")
47
+ with open(subtitle_path, 'w', encoding='utf-8') as f:
48
+ f.write(transcript.export_subtitles_srt())
49
+
50
+ return subtitle_path, "Transcription completed successfully"
51
+
52
+ # Step 4: Add subtitles to video with improved path handling
53
+ def add_subtitles(video_path, subtitle_path, output_path):
54
+ # Create a temporary copy of the subtitle file in the same directory as the output
55
+ output_dir = os.path.dirname(output_path)
56
+ temp_subtitle = os.path.join(output_dir, "temp_subtitle.srt")
57
+
58
+ # Copy subtitle file to output directory
59
+ shutil.copy(subtitle_path, temp_subtitle)
60
+
61
+ # Use the file name only for the subtitles filter
62
+ subtitle_filename = os.path.basename(temp_subtitle)
63
+
64
+ # Escape any special characters in paths
65
+ subtitle_filename = subtitle_filename.replace("'", "'\\''")
66
+
67
+ # Build the FFmpeg command with proper path handling
68
+ command = [
69
+ "ffmpeg",
70
+ "-i", video_path,
71
+ "-vf", f"subtitles={subtitle_filename}:force_style='Fontname=Arial,Fontsize=30,PrimaryColour=&HFFFFFF,OutlineColour=&H000000,BorderStyle=3,Outline=1'",
72
+ "-c:a", "copy",
73
+ "-y", # Override output file if it exists
74
+ output_path
75
+ ]
76
+
77
+ # Change working directory to where the subtitle file is
78
+ current_dir = os.getcwd()
79
+ os.chdir(output_dir)
80
+
81
+ try:
82
+ result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
83
+
84
+ if result.returncode == 0:
85
+ return output_path, "Subtitles added successfully!"
86
+ else:
87
+ return None, f"Error adding subtitles: {result.stderr}"
88
+ finally:
89
+ # Change back to original directory and clean up
90
+ os.chdir(current_dir)
91
+ try:
92
+ os.remove(temp_subtitle)
93
+ except:
94
+ pass
95
+
96
+ # Main processing function for Gradio with progress tracking
97
+ def process_video(video_input, youtube_url, progress=gr.Progress()):
98
+ # Create temp directory for processing
99
+ temp_dir = tempfile.mkdtemp()
100
+
101
+ # Start progress tracking (0-100%)
102
+ progress(0, desc="Starting process...")
103
+
104
+ # Input handling
105
+ if youtube_url:
106
+ input_video = os.path.join(temp_dir, "youtube_video.mp4")
107
+ status_msg = f"Downloading YouTube video: {youtube_url}"
108
+ progress(5, desc="Downloading YouTube video...")
109
+ yield None, status_msg
110
+ try:
111
+ download_youtube(youtube_url, input_video)
112
+ except Exception as e:
113
+ yield None, f"Error downloading YouTube video: {str(e)}"
114
+ return
115
+ else:
116
+ input_video = video_input
117
+ if not input_video:
118
+ yield None, "Please provide either a video file or a YouTube URL"
119
+ return
120
+
121
+ # Extract audio
122
+ audio_path = os.path.join(temp_dir, "extracted_audio.mp3")
123
+ status_msg = "Extracting audio from video..."
124
+ progress(20, desc="Extracting audio...")
125
+ yield None, status_msg
126
+ try:
127
+ extract_audio(input_video, audio_path)
128
+ except Exception as e:
129
+ yield None, f"Error extracting audio: {str(e)}"
130
+ return
131
+
132
+ # Transcribe audio
133
+ status_msg = "Transcribing audio..."
134
+ progress(40, desc="Transcribing audio...")
135
+ yield None, status_msg
136
+ try:
137
+ subtitle_path, msg = transcribe_audio(audio_path)
138
+ if not subtitle_path:
139
+ yield None, msg
140
+ return
141
+ except Exception as e:
142
+ yield None, f"Error during transcription: {str(e)}"
143
+ return
144
+
145
+ # Add subtitles to video
146
+ output_path = os.path.join(temp_dir, "output_with_subtitles.mp4")
147
+ status_msg = "Adding subtitles to video..."
148
+ progress(70, desc="Adding subtitles...")
149
+ yield None, status_msg
150
+ try:
151
+ result_path, msg = add_subtitles(input_video, subtitle_path, output_path)
152
+ if not result_path:
153
+ yield None, msg
154
+ return
155
+
156
+ progress(95, desc="Finalizing...")
157
+ time.sleep(1) # Small delay to ensure UI updates
158
+ progress(100, desc="Complete!")
159
+ yield result_path, "Process completed successfully! Your video with subtitles is ready."
160
+ except Exception as e:
161
+ yield None, f"Error adding subtitles: {str(e)}"
162
+
163
+ # Create Gradio Interface
164
+ with gr.Blocks(title="Video Subtitle Generator") as app:
165
+ gr.Markdown(
166
+ """
167
+ # 🎬 Video Subtitle Generator
168
+
169
+ Upload a video file or provide a YouTube URL to generate subtitles.
170
+ """
171
+ )
172
+
173
+ with gr.Row():
174
+ with gr.Column(scale=1):
175
+ video_input = gr.Video(label="Upload Video")
176
+ youtube_url = gr.Textbox(label="Or Enter YouTube URL")
177
+ submit_btn = gr.Button("Generate Subtitles", variant="primary")
178
+
179
+ with gr.Column(scale=1):
180
+ output_video = gr.Video(label="Video with Subtitles")
181
+ status = gr.Textbox(label="Status", value="Ready to process")
182
+
183
+ submit_btn.click(
184
+ fn=process_video,
185
+ inputs=[video_input, youtube_url],
186
+ outputs=[output_video, status],
187
+ show_progress=True
188
+ )
189
+
190
+ # Launch app
191
+ if __name__ == "__main__":
192
+ app.launch(debug=True)
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # gradio
2
+ # assemblyai
3
+ # python-dotenv
4
+ # ffmpeg-python
5
+ # moviepy==1.0.3
6
+ gradio>=4.0.0
7
+ moviepy>=1.0.3
8
+ assemblyai>=0.20.0
9
+ yt-dlp>=2023.11.14
10
+ python-dotenv>=1.0.0
11
+ load_dotenv