ujwal55 commited on
Commit
993028b
·
1 Parent(s): f75f8b3

Pushing the code to space

Browse files
Files changed (4) hide show
  1. README.md +59 -0
  2. app.py +331 -0
  3. cookies/cookies.txt +10 -0
  4. requirements.txt +4 -0
README.md CHANGED
@@ -11,3 +11,62 @@ short_description: A tool for creating educational physics videos
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  ---
12
 
13
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
14
+
15
+
16
+ # Physics Video Generator
17
+
18
+ A tool for creating educational physics videos by combining topic slides with relevant educational content.
19
+
20
+ ## Features
21
+
22
+ - **Topic Processing**: Handles various input formats including numbered lists and headers
23
+ - **Educational Slides**: Creates clean title slides for each physics topic
24
+ - **Content Integration**: Finds and incorporates relevant educational videos
25
+ - **Video Compilation**: Produces a single comprehensive video file
26
+ - **Web Interface**: Easy-to-use browser-based interface
27
+
28
+ ## Usage
29
+
30
+ 1. Enter your physics topics in the text area
31
+ 2. Click "Create Physics Video"
32
+ 3. Wait for processing to complete
33
+ 4. Download your educational video
34
+
35
+ ## Supported Input Formats
36
+
37
+ Numbered lists:
38
+ ```
39
+ 1. Newton's Laws of Motion
40
+ 2. Force and Acceleration
41
+ 3. Energy Conservation
42
+ ```
43
+
44
+ Headers:
45
+ ```
46
+ # Kinematics
47
+ # Dynamics
48
+ # Thermodynamics
49
+ ```
50
+
51
+ Plain text:
52
+ ```
53
+ Wave Motion
54
+ Sound Waves
55
+ Electromagnetic Waves
56
+ ```
57
+
58
+ ## Technical Specifications
59
+
60
+ - Built with modern web technologies
61
+ - Video processing handled server-side
62
+ - Supports multiple physics topics per video
63
+ - Educational content sourcing from public videos
64
+ - Optimized for web delivery
65
+
66
+ ## Limitations
67
+
68
+ - Processing time varies based on content availability
69
+ - Limited to publicly available educational resources
70
+ - Designed specifically for physics education
71
+ - Maximum of 8 topics per video session
72
+
app.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Physics Chapter Video Generator
3
+ Creates educational videos by combining title cards with relevant content.
4
+ """
5
+
6
+ import re, shutil, subprocess, textwrap, os, tempfile
7
+ from pathlib import Path
8
+ from typing import List, Optional
9
+ import gradio as gr
10
+ import random
11
+
12
+ print("CWD:", os.getcwd())
13
+ print("cookies.txt exists:", os.path.exists("./cookies/cookies.txt"))
14
+
15
+
16
+ # --- Oxylabs Proxy Configuration ---
17
+ PROXY_USERNAME = "ujwal_CmiMZ"
18
+ PROXY_PASSWORD = "xJv4DChht5P6y+u"
19
+ PROXY_COUNTRY = "US"
20
+
21
+ # List of proxy endpoints (Oxylabs DC Proxies)
22
+ PROXY_PORTS = [8001, 8002, 8003, 8004, 8005]
23
+ PROXY_HOST = "dc.oxylabs.io"
24
+
25
+ def get_random_proxy():
26
+ port = random.choice(PROXY_PORTS)
27
+ return f"http://user-{PROXY_USERNAME}-country-{PROXY_COUNTRY}:{PROXY_PASSWORD}@{PROXY_HOST}:{port}"
28
+
29
+
30
+ # ---------------- CONFIG ----------------
31
+ TITLE_DUR = 3 # seconds
32
+ SIZE = "1280x720" # resolution for cards
33
+ FPS = 30
34
+ CRF = 28 # Higher CRF for smaller files in HF Spaces
35
+ PRESET = "ultrafast" # Fastest encoding for HF Spaces
36
+ YT_MAX_RESULTS = 2 # Reduced for faster processing
37
+ MAX_VIDEO_LENGTH = 30 # Max seconds per video clip
38
+ MAX_TOPICS = 8 # Limit topics for HF Spaces resources
39
+ # ----------------------------------------
40
+
41
+ # ---------- helpers ----------
42
+ def run_cmd(cmd: list[str], timeout: int = 120) -> bool:
43
+ """Run command with timeout and proper error handling"""
44
+ try:
45
+ result = subprocess.run(
46
+ cmd,
47
+ check=True,
48
+ timeout=timeout,
49
+ capture_output=True,
50
+ text=True
51
+ )
52
+ return True
53
+ except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
54
+ print(f"Command failed:\n{' '.join(cmd)}\nError:\n{e.stderr if hasattr(e, 'stderr') else str(e)}")
55
+ return False
56
+
57
+ def yt_urls(query: str, max_results: int) -> List[str]:
58
+ """Get YouTube URLs for search query via proxy"""
59
+ try:
60
+ import requests
61
+ from youtube_search import YoutubeSearch
62
+
63
+ proxy_url = get_random_proxy()
64
+ proxies = {
65
+ "http": proxy_url,
66
+ "https": proxy_url,
67
+ }
68
+
69
+ # Monkey-patch YoutubeSearch to use proxy
70
+ original_get = requests.get
71
+
72
+ def proxied_get(*args, **kwargs):
73
+ kwargs["proxies"] = proxies
74
+ kwargs["verify"] = False
75
+ return original_get(*args, **kwargs)
76
+
77
+ requests.get = proxied_get
78
+ results = YoutubeSearch(query, max_results=max_results).to_dict()
79
+ requests.get = original_get # Restore
80
+
81
+ return ["https://www.youtube.com" + r["url_suffix"] for r in results]
82
+ except Exception as e:
83
+ print(f"YouTube search failed: {e}")
84
+ return []
85
+
86
+
87
+ def safe_filename(name: str) -> str:
88
+ """Create safe filename"""
89
+ return re.sub(r"[^\w\-\.]", "_", name)[:50] # Limit length
90
+
91
+
92
+ def dl_video(url: str, out: Path) -> bool:
93
+ """Download video with length limit using rotating proxy"""
94
+ out.parent.mkdir(exist_ok=True)
95
+ proxy = get_random_proxy()
96
+
97
+ cmd = [
98
+ "yt-dlp",
99
+ "--match-filter", f"duration<{MAX_VIDEO_LENGTH}",
100
+ "-f", "mp4",
101
+ "--merge-output-format", "mp4",
102
+ "-o", str(out),
103
+ "--no-playlist",
104
+ # "--no-check-certificate",
105
+ "--proxy", proxy,
106
+ "--cookies", "./cookies/cookies.txt",
107
+ url,
108
+ ]
109
+ return run_cmd(cmd, timeout=60)
110
+
111
+
112
+ def make_card(text: str, out: Path, dur: int = TITLE_DUR) -> bool:
113
+ """Create title card with text"""
114
+ # Wrap text for better display
115
+ wrapped = textwrap.wrap(text, width=25)
116
+ safe_text = "\\n".join(w.replace("'", r"\\'") for w in wrapped)
117
+
118
+
119
+ cmd = [
120
+ "ffmpeg",
121
+ "-loglevel", "error",
122
+ "-f", "lavfi", "-i", f"color=c=navy:s={SIZE}:d={dur}",
123
+ "-f", "lavfi", "-i", "anullsrc=r=44100:cl=stereo",
124
+ "-vf", (
125
+ f"drawtext=text='{safe_text}':fontcolor=white:fontsize=60:"
126
+ "x=(w-text_w)/2:y=(h-text_h)/2:fontfile=/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
127
+ ),
128
+ "-shortest", "-r", str(FPS),
129
+ "-c:v", "libx264", "-preset", PRESET, "-crf", str(CRF),
130
+ "-c:a", "aac", "-b:a", "96k",
131
+ "-movflags", "+faststart",
132
+ "-y", str(out),
133
+ ]
134
+ return run_cmd(cmd)
135
+
136
+ def extract_topics(text: str) -> List[str]:
137
+ """Extract topics from input text"""
138
+ topics = []
139
+ for line in text.splitlines():
140
+ line = line.strip()
141
+ if not line or len(topics) >= MAX_TOPICS:
142
+ continue
143
+
144
+ # Match numbered lists
145
+ if re.match(r"^\d+[\.)]\s+.+", line):
146
+ topic = re.sub(r"^\d+[\.)]\s*", "", line)
147
+ topics.append(topic)
148
+ # Match markdown headers
149
+ elif re.match(r"^#+\s+.+", line):
150
+ topic = re.sub(r"^#+\s*", "", line)
151
+ topics.append(topic)
152
+ # Match all caps titles
153
+ elif line.isupper() and 3 <= len(line) <= 50:
154
+ topics.append(line.title())
155
+ # Match regular lines as topics
156
+ elif len(line) > 3 and not line.startswith(('http', 'www')):
157
+ topics.append(line)
158
+
159
+ return topics[:MAX_TOPICS]
160
+
161
+ def create_physics_video(chapter_text: str, progress=gr.Progress()) -> Optional[str]:
162
+ """Generate educational physics video from chapter topics"""
163
+ if not chapter_text.strip():
164
+ return None
165
+
166
+ progress(0, desc="Extracting topics...")
167
+ topics = extract_topics(chapter_text)
168
+
169
+ if not topics:
170
+ return None
171
+
172
+ # Create temporary directory
173
+ with tempfile.TemporaryDirectory() as temp_dir:
174
+ temp_path = Path(temp_dir)
175
+ concat_paths: List[Path] = []
176
+
177
+ total_steps = len(topics) * 2 + 3 # topics * (card + video) + opening + closing + concat
178
+ current_step = 0
179
+
180
+ # Opening card
181
+ progress(current_step/total_steps, desc="Creating opening card...")
182
+ opening = temp_path / "00_opening.mp4"
183
+ if make_card("Physics Chapter Overview", opening):
184
+ concat_paths.append(opening)
185
+ current_step += 1
186
+
187
+ # Process each topic
188
+ for idx, topic in enumerate(topics, 1):
189
+ # Create title card
190
+ progress(current_step/total_steps, desc=f"Creating card for: {topic[:30]}...")
191
+ card = temp_path / f"title_{idx:02d}.mp4"
192
+ if make_card(topic, card):
193
+ concat_paths.append(card)
194
+ current_step += 1
195
+
196
+ # Try to download video
197
+ progress(current_step/total_steps, desc=f"Searching video for: {topic[:30]}...")
198
+ video_found = False
199
+
200
+ for url in yt_urls(f"{topic} physics explanation", YT_MAX_RESULTS):
201
+ vid_id_match = re.search(r"(?:v=|be/|shorts/)([\w\-]{11})", url)
202
+ if not vid_id_match:
203
+ continue
204
+
205
+ vid_path = temp_path / f"{safe_filename(vid_id_match.group(1))}.mp4"
206
+ if dl_video(url, vid_path):
207
+ concat_paths.append(vid_path)
208
+ video_found = True
209
+ break
210
+
211
+ if not video_found:
212
+ # Create a placeholder card if no video found
213
+ placeholder = temp_path / f"placeholder_{idx:02d}.mp4"
214
+ if make_card(f"Exploring: {topic}", placeholder, dur=5):
215
+ concat_paths.append(placeholder)
216
+
217
+ current_step += 1
218
+
219
+ # Closing card
220
+ progress(current_step/total_steps, desc="Creating closing card...")
221
+ closing = temp_path / "zz_closing.mp4"
222
+ if make_card("Thank you for learning!", closing):
223
+ concat_paths.append(closing)
224
+ current_step += 1
225
+
226
+ if len(concat_paths) < 2:
227
+ return None
228
+
229
+ # Create concat file
230
+ list_file = temp_path / "list.txt"
231
+ list_file.write_text(
232
+ "".join(f"file '{p.absolute()}'\n" for p in concat_paths),
233
+ encoding="utf-8"
234
+ )
235
+
236
+ # Final output path
237
+ output_path = "physics_chapter_video.mp4"
238
+
239
+ # Concatenate videos
240
+ progress(current_step/total_steps, desc="Creating final video...")
241
+ cmd = [
242
+ "ffmpeg",
243
+ "-loglevel", "error",
244
+ "-f", "concat", "-safe", "0", "-i", str(list_file),
245
+ "-c:v", "libx264", "-preset", PRESET, "-crf", str(CRF),
246
+ "-c:a", "aac", "-b:a", "128k",
247
+ "-movflags", "+faststart",
248
+ "-y", output_path,
249
+ ]
250
+
251
+ if run_cmd(cmd, timeout=300):
252
+ return output_path
253
+
254
+ return None
255
+
256
+ # Gradio Interface
257
+ def create_interface():
258
+ """Setup the web interface"""
259
+ with gr.Blocks(title="Physics Video Generator", theme=gr.themes.Soft()) as app:
260
+ gr.Markdown("""
261
+ # Physics Video Generator
262
+
263
+ Transform your physics topics into engaging educational videos! This tool will:
264
+ - Create professional title slides for each topic
265
+ - Find relevant educational content
266
+ - Combine everything into a complete video
267
+
268
+ **How to use:** Enter your topics one per line, or use numbered lists, or markdown headers.
269
+ """)
270
+
271
+ with gr.Row():
272
+ with gr.Column():
273
+ chapter_input = gr.Textbox(
274
+ label="Chapter Topics",
275
+ placeholder="""Enter topics like:
276
+ 1. Newton's Laws of Motion
277
+ 2. Force and Acceleration
278
+ 3. Momentum and Impulse
279
+ 4. Energy Conservation
280
+ 5. Circular Motion
281
+
282
+ Or:
283
+ # Kinematics
284
+ # Dynamics
285
+ # Thermodynamics""",
286
+ lines=10,
287
+ max_lines=15
288
+ )
289
+
290
+ generate_btn = gr.Button("Create Physics Video", variant="primary", size="lg")
291
+
292
+ with gr.Column():
293
+ video_output = gr.Video(label="Your Physics Video")
294
+
295
+ gr.Markdown("""
296
+ ### Important Notes:
297
+ - Processing typically takes 2-5 minutes
298
+ - Videos are optimized for educational use
299
+ - Limited to 8 topics per session
300
+ - Each video segment is capped at 30 seconds
301
+ """)
302
+
303
+ generate_btn.click(
304
+ fn=create_physics_video,
305
+ inputs=[chapter_input],
306
+ outputs=[video_output],
307
+ show_progress=True
308
+ )
309
+
310
+ # Examples
311
+ gr.Examples(
312
+ examples=[
313
+ ["1. Newton's First Law\n2. Newton's Second Law\n3. Newton's Third Law\n4. Applications of Newton's Laws"],
314
+ ["# Wave Motion\n# Sound Waves\n# Light Waves\n# Electromagnetic Spectrum"],
315
+ ["THERMODYNAMICS\nHEAT TRANSFER\nENTROPY\nCARNOT CYCLE"],
316
+ ["Quantum Mechanics Basics\nWave-Particle Duality\nHeisenberg Uncertainty Principle\nQuantum Tunneling"]
317
+ ],
318
+ inputs=[chapter_input],
319
+ label="Example Topics"
320
+ )
321
+
322
+ return app
323
+
324
+ if __name__ == "__main__":
325
+ app = create_interface()
326
+ app.queue(max_size=3) # Limit concurrent users for HF Spaces
327
+ app.launch(
328
+ share=False,
329
+ server_name="0.0.0.0",
330
+ server_port=7860
331
+ )
cookies/cookies.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Netscape HTTP Cookie File
2
+ # http://curl.haxx.se/rfc/cookie_spec.html
3
+ # This is a generated file! Do not edit.
4
+
5
+ .youtube.com TRUE / TRUE 1749730250 GPS 1
6
+ .youtube.com TRUE / TRUE 1784289135 PREF tz=Asia.Calcutta
7
+ .youtube.com TRUE / TRUE 0 YSC Zf6nnsaRwi0
8
+ .youtube.com TRUE / TRUE 1765281121 VISITOR_INFO1_LIVE 6C5bnjiP8JA
9
+ .youtube.com TRUE / TRUE 1765281121 VISITOR_PRIVACY_METADATA CgJJThIEGgAgNQ%3D%3D
10
+ .youtube.com TRUE / TRUE 1765280485 __Secure-ROLLOUT_TOKEN CI6E_L_ai7jwVBDW9ae15uuNAxjsu5bG5uuNAw%3D%3D
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ youtube-search-python
3
+ yt-dlp
4
+ youtube_search