lokesh341 commited on
Commit
4351bdd
·
verified ·
1 Parent(s): af8de94

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +139 -212
app.py CHANGED
@@ -1,19 +1,23 @@
1
- # app.py - Gym Workout Generator (Fixed - No 'info' parameter)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
5
  import os
6
  import re
7
  from datetime import datetime
8
- from PIL import Image, ImageDraw, ImageFont
9
  import tempfile
 
 
 
10
 
11
  # Video generation
12
  try:
13
  from moviepy.editor import ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip, ColorClip
 
14
  VIDEO_ENABLED = True
15
  except ImportError:
16
  VIDEO_ENABLED = False
 
17
 
18
  class GymWorkoutPDF(FPDF):
19
  def footer(self):
@@ -30,12 +34,13 @@ def clean_text(text, max_length=45):
30
  u"\U0001F600-\U0001F64F"
31
  u"\U0001F300-\U0001F5FF"
32
  u"\U0001F680-\U0001F6FF"
 
33
  "]+",
34
  flags=re.UNICODE
35
  )
36
  text = emoji_pattern.sub('', text)
37
  replacements = {
38
- '–': '-', '—': '-', '""': '"', '•': '*', '°': 'deg'
39
  }
40
  for old, new in replacements.items():
41
  text = text.replace(old, new)
@@ -43,304 +48,226 @@ def clean_text(text, max_length=45):
43
  text = re.sub(r'\s+', ' ', text.strip())
44
  return text[:max_length]
45
 
46
- def create_workout_screenshot(image_path, workout_name, workout_num, main_title, exercise_list):
47
- """Create professional workout screenshot template"""
48
  try:
49
- # Load base image
50
  img = Image.open(image_path)
51
- img = img.resize((900, 700), Image.Resampling.LANCZOS)
52
 
53
- # Create workout template
54
- template = Image.new('RGB', (900, 700), color=(25, 25, 25))
55
- draw = ImageDraw.Draw(template)
56
 
57
- # Header
58
- header_bg = Image.new('RGBA', (900, 120), color=(0, 100, 0, 220))
59
- template.paste(header_bg, (0, 0))
60
 
61
- # Fonts with fallback
62
  try:
63
- font_title = ImageFont.truetype("arial.ttf", 50)
64
- font_subtitle = ImageFont.truetype("arial.ttf", 32)
65
- font_exercise = ImageFont.truetype("arial.ttf", 24)
66
- font_small = ImageFont.truetype("arial.ttf", 18)
67
  except:
68
- font_title = ImageFont.load_default()
69
- font_subtitle = ImageFont.load_default()
70
- font_exercise = ImageFont.load_default()
71
  font_small = ImageFont.load_default()
72
 
73
- # Workout header text
74
  clean_name = clean_text(workout_name)
75
  clean_title = clean_text(main_title)
76
 
77
- draw.text((30, 20), f"WORKOUT {workout_num}", fill="white", font=font_title)
78
- draw.text((30, 70), clean_name.upper(), fill="#FFD700", font=font_subtitle)
79
- draw.text((30, 100), f"PROGRAM: {clean_title}", fill="white", font=font_small)
80
-
81
- # Exercise section
82
- exercises_bg = Image.new('RGBA', (850, 300), color=(0, 0, 0, 150))
83
- template.paste(exercises_bg, (25, 130))
84
-
85
- draw.text((40, 140), "EXERCISES:", fill="#00FF00", font=font_exercise)
86
-
87
- sample_exercises = exercise_list or [
88
- "Bench Press - 4 sets x 10-12 reps",
89
- "Incline Dumbbell Press - 3 sets x 12 reps",
90
- "Chest Flys - 3 sets x 15 reps",
91
- "Push Ups - 3 sets to failure"
92
- ]
93
-
94
- y_pos = 180
95
- for exercise in sample_exercises[:6]:
96
- draw.text((40, y_pos), f"• {exercise}", fill="white", font=font_small)
97
- y_pos += 35
98
 
99
- # Main workout image
100
- img_resized = img.resize((800, 350), Image.Resampling.LANCZOS)
101
- template.paste(img_resized, (50, 450))
102
 
103
  # Footer
104
- footer_bg = Image.new('RGBA', (900, 100), color=(0, 50, 0, 200))
105
- template.paste(footer_bg, (0, 600))
106
 
107
- draw.text((50, 620), "WORKOUT EXECUTION PLAN", fill="white", font=font_subtitle)
108
- draw.text((50, 650), "Sets x Reps | Rest Periods | Progressive Overload", fill="#FFD700", font=font_small)
109
-
110
- # Save
111
- template_path = f"workout_screenshot_{workout_num}.jpg"
112
- template.save(template_path, "JPEG", quality=95)
113
  return template_path
114
 
115
  except Exception as e:
116
- print(f"Screenshot creation failed: {e}")
117
  return image_path
118
 
119
- def generate_workout_content(main_title, workout_names, image_files, exercise_details):
120
  if not main_title.strip():
121
  return None, None, "Error: Main Title Required!"
122
 
123
  names_list = [n.strip() for n in workout_names.split(',') if n.strip()]
 
124
  if not names_list:
125
  return None, None, "Error: Add Workout Names!"
126
 
127
- if len(names_list) > 20:
128
- return None, None, "Error: Max 20 Workouts!"
129
 
130
- # Parse exercises
131
- exercise_lists = []
132
- if exercise_details.strip():
133
- exercises = [ex.strip() for ex in exercise_details.split('\n') if ex.strip()]
134
- exercises_per_workout = len(exercises) // len(names_list) if names_list else 0
135
- for i in range(len(names_list)):
136
- start_idx = i * exercises_per_workout
137
- end_idx = start_idx + exercises_per_workout
138
- exercise_lists.append(exercises[start_idx:end_idx])
139
- else:
140
- default_exercises = {
141
- "chest": ["Bench Press 4x10", "Incline Press 3x12", "Chest Flys 3x15"],
142
- "leg": ["Squats 4x8", "Leg Press 3x12", "Lunges 3x10"],
143
- "back": ["Deadlifts 4x8", "Pull Ups 3x10", "Rows 3x12"],
144
- "arm": ["Bicep Curls 3x12", "Tricep Extensions 3x12", "Hammer Curls 3x15"]
145
- }
146
- for workout in names_list:
147
- workout_lower = workout.lower()
148
- for key, ex_list in default_exercises.items():
149
- if key in workout_lower:
150
- exercise_lists.append(ex_list)
151
- break
152
- else:
153
- exercise_lists.append(["Main Exercise 4x10", "Secondary 3x12", "Finisher 3x15"])
154
 
155
  try:
 
156
  pdf = GymWorkoutPDF()
157
- pdf.set_margins(15, 15, 15)
158
 
159
- # Title Page
160
  pdf.add_page()
161
- pdf.set_font('Arial', 'B', 22)
162
  pdf.set_text_color(0, 128, 0)
163
- pdf.cell(0, 20, "PROFESSIONAL GYM WORKOUT PROGRAM", ln=1, align='C')
164
- pdf.set_font('Arial', 'B', 18)
165
- clean_title = clean_text(main_title)
166
- pdf.cell(0, 18, clean_title.upper(), ln=1, align='C')
167
 
168
- pdf.ln(10)
169
- pdf.set_font('Arial', '', 12)
170
- pdf.cell(0, 12, f"TOTAL WORKOUTS: {len(names_list)}", ln=1, align='C')
171
- pdf.cell(0, 12, f"CREATED: {datetime.now().strftime('%d/%m/%Y')}", ln=1, align='C')
172
 
173
  template_paths = []
174
 
175
- # Workout Pages
176
- for i, (workout_name, exercises) in enumerate(zip(names_list, exercise_lists)):
177
  pdf.add_page()
178
-
179
- pdf.set_font('Arial', 'B', 18)
180
  pdf.set_text_color(0, 128, 0)
181
- pdf.cell(0, 16, f"WORKOUT {i+1}", ln=1, align='C')
182
 
183
- clean_name = clean_text(workout_name)
184
- pdf.set_font('Arial', 'B', 16)
185
- pdf.multi_cell(0, 14, clean_name.upper(), align='C')
186
 
187
- # Create screenshot
188
- img_path = image_files[i] if i < len(image_files) else None
189
- screenshot_path = None
190
 
191
  if img_path and os.path.exists(img_path):
192
- screenshot_path = create_workout_screenshot(
193
- img_path, workout_name, i+1, main_title, exercises
194
  )
195
- template_paths.append(screenshot_path)
196
 
197
- img_width = 140
 
198
  x_pos = (pdf.w - img_width) / 2
199
- try:
200
- pdf.image(screenshot_path, x=x_pos, y=pdf.get_y(), w=img_width, h=180)
201
- pdf.set_draw_color(0, 128, 0)
202
- pdf.rect(x_pos, pdf.get_y(), img_width, 180, 'D')
203
- except:
204
- pass
205
  else:
206
- img_width = 140
207
- x_pos = (pdf.w - img_width) / 2
208
  pdf.set_fill_color(240, 248, 240)
209
  pdf.set_draw_color(0, 128, 0)
210
- pdf.rect(x_pos, pdf.get_y(), img_width, 180, 'FD')
211
- pdf.set_font('Arial', 'B', 12)
212
- pdf.set_xy(x_pos + 10, pdf.get_y() + 70)
213
- pdf.cell(img_width-20, 10, f"WORKOUT {i+1}", ln=1, align='C')
214
-
215
- # Workout details
216
- pdf.set_y(pdf.get_y() + 200)
217
- pdf.set_font('Arial', 'B', 14)
218
- pdf.set_text_color(0, 128, 0)
219
- pdf.cell(0, 12, "EXERCISE PLAN", ln=1, align='C')
220
-
221
- pdf.set_font('Arial', '', 10)
222
- for j, exercise in enumerate(exercises[:5]):
223
- pdf.ln(6)
224
- pdf.cell(0, 8, f"{j+1}. {exercise}", ln=1, align='L')
225
 
226
- timestamp = int(datetime.now().timestamp())
227
- pdf_path = f"gym_workouts_{timestamp}.pdf"
228
- pdf.output(pdf_path)
 
229
 
230
- # Video
231
  video_path = None
232
  if VIDEO_ENABLED and template_paths:
233
  try:
234
  clips = []
235
- duration = 7
 
236
 
237
- title_clip = ColorClip(size=(1280, 720), color=(0, 100, 0)).set_duration(4)
238
- title_text = TextClip(f"GYM WORKOUT PROGRAM\n{clean_title.upper()}",
239
- fontsize=70, color='white').set_duration(4).set_position('center')
240
- clips.append(CompositeVideoClip([title_clip, title_text]))
 
 
 
 
241
 
242
- for screenshot in template_paths:
243
- if os.path.exists(screenshot):
244
- clip = (ImageClip(screenshot)
245
- .resize(height=720)
246
- .set_duration(duration)
247
- .crossfadein(1))
248
- clips.append(clip)
 
 
249
 
250
- if len(clips) > 1:
251
- video = concatenate_videoclips(clips)
252
- video_path = f"workout_video_{timestamp}.mp4"
253
- video.write_videofile(video_path, fps=24, codec='libx264', audio=False)
 
 
 
 
 
 
 
 
254
  video.close()
255
- except:
256
- pass
 
 
257
 
258
- # Cleanup
259
  for path in template_paths:
260
  try:
261
  os.remove(path)
262
  except:
263
  pass
264
 
265
- video_status = "HD Video Generated!" if video_path else "No Video (MoviePy needed)"
266
- return pdf_path, video_path, f"""
267
- ✅ SUCCESS! {len(names_list)} Workout Screenshots Generated!
268
- 📄 PDF: {len(names_list) + 1} pages with professional templates
269
- 🖼️ {len(template_paths)} Workout screenshots created
270
  🎥 {video_status}
271
- 💪 Ready for gym clients!
272
  """
273
 
 
 
274
  except Exception as e:
275
  return None, None, f"Error: {str(e)}"
276
 
277
- # FIXED UI - No 'info' parameter
278
  with gr.Blocks(
279
  title="Gym Workout Generator",
280
- theme=gr.themes.Soft(primary_hue="green"),
281
- css="""
282
- .header {background: linear-gradient(135deg, #16a34a, #15803d); color: white; padding: 25px; border-radius: 15px; text-align: center;}
283
- .input-box {background: #f0fdf4; padding: 15px; border-radius: 10px; border: 2px solid #dcfce7; margin: 10px 0;}
284
- .success {background: #dcfce7; color: #166534; padding: 20px; border-radius: 10px;}
285
- """
286
  ) as demo:
287
 
288
- gr.HTML("""
289
- <div class="header">
290
- <h2>Gym Workout Screenshot Generator</h2>
291
- <p>Create professional workout plans with exercise screenshots</p>
292
- </div>
293
- """)
294
 
295
  gr.Markdown("""
296
  ### How to Use:
297
- 1. Enter program title
298
- 2. Add workout names (comma separated)
299
- 3. Upload exercise images/screenshots
300
- 4. Get PDF with workout templates + HD video
 
 
301
  """)
302
 
303
- with gr.Row():
304
- with gr.Column(scale=2):
305
- main_title = gr.Textbox(
306
- label="PROGRAM TITLE *",
307
- placeholder="12 Week Strength Program",
308
- elem_classes="input-box"
309
- )
310
-
311
- workout_names = gr.Textbox(
312
- label="WORKOUT NAMES * (Comma separated)",
313
- placeholder="Chest Day, Leg Day, Back Day, Arms",
314
- lines=3,
315
- elem_classes="input-box"
316
- )
317
-
318
- exercise_details = gr.Textbox(
319
- label="EXERCISES (Optional)",
320
- placeholder="Bench Press 4x10\nSquats 4x8\nDeadlifts 3x8",
321
- lines=4,
322
- elem_classes="input-box"
323
- )
324
-
325
- with gr.Column(scale=1):
326
- images_input = gr.File(
327
- label="Exercise Images/Screenshots",
328
- file_count="multiple",
329
- file_types=[".jpg", ".jpeg", ".png"],
330
- elem_classes="input-box"
331
- ) # REMOVED 'info' parameter
332
 
333
- generate_btn = gr.Button("Generate Workout Screenshots + PDF", variant="primary")
334
 
335
- with gr.Row():
336
- pdf_output = gr.File(label="Download PDF")
337
- video_output = gr.File(label="Download Video (if available)")
338
 
 
 
339
  status = gr.Markdown("Ready...")
340
 
341
  generate_btn.click(
342
- fn=generate_workout_content,
343
- inputs=[main_title, workout_names, images_input, exercise_details],
344
  outputs=[pdf_output, video_output, status]
345
  )
346
 
 
1
+ # app.py - Gym Workout Generator (Fixed Video Download + Generated Templates)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
5
  import os
6
  import re
7
  from datetime import datetime
 
8
  import tempfile
9
+ import base64
10
+ from io import BytesIO
11
+ from PIL import Image, ImageDraw, ImageFont
12
 
13
  # Video generation
14
  try:
15
  from moviepy.editor import ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip, ColorClip
16
+ from moviepy.video.fx.all import crop
17
  VIDEO_ENABLED = True
18
  except ImportError:
19
  VIDEO_ENABLED = False
20
+ print("MoviePy not available - using basic video generation")
21
 
22
  class GymWorkoutPDF(FPDF):
23
  def footer(self):
 
34
  u"\U0001F600-\U0001F64F"
35
  u"\U0001F300-\U0001F5FF"
36
  u"\U0001F680-\U0001F6FF"
37
+ u"\U0001F1E0-\U0001F1FF"
38
  "]+",
39
  flags=re.UNICODE
40
  )
41
  text = emoji_pattern.sub('', text)
42
  replacements = {
43
+ '–': '-', '—': '-', '“': '"', '”': '"', '•': '*', '°': 'deg'
44
  }
45
  for old, new in replacements.items():
46
  text = text.replace(old, new)
 
48
  text = re.sub(r'\s+', ' ', text.strip())
49
  return text[:max_length]
50
 
51
+ def create_screenshot_template(image_path, workout_name, workout_num, main_title):
 
52
  try:
53
+ # Load image
54
  img = Image.open(image_path)
55
+ img = img.resize((800, 600), Image.Resampling.LANCZOS)
56
 
57
+ # Create template
58
+ template = Image.new('RGB', (800, 600), color=(34, 139, 34))
 
59
 
60
+ draw = ImageDraw.Draw(template)
 
 
61
 
62
+ # Fallback fonts
63
  try:
64
+ font_large = ImageFont.truetype("arial.ttf", 48)
65
+ font_medium = ImageFont.truetype("arial.ttf", 36)
66
+ font_small = ImageFont.truetype("arial.ttf", 24)
 
67
  except:
68
+ font_large = ImageFont.load_default()
69
+ font_medium = ImageFont.load_default()
 
70
  font_small = ImageFont.load_default()
71
 
 
72
  clean_name = clean_text(workout_name)
73
  clean_title = clean_text(main_title)
74
 
75
+ # Add text
76
+ draw.text((50, 20), f"WORKOUT {workout_num}", fill="white", font=font_large)
77
+ draw.text((50, 80), clean_name.upper(), fill="yellow", font=font_medium)
78
+ draw.text((50, 120), f"PROGRAM: {clean_title}", fill="white", font=font_small)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ # Paste image
81
+ img_resized = img.resize((700, 400), Image.Resampling.LANCZOS)
82
+ template.paste(img_resized, (50, 150))
83
 
84
  # Footer
85
+ draw.text((50, 540), "Workout Execution Plan - Print & Train!", fill="white", font=font_small)
 
86
 
87
+ # Save to temp
88
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_file:
89
+ template_path = tmp_file.name
90
+ template.save(template_path, "JPEG", quality=95)
 
 
91
  return template_path
92
 
93
  except Exception as e:
94
+ print(f"Template error: {e}")
95
  return image_path
96
 
97
+ def generate_content(main_title, workout_names, image_files):
98
  if not main_title.strip():
99
  return None, None, "Error: Main Title Required!"
100
 
101
  names_list = [n.strip() for n in workout_names.split(',') if n.strip()]
102
+
103
  if not names_list:
104
  return None, None, "Error: Add Workout Names!"
105
 
106
+ if len(names_list) > 30:
107
+ return None, None, "Error: Max 30 Workouts!"
108
 
109
+ if not image_files:
110
+ return None, None, "Error: Upload at least 1 image!"
111
+
112
+ available_images = len(image_files)
113
+ status_msg = f"Using {available_images} images for {len(names_list)} workouts (placeholders for missing)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  try:
116
+ clean_main_title = clean_text(main_title, 50)
117
  pdf = GymWorkoutPDF()
118
+ pdf.set_margins(18, 18, 18)
119
 
120
+ # PDF Title
121
  pdf.add_page()
122
+ pdf.set_font('Arial', 'B', 20)
123
  pdf.set_text_color(0, 128, 0)
124
+ pdf.cell(0, 18, "GYM WORKOUT PROGRAM", ln=1, align='C')
125
+ pdf.set_font('Arial', 'B', 16)
126
+ pdf.cell(0, 15, clean_main_title.upper(), ln=1, align='C')
 
127
 
128
+ pdf.ln(15)
129
+ pdf.set_font('Arial', '', 11)
130
+ pdf.cell(0, 10, f"TOTAL WORKOUTS: {len(names_list)}", ln=1, align='C')
131
+ pdf.cell(0, 10, f"CREATED: {datetime.now().strftime('%d/%m/%Y')}", ln=1, align='C')
132
 
133
  template_paths = []
134
 
135
+ for i, workout_name in enumerate(names_list):
 
136
  pdf.add_page()
137
+ pdf.set_font('Arial', 'B', 16)
 
138
  pdf.set_text_color(0, 128, 0)
139
+ pdf.cell(0, 14, f"WORKOUT {i+1}", ln=1, align='C')
140
 
141
+ clean_name = clean_text(workout_name, 40)
142
+ pdf.set_font('Arial', 'B', 14)
143
+ pdf.multi_cell(0, 12, clean_name.upper(), align='C')
144
 
145
+ img_path = image_files[i] if i < available_images else None
146
+ template_path = None
 
147
 
148
  if img_path and os.path.exists(img_path):
149
+ template_path = create_screenshot_template(
150
+ img_path, workout_name, i+1, main_title
151
  )
152
+ template_paths.append(template_path)
153
 
154
+ # Add template to PDF
155
+ img_width = 110
156
  x_pos = (pdf.w - img_width) / 2
157
+ pdf.image(template_path, x=x_pos, y=pdf.get_y(), w=img_width, h=130)
158
+ pdf.set_draw_color(0, 128, 0)
159
+ pdf.rect(x_pos, pdf.get_y(), img_width, 130, 'D')
 
 
 
160
  else:
161
+ # Placeholder
 
162
  pdf.set_fill_color(240, 248, 240)
163
  pdf.set_draw_color(0, 128, 0)
164
+ pdf.rect(x_pos, pdf.get_y(), img_width, 130, 'FD')
165
+ pdf.set_font('Arial', 'B', 11)
166
+ pdf.set_xy(x_pos + 10, pdf.get_y() + 50)
167
+ pdf.cell(90, 10, f"WORKOUT {i+1}", ln=1, align='C')
 
 
 
 
 
 
 
 
 
 
 
168
 
169
+ # Save PDF to temp
170
+ with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp_pdf:
171
+ pdf_path = tmp_pdf.name
172
+ pdf.output(pdf_path)
173
 
174
+ # VIDEO GENERATION
175
  video_path = None
176
  if VIDEO_ENABLED and template_paths:
177
  try:
178
  clips = []
179
+ hd_width, hd_height = 1920, 1080
180
+ duration_per_slide = 6
181
 
182
+ # Title slide
183
+ title_clip = ColorClip(size=(hd_width, hd_height), color=(0, 128, 0)).set_duration(4)
184
+ title_text = TextClip(
185
+ f"GYM WORKOUT PROGRAM\n{clean_main_title.upper()}",
186
+ fontsize=100, color='white', font='Arial-Bold'
187
+ ).set_position('center').set_duration(4)
188
+ title_slide = CompositeVideoClip([title_clip, title_text])
189
+ clips.append(title_slide)
190
 
191
+ # Workout slides
192
+ for i, template_img in enumerate(template_paths):
193
+ if os.path.exists(template_img):
194
+ img_clip = ImageClip(template_img).resize(height=hd_height)
195
+ img_clip = img_clip.set_duration(duration_per_slide)
196
+
197
+ img_clip = img_clip.fx(crop, x1=100, y1=50, x2=hd_width-100, y2=hd_height-100)
198
+
199
+ clips.append(img_clip)
200
 
201
+ if clips:
202
+ video = concatenate_videoclips(clips, method="compose")
203
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp_video:
204
+ video_path = tmp_video.name
205
+ video.write_videofile(
206
+ video_path,
207
+ fps=24,
208
+ codec='libx264',
209
+ audio=False,
210
+ verbose=False,
211
+ logger=None
212
+ )
213
  video.close()
214
+
215
+ except Exception as e:
216
+ print(f"Video error: {e}")
217
+ video_path = None
218
 
219
+ # Cleanup templates
220
  for path in template_paths:
221
  try:
222
  os.remove(path)
223
  except:
224
  pass
225
 
226
+ video_status = "HD Video Generated and Ready to Download!" if video_path else "Video Generation Failed - Check MoviePy/FFmpeg Installation"
227
+ success_msg = f"""
228
+ ✅ SUCCESS! Generated for {len(names_list)} workouts!
229
+ 📄 PDF with generated screenshot templates
 
230
  🎥 {video_status}
231
+ 💪 Images processed: {available_images} (templates created)
232
  """
233
 
234
+ return pdf_path, video_path, success_msg
235
+
236
  except Exception as e:
237
  return None, None, f"Error: {str(e)}"
238
 
239
+ # UI
240
  with gr.Blocks(
241
  title="Gym Workout Generator",
242
+ theme=gr.themes.Soft(primary_hue="green")
 
 
 
 
 
243
  ) as demo:
244
 
245
+ gr.Markdown("# Gym Workout Generator\nPDF + HD Video + Screenshot Templates")
 
 
 
 
 
246
 
247
  gr.Markdown("""
248
  ### How to Use:
249
+ 1. Enter Title
250
+ 2. Add Workout Names (comma separated)
251
+ 3. Upload Images/Screenshots (optional - placeholders for missing)
252
+ 4. Get PDF + Video with generated templates!
253
+
254
+ Tip: Uploaded images get auto-converted to gym screenshot templates!
255
  """)
256
 
257
+ main_title = gr.Textbox(label="MAIN PROGRAM TITLE *")
258
+ workout_names = gr.Textbox(label="WORKOUT NAMES * (Comma separated)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
+ images_input = gr.File(label="Images/Screenshots (Optional)", file_count="multiple", file_types=["image"])
261
 
262
+ generate_btn = gr.Button("Generate")
 
 
263
 
264
+ pdf_output = gr.File(label="PDF")
265
+ video_output = gr.File(label="HD Video")
266
  status = gr.Markdown("Ready...")
267
 
268
  generate_btn.click(
269
+ fn=generate_content,
270
+ inputs=[main_title, workout_names, images_input],
271
  outputs=[pdf_output, video_output, status]
272
  )
273