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

updated app.py

Browse files
Files changed (1) hide show
  1. app.py +53 -147
app.py CHANGED
@@ -8,36 +8,22 @@ 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"""
@@ -54,45 +40,33 @@ def run_cmd(cmd: list[str], timeout: int = 120) -> bool:
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",
@@ -101,21 +75,15 @@ def dl_video(url: str, out: Path) -> bool:
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",
@@ -134,110 +102,82 @@ def make_card(text: str, out: Path, dur: int = TITLE_DUR) -> bool:
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",
@@ -247,85 +187,51 @@ def create_physics_video(chapter_text: str, progress=gr.Progress()) -> Optional[
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
- )
 
8
  from typing import List, Optional
9
  import gradio as gr
10
  import random
11
+ import urllib.request
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  # ---------------- CONFIG ----------------
14
+ TITLE_DUR = 3
15
+ SIZE = "1280x720"
16
  FPS = 30
17
+ CRF = 28
18
+ PRESET = "ultrafast"
19
+ YT_MAX_RESULTS = 2
20
+ MAX_VIDEO_LENGTH = 30
21
+ MAX_TOPICS = 8
22
+ PROXY_URL = "http://brd-customer-hl_0443f347-zone-freemium:[email protected]:33335"
23
  # ----------------------------------------
24
 
25
+ print("CWD:", os.getcwd())
26
+
27
  # ---------- helpers ----------
28
  def run_cmd(cmd: list[str], timeout: int = 120) -> bool:
29
  """Run command with timeout and proper error handling"""
 
40
  print(f"Command failed:\n{' '.join(cmd)}\nError:\n{e.stderr if hasattr(e, 'stderr') else str(e)}")
41
  return False
42
 
43
+ def youtube_search_with_proxy(query: str, max_results: int) -> List[str]:
44
+ """Search YouTube using proxy via urllib + monkey-patching"""
45
  try:
46
+ import ssl
47
  from youtube_search import YoutubeSearch
48
 
49
+ proxy = PROXY_URL
50
+ opener = urllib.request.build_opener(
51
+ urllib.request.ProxyHandler({'http': proxy, 'https': proxy})
52
+ )
53
+ urllib.request.install_opener(opener)
 
 
 
 
 
 
 
 
54
 
 
55
  results = YoutubeSearch(query, max_results=max_results).to_dict()
 
 
56
  return ["https://www.youtube.com" + r["url_suffix"] for r in results]
57
  except Exception as e:
58
+ print(f"YouTube search failed with proxy: {e}")
59
  return []
60
 
61
+ def yt_urls(query: str, max_results: int) -> List[str]:
62
+ return youtube_search_with_proxy(query, max_results)
63
 
64
  def safe_filename(name: str) -> str:
65
+ return re.sub(r"[^\w\-\.]", "_", name)[:50]
 
 
66
 
67
  def dl_video(url: str, out: Path) -> bool:
68
+ """Download video using yt-dlp with proxy"""
69
  out.parent.mkdir(exist_ok=True)
 
70
 
71
  cmd = [
72
  "yt-dlp",
 
75
  "--merge-output-format", "mp4",
76
  "-o", str(out),
77
  "--no-playlist",
78
+ "--proxy", PROXY_URL,
 
 
79
  url,
80
  ]
81
  return run_cmd(cmd, timeout=60)
82
 
 
83
  def make_card(text: str, out: Path, dur: int = TITLE_DUR) -> bool:
 
 
84
  wrapped = textwrap.wrap(text, width=25)
85
  safe_text = "\\n".join(w.replace("'", r"\\'") for w in wrapped)
86
 
 
87
  cmd = [
88
  "ffmpeg",
89
  "-loglevel", "error",
 
102
  return run_cmd(cmd)
103
 
104
  def extract_topics(text: str) -> List[str]:
 
105
  topics = []
106
  for line in text.splitlines():
107
  line = line.strip()
108
  if not line or len(topics) >= MAX_TOPICS:
109
  continue
 
 
110
  if re.match(r"^\d+[\.)]\s+.+", line):
111
+ topics.append(re.sub(r"^\d+[\.)]\s*", "", line))
 
 
112
  elif re.match(r"^#+\s+.+", line):
113
+ topics.append(re.sub(r"^#+\s*", "", line))
 
 
114
  elif line.isupper() and 3 <= len(line) <= 50:
115
  topics.append(line.title())
 
116
  elif len(line) > 3 and not line.startswith(('http', 'www')):
117
  topics.append(line)
 
118
  return topics[:MAX_TOPICS]
119
 
120
  def create_physics_video(chapter_text: str, progress=gr.Progress()) -> Optional[str]:
 
121
  if not chapter_text.strip():
122
  return None
123
 
124
  progress(0, desc="Extracting topics...")
125
  topics = extract_topics(chapter_text)
 
126
  if not topics:
127
  return None
128
+
 
129
  with tempfile.TemporaryDirectory() as temp_dir:
130
  temp_path = Path(temp_dir)
131
  concat_paths: List[Path] = []
132
+
133
+ total_steps = len(topics) * 2 + 3
134
  current_step = 0
135
+
136
+ progress(current_step / total_steps, desc="Creating opening card...")
 
137
  opening = temp_path / "00_opening.mp4"
138
  if make_card("Physics Chapter Overview", opening):
139
  concat_paths.append(opening)
140
  current_step += 1
141
+
 
142
  for idx, topic in enumerate(topics, 1):
 
143
  progress(current_step/total_steps, desc=f"Creating card for: {topic[:30]}...")
144
  card = temp_path / f"title_{idx:02d}.mp4"
145
  if make_card(topic, card):
146
  concat_paths.append(card)
147
  current_step += 1
148
+
 
149
  progress(current_step/total_steps, desc=f"Searching video for: {topic[:30]}...")
150
  video_found = False
 
151
  for url in yt_urls(f"{topic} physics explanation", YT_MAX_RESULTS):
152
  vid_id_match = re.search(r"(?:v=|be/|shorts/)([\w\-]{11})", url)
153
  if not vid_id_match:
154
  continue
 
155
  vid_path = temp_path / f"{safe_filename(vid_id_match.group(1))}.mp4"
156
  if dl_video(url, vid_path):
157
  concat_paths.append(vid_path)
158
  video_found = True
159
  break
160
+
161
  if not video_found:
 
162
  placeholder = temp_path / f"placeholder_{idx:02d}.mp4"
163
  if make_card(f"Exploring: {topic}", placeholder, dur=5):
164
  concat_paths.append(placeholder)
 
165
  current_step += 1
166
+
167
+ progress(current_step / total_steps, desc="Creating closing card...")
 
168
  closing = temp_path / "zz_closing.mp4"
169
  if make_card("Thank you for learning!", closing):
170
  concat_paths.append(closing)
171
  current_step += 1
172
+
173
  if len(concat_paths) < 2:
174
  return None
175
+
 
176
  list_file = temp_path / "list.txt"
177
+ list_file.write_text("".join(f"file '{p.absolute()}'\n" for p in concat_paths), encoding="utf-8")
 
 
 
 
 
178
  output_path = "physics_chapter_video.mp4"
179
+
180
+ progress(current_step / total_steps, desc="Creating final video...")
 
181
  cmd = [
182
  "ffmpeg",
183
  "-loglevel", "error",
 
187
  "-movflags", "+faststart",
188
  "-y", output_path,
189
  ]
190
+
191
  if run_cmd(cmd, timeout=300):
192
  return output_path
 
193
  return None
194
 
195
  # Gradio Interface
196
  def create_interface():
 
197
  with gr.Blocks(title="Physics Video Generator", theme=gr.themes.Soft()) as app:
198
  gr.Markdown("""
199
  # Physics Video Generator
200
 
201
+ Turn your physics topics into an engaging educational video.
 
 
 
 
 
202
  """)
 
203
  with gr.Row():
204
  with gr.Column():
205
  chapter_input = gr.Textbox(
206
  label="Chapter Topics",
207
+ placeholder="1. Newton's First Law\n2. Force\n3. Momentum",
 
 
 
 
 
 
 
 
 
 
208
  lines=10,
209
  max_lines=15
210
  )
 
211
  generate_btn = gr.Button("Create Physics Video", variant="primary", size="lg")
 
212
  with gr.Column():
213
  video_output = gr.Video(label="Your Physics Video")
214
+ gr.Markdown("Each video is limited to ~30 sec/topic.")
215
+
 
 
 
 
 
 
 
216
  generate_btn.click(
217
  fn=create_physics_video,
218
  inputs=[chapter_input],
219
  outputs=[video_output],
220
  show_progress=True
221
  )
222
+
 
223
  gr.Examples(
224
  examples=[
225
+ ["1. Newton's First Law\n2. Newton's Second Law\n3. Newton's Third Law"],
226
+ ["# Thermodynamics\n# Heat Transfer\n# Entropy"],
227
+ ["QUANTUM MECHANICS\nWAVE-PARTICLE DUALITY\nUNCERTAINTY PRINCIPLE"]
 
228
  ],
229
  inputs=[chapter_input],
230
  label="Example Topics"
231
  )
 
232
  return app
233
 
234
  if __name__ == "__main__":
235
  app = create_interface()
236
+ app.queue(max_size=3)
237
+ app.launch(server_name="0.0.0.0", server_port=7860)