lokesh341 commited on
Commit
83d8a45
·
verified ·
1 Parent(s): 34fa595

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -209
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - Professional Gym Workout Generator (FIXED - No 'info' parameter)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
@@ -6,322 +6,262 @@ import os
6
  import re
7
  from datetime import datetime
8
  import tempfile
 
9
  from PIL import Image, ImageDraw, ImageFont
10
- import numpy as np
11
 
12
- # Enhanced video generation
13
  try:
14
- from moviepy.editor import (
15
- ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip,
16
- ColorClip, vfx
17
- )
18
  VIDEO_ENABLED = True
19
  except ImportError:
20
  VIDEO_ENABLED = False
 
21
 
22
- class ProfessionalGymPDF(FPDF):
23
- def header(self):
24
- self.set_font('Arial', 'B', 15)
25
- self.set_text_color(0, 128, 0)
26
- self.cell(0, 10, 'PROFESSIONAL GYM WORKOUT PLAN', 0, 1, 'C')
27
- self.ln(5)
28
-
29
  def footer(self):
30
- self.set_y(-15)
31
- self.set_font('Arial', 'I', 8)
32
- self.cell(0, 10, f'Page {self.page_no()} | {datetime.now().strftime("%d/%m/%Y")}', 0, 0, 'C')
33
 
34
- def clean_text(text, max_length=60):
35
  if not text:
36
- return "Workout Session"
37
  text = str(text)
38
- emoji_pattern = re.compile("["
 
39
  u"\U0001F600-\U0001F64F"
40
  u"\U0001F300-\U0001F5FF"
41
  u"\U0001F680-\U0001F6FF"
42
  u"\U0001F1E0-\U0001F1FF"
43
- "]+", flags=re.UNICODE)
 
 
44
  text = emoji_pattern.sub('', text)
45
- text = re.sub(r'[^\w\s\-.,!?\(\)]', '', text)
 
 
 
 
 
46
  text = re.sub(r'\s+', ' ', text.strip())
47
  return text[:max_length]
48
 
49
- def create_premium_template(image_path, workout_name, workout_num, main_title):
50
- """Create professional HD template with workout name overlay"""
51
  try:
 
52
  img = Image.open(image_path)
53
- img = img.resize((1920, 1080), Image.Resampling.LANCZOS)
 
 
 
54
 
55
- template = Image.new('RGB', (1920, 1080), color=(25, 25, 25))
56
  draw = ImageDraw.Draw(template)
57
 
 
58
  try:
59
- font_title = ImageFont.truetype("arial.ttf", 80)
60
- font_workout = ImageFont.truetype("arial.ttf", 60)
61
- font_sub = ImageFont.truetype("arial.ttf", 40)
62
  except:
63
- font_title = ImageFont.load_default()
64
- font_workout = ImageFont.load_default()
65
- font_sub = ImageFont.load_default()
66
 
67
  clean_name = clean_text(workout_name)
68
  clean_title = clean_text(main_title)
69
 
70
- header_bg = Image.new('RGBA', (1920, 200), (0, 100, 0, 200))
71
- template.paste(header_bg, (0, 0), header_bg)
72
-
73
- draw.text((100, 50), f"WORKOUT SESSION {workout_num}", fill="white", font=font_title)
74
- draw.text((100, 130), clean_name.upper(), fill="#FFD700", font=font_workout)
75
- draw.text((100, 200), f"PROGRAM: {clean_title}", fill="white", font=font_sub)
76
-
77
- img_resized = img.resize((1600, 800), Image.Resampling.LANCZOS)
78
- template.paste(img_resized, (160, 300))
79
-
80
- draw.rectangle([160, 300, 1760, 1100], outline="#00FF00", width=5)
81
 
82
- footer_bg = Image.new('RGBA', (1920, 150), (0, 0, 0, 180))
83
- template.paste(footer_bg, (0, 930), footer_bg)
 
84
 
85
- draw.text((100, 950), "EXECUTION PLAN:", fill="#00FF00", font=font_sub)
86
- draw.text((100, 990), "SETS | REPS | REST | PROGRESS", fill="white", font=font_sub)
87
- draw.text((100, 1030), "PRINT • TRAIN • TRACK • REPEAT", fill="#FFD700", font=font_sub)
88
 
89
- template_path = f"hd_template_{workout_num}.jpg"
90
- template.save(template_path, "JPEG", quality=95, optimize=False)
 
 
91
  return template_path
92
 
93
  except Exception as e:
94
  print(f"Template error: {e}")
95
  return image_path
96
 
97
- def generate_professional_content(main_title, workout_names, image_files):
98
  if not main_title.strip():
99
- return None, None, " Main Title Required!"
100
 
101
  names_list = [n.strip() for n in workout_names.split(',') if n.strip()]
 
102
  if not names_list:
103
- return None, None, " Add Workout Names!"
 
 
 
104
 
105
- if len(names_list) > 20:
106
- return None, None, " Max 20 Workouts!"
107
 
108
- available_images = len(image_files) if image_files else 0
 
109
 
110
  try:
111
- clean_main_title = clean_text(main_title, 60)
112
- pdf = ProfessionalGymPDF()
113
- pdf.set_margins(20, 20, 20)
114
- pdf.add_page()
115
 
116
- # Premium PDF Title Page
117
- pdf.set_font('Arial', 'B', 24)
 
118
  pdf.set_text_color(0, 128, 0)
119
- pdf.cell(0, 20, "PROFESSIONAL", ln=1, align='C')
120
- pdf.set_font('Arial', 'B', 28)
121
- pdf.cell(0, 25, "GYM WORKOUT", ln=1, align='C')
122
- pdf.set_font('Arial', 'B', 22)
123
- pdf.cell(0, 20, "PROGRAM", ln=1, align='C')
124
-
125
- pdf.ln(10)
126
- pdf.set_font('Arial', 'B', 18)
127
  pdf.cell(0, 15, clean_main_title.upper(), ln=1, align='C')
128
 
129
  pdf.ln(15)
130
- pdf.set_font('Arial', '', 12)
131
- pdf.set_text_color(100, 100, 100)
132
- pdf.cell(0, 10, f"TOTAL SESSIONS: {len(names_list)}", ln=1, align='C')
133
- pdf.cell(0, 10, f"CREATED: {datetime.now().strftime('%d/%m/%Y %H:%M')}", ln=1, align='C')
134
- pdf.cell(0, 10, "PROFESSIONAL GYM CERTIFIED", ln=1, align='C')
135
 
136
- hd_templates = []
137
 
138
  for i, workout_name in enumerate(names_list):
139
  pdf.add_page()
140
- pdf.set_font('Arial', 'B', 20)
141
- pdf.set_text_color(0, 128, 0)
142
- pdf.cell(0, 18, f"SESSION {i+1}", ln=1, align='C')
143
-
144
- clean_name = clean_text(workout_name, 50)
145
  pdf.set_font('Arial', 'B', 16)
146
- pdf.cell(0, 14, clean_name.upper(), ln=1, align='C')
147
-
148
- pdf.ln(5)
149
- pdf.set_font('Arial', 'B', 14)
150
  pdf.set_text_color(0, 128, 0)
151
- pdf.cell(0, 12, "EXECUTION PROTOCOL", ln=1, align='C')
152
-
153
- pdf.set_draw_color(0, 128, 0)
154
- pdf.set_line_width(2)
155
- pdf.line(20, pdf.get_y(), pdf.w-20, pdf.get_y())
156
 
157
- pdf.ln(8)
158
- pdf.set_font('Arial', '', 10)
159
- pdf.set_text_color(60, 60, 60)
160
-
161
- workout_details = [
162
- f"PRIMARY FOCUS: {clean_name}",
163
- "WARMUP: 5-10 min dynamic stretches",
164
- "SETS x REPS: _________________________",
165
- "WEIGHT PROGRESSION: _________________",
166
- "REST INTERVALS: ______________________",
167
- "TARGET RPE: 7-9 / 10",
168
- "COOLDOWN: Static stretches 5-10 min"
169
- ]
170
-
171
- for detail in workout_details:
172
- pdf.ln(6)
173
- pdf.cell(0, 8, detail, ln=1, align='L')
174
 
175
  img_path = image_files[i] if i < available_images else None
176
  template_path = None
177
 
178
  if img_path and os.path.exists(img_path):
179
- template_path = create_premium_template(
180
  img_path, workout_name, i+1, main_title
181
  )
182
- hd_templates.append(template_path)
183
 
184
- img_width = 140
 
185
  x_pos = (pdf.w - img_width) / 2
186
- try:
187
- pdf.image(template_path, x=x_pos, y=pdf.get_y(), w=img_width, h=180)
188
- pdf.set_draw_color(0, 128, 0)
189
- pdf.set_line_width(1)
190
- pdf.rect(x_pos, pdf.get_y(), img_width, 180, 'D')
191
- except:
192
- pass
193
-
194
- pdf.ln(10)
195
- pdf.set_font('Arial', 'B', 12)
196
- pdf.set_text_color(0, 128, 0)
197
- pdf.cell(0, 10, "SIGNATURE: ________________ Date: ________", ln=1, align='C')
198
 
199
- timestamp = int(datetime.now().timestamp())
200
- pdf_path = f"professional_workout_{timestamp}.pdf"
201
- pdf.output(pdf_path)
 
202
 
203
- # HD VIDEO GENERATION
204
  video_path = None
205
- if VIDEO_ENABLED and hd_templates:
206
  try:
207
  clips = []
208
- duration_per_slide = 8
 
209
 
210
- title_bg = ColorClip(size=(1920, 1080), color=(0, 50, 0)).set_duration(5)
 
211
  title_text = TextClip(
212
- f"PROFESSIONAL GYM PROGRAM\n{clean_main_title.upper()}",
213
- fontsize=120, color='white', font='Arial-Bold',
214
- size=(1600, None)
215
- ).set_position('center').set_duration(5)
216
- title_slide = CompositeVideoClip([title_bg, title_text]).set_duration(5)
217
  clips.append(title_slide)
218
 
219
- for template_img in hd_templates:
 
220
  if os.path.exists(template_img):
221
- img_clip = (ImageClip(template_img)
222
- .set_duration(duration_per_slide)
223
- .resize(height=1080)
224
- .fx(vfx.fadein, 1)
225
- .fx(vfx.fadeout, 1))
226
  clips.append(img_clip)
227
 
228
- video = concatenate_videoclips(clips, method="compose")
229
- video_path = f"hd_workout_video_{timestamp}.mp4"
230
- video.write_videofile(
231
- video_path,
232
- fps=30,
233
- codec='libx264',
234
- audio=False,
235
- bitrate="8000k",
236
- verbose=False,
237
- logger=None
238
- )
239
- video.close()
 
240
 
241
  except Exception as e:
242
  print(f"Video error: {e}")
243
  video_path = None
244
 
245
  # Cleanup
246
- for path in hd_templates:
247
  try:
248
  os.remove(path)
249
  except:
250
  pass
251
 
252
- video_status = "HD VIDEO GENERATED (1920x1080)" if video_path else "Video Failed"
253
  success_msg = f"""
254
- 🎯 PROFESSIONAL GYM CONTENT GENERATED!
255
-
256
- 📄 PDF: {len(names_list) + 1} premium pages
257
- 🎥 {video_status}
258
- 💪 Workouts: {len(names_list)} sessions
259
- 🖼️ Images: {available_images} HD templates
260
  """
261
 
262
  return pdf_path, video_path, success_msg
263
 
264
  except Exception as e:
265
- return None, None, f"Error: {str(e)}"
266
 
267
- # FIXED UI - REMOVED 'info' parameter
268
- with gr.Blocks(
269
- title="Professional Gym Workout Generator",
270
- theme=gr.themes.Soft(primary_hue="green"),
271
- css="""
272
- .header {background: linear-gradient(135deg, #0f5132, #198754); color: white; padding: 30px; border-radius: 15px; text-align: center;}
273
- .input-box {background: #f8fff9; padding: 20px; border-radius: 10px; border: 2px solid #198754;}
274
- .btn-pro {background: linear-gradient(45deg, #198754, #0f5132) !important; font-size: 18px; padding: 15px;}
275
- .status-pro {background: #d1e7dd; color: #0f5132; padding: 20px; border-radius: 10px; border-left: 5px solid #198754;}
276
- """
277
- ) as demo:
278
 
279
- gr.HTML("""
280
- <div class="header">
281
- <h1>🏋️ PROFESSIONAL GYM GENERATOR</h1>
282
- <p><strong>HD Video (1920x1080) + Premium PDF</strong></p>
283
- </div>
284
- """)
285
 
286
  gr.Markdown("""
287
- ### 🚀 Features:
288
- - HD Video: 1920x1080 with workout names on each slide
289
- - Premium PDF: Professional execution protocols
290
- - Auto-generates gym templates from your images
291
- - Each slide shows: WORKOUT # + NAME + Your image
 
 
292
  """)
293
 
294
- with gr.Row():
295
- with gr.Column(scale=2):
296
- main_title = gr.Textbox(
297
- label="🏆 PROGRAM TITLE *",
298
- placeholder="12 Week Professional Strength Cycle",
299
- elem_classes="input-box"
300
- )
301
- workout_names = gr.Textbox(
302
- label="💪 WORKOUT NAMES * (Comma separated)",
303
- placeholder="Chest Development, Leg Power, Back Strength, Arms & Shoulders, Core Blast",
304
- lines=4,
305
- elem_classes="input-box"
306
- )
307
- with gr.Column(scale=1):
308
- images_input = gr.File(
309
- label="📸 Workout Images *",
310
- file_count="multiple",
311
- file_types=["image"],
312
- elem_classes="input-box"
313
- )
314
 
315
- generate_btn = gr.Button("🎬 Generate HD Video + Premium PDF", variant="primary", elem_classes="btn-pro")
316
 
317
- with gr.Row():
318
- pdf_output = gr.File(label="📄 Premium PDF Download")
319
- video_output = gr.File(label="🎥 HD Video Download (1920x1080)")
320
 
321
- status = gr.Markdown("Ready to create professional gym content...", elem_classes="status-pro")
 
 
322
 
323
  generate_btn.click(
324
- fn=generate_professional_content,
325
  inputs=[main_title, workout_names, images_input],
326
  outputs=[pdf_output, video_output, status]
327
  )
 
1
+ # app.py - Gym Workout Generator (Fixed Video + Generated Templates in PDF/Video)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
 
6
  import re
7
  from datetime import datetime
8
  import tempfile
9
+ from io import BytesIO
10
  from PIL import Image, ImageDraw, ImageFont
 
11
 
12
+ # Video generation
13
  try:
14
+ from moviepy.editor import ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip, ColorClip
15
+ from moviepy.video.fx.all import crop
 
 
16
  VIDEO_ENABLED = True
17
  except ImportError:
18
  VIDEO_ENABLED = False
19
+ print("MoviePy not available - using basic video generation")
20
 
21
+ class GymWorkoutPDF(FPDF):
 
 
 
 
 
 
22
  def footer(self):
23
+ self.set_y(-10)
24
+ self.set_font('Arial', 'I', 7)
25
+ self.cell(0, 8, f'GYM WORKOUT PLAN | Page {self.page_no()}', 0, 0, 'C')
26
 
27
+ def clean_text(text, max_length=45):
28
  if not text:
29
+ return "Workout"
30
  text = str(text)
31
+ emoji_pattern = re.compile(
32
+ "["
33
  u"\U0001F600-\U0001F64F"
34
  u"\U0001F300-\U0001F5FF"
35
  u"\U0001F680-\U0001F6FF"
36
  u"\U0001F1E0-\U0001F1FF"
37
+ "]+",
38
+ flags=re.UNICODE
39
+ )
40
  text = emoji_pattern.sub('', text)
41
+ replacements = {
42
+ '–': '-', '—': '-', '“': '"', '”': '"', '•': '*', '°': 'deg'
43
+ }
44
+ for old, new in replacements.items():
45
+ text = text.replace(old, new)
46
+ text = re.sub(r'[^\x00-\x7F\u0020-\u007E]', '', text)
47
  text = re.sub(r'\s+', ' ', text.strip())
48
  return text[:max_length]
49
 
50
+ def create_screenshot_template(image_path, workout_name, workout_num, main_title):
 
51
  try:
52
+ # Load image
53
  img = Image.open(image_path)
54
+ img = img.resize((800, 600), Image.Resampling.LANCZOS)
55
+
56
+ # Create template
57
+ template = Image.new('RGB', (800, 600), color=(34, 139, 34))
58
 
 
59
  draw = ImageDraw.Draw(template)
60
 
61
+ # Fallback fonts
62
  try:
63
+ font_large = ImageFont.truetype("arial.ttf", 48)
64
+ font_medium = ImageFont.truetype("arial.ttf", 36)
65
+ font_small = ImageFont.truetype("arial.ttf", 24)
66
  except:
67
+ font_large = ImageFont.load_default()
68
+ font_medium = ImageFont.load_default()
69
+ font_small = ImageFont.load_default()
70
 
71
  clean_name = clean_text(workout_name)
72
  clean_title = clean_text(main_title)
73
 
74
+ # Add text
75
+ draw.text((50, 20), f"WORKOUT {workout_num}", fill="white", font=font_large)
76
+ draw.text((50, 80), clean_name.upper(), fill="yellow", font=font_medium)
77
+ draw.text((50, 120), f"PROGRAM: {clean_title}", fill="white", font=font_small)
 
 
 
 
 
 
 
78
 
79
+ # Paste image
80
+ img_resized = img.resize((700, 400), Image.Resampling.LANCZOS)
81
+ template.paste(img_resized, (50, 150))
82
 
83
+ # Footer
84
+ draw.text((50, 540), "Workout Execution Plan - Print & Train!", fill="white", font=font_small)
 
85
 
86
+ # Save to temp
87
+ with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp_file:
88
+ template_path = tmp_file.name
89
+ template.save(template_path, "JPEG", quality=95)
90
  return template_path
91
 
92
  except Exception as e:
93
  print(f"Template error: {e}")
94
  return image_path
95
 
96
+ def generate_content(main_title, workout_names, image_files):
97
  if not main_title.strip():
98
+ return None, None, "Error: Main Title Required!"
99
 
100
  names_list = [n.strip() for n in workout_names.split(',') if n.strip()]
101
+
102
  if not names_list:
103
+ return None, None, "Error: Add Workout Names!"
104
+
105
+ if len(names_list) > 30:
106
+ return None, None, "Error: Max 30 Workouts!"
107
 
108
+ if not image_files:
109
+ return None, None, "Error: Upload at least 1 image!"
110
 
111
+ available_images = len(image_files)
112
+ status_msg = f"Using {available_images} images for {len(names_list)} workouts (placeholders for missing)"
113
 
114
  try:
115
+ clean_main_title = clean_text(main_title, 50)
116
+ pdf = GymWorkoutPDF()
117
+ pdf.set_margins(18, 18, 18)
 
118
 
119
+ # PDF Title
120
+ pdf.add_page()
121
+ pdf.set_font('Arial', 'B', 20)
122
  pdf.set_text_color(0, 128, 0)
123
+ pdf.cell(0, 18, "GYM WORKOUT PROGRAM", ln=1, align='C')
124
+ pdf.set_font('Arial', 'B', 16)
 
 
 
 
 
 
125
  pdf.cell(0, 15, clean_main_title.upper(), ln=1, align='C')
126
 
127
  pdf.ln(15)
128
+ pdf.set_font('Arial', '', 11)
129
+ pdf.cell(0, 10, f"TOTAL WORKOUTS: {len(names_list)}", ln=1, align='C')
130
+ pdf.cell(0, 10, f"CREATED: {datetime.now().strftime('%d/%m/%Y')}", ln=1, align='C')
 
 
131
 
132
+ template_paths = []
133
 
134
  for i, workout_name in enumerate(names_list):
135
  pdf.add_page()
 
 
 
 
 
136
  pdf.set_font('Arial', 'B', 16)
 
 
 
 
137
  pdf.set_text_color(0, 128, 0)
138
+ pdf.cell(0, 14, f"WORKOUT {i+1}", ln=1, align='C')
 
 
 
 
139
 
140
+ clean_name = clean_text(workout_name, 40)
141
+ pdf.set_font('Arial', 'B', 14)
142
+ pdf.multi_cell(0, 12, clean_name.upper(), align='C')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
 
144
  img_path = image_files[i] if i < available_images else None
145
  template_path = None
146
 
147
  if img_path and os.path.exists(img_path):
148
+ template_path = create_screenshot_template(
149
  img_path, workout_name, i+1, main_title
150
  )
151
+ template_paths.append(template_path)
152
 
153
+ # Add to PDF
154
+ img_width = 110
155
  x_pos = (pdf.w - img_width) / 2
156
+ pdf.image(template_path, x=x_pos, y=pdf.get_y(), w=img_width, h=130)
157
+ pdf.set_draw_color(0, 128, 0)
158
+ pdf.rect(x_pos, pdf.get_y(), img_width, 130, 'D')
159
+ else:
160
+ pdf.set_fill_color(240, 248, 240)
161
+ pdf.set_draw_color(0, 128, 0)
162
+ pdf.rect(x_pos, pdf.get_y(), img_width, 130, 'FD')
163
+ pdf.set_font('Arial', 'B', 11)
164
+ pdf.set_xy(x_pos + 10, pdf.get_y() + 50)
165
+ pdf.cell(90, 10, f"WORKOUT {i+1}", ln=1, align='C')
 
 
166
 
167
+ # Save PDF to temp
168
+ with tempfile.NamedTemporaryFile(suffix=".pdf", delete=False) as tmp_pdf:
169
+ pdf_path = tmp_pdf.name
170
+ pdf.output(pdf_path)
171
 
172
+ # VIDEO GENERATION
173
  video_path = None
174
+ if VIDEO_ENABLED and template_paths:
175
  try:
176
  clips = []
177
+ hd_width, hd_height = 1920, 1080
178
+ duration_per_slide = 6
179
 
180
+ # Title slide
181
+ title_clip = ColorClip(size=(hd_width, hd_height), color=(0, 128, 0)).set_duration(4)
182
  title_text = TextClip(
183
+ f"GYM WORKOUT PROGRAM\n{clean_main_title.upper()}",
184
+ fontsize=100, color='white', font='Arial-Bold'
185
+ ).set_position('center').set_duration(4)
186
+ title_slide = CompositeVideoClip([title_clip, title_text])
 
187
  clips.append(title_slide)
188
 
189
+ # Workout slides
190
+ for i, template_img in enumerate(template_paths):
191
  if os.path.exists(template_img):
192
+ img_clip = ImageClip(template_img).resize(height=hd_height)
193
+ img_clip = img_clip.set_duration(duration_per_slide)
194
+
195
+ img_clip = img_clip.fx(crop, x1=100, y1=50, x2=hd_width-100, y2=hd_height-100)
196
+
197
  clips.append(img_clip)
198
 
199
+ if clips:
200
+ video = concatenate_videoclips(clips, method="compose")
201
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as tmp_video:
202
+ video_path = tmp_video.name
203
+ video.write_videofile(
204
+ video_path,
205
+ fps=24,
206
+ codec='libx264',
207
+ audio=False,
208
+ verbose=False,
209
+ logger=None
210
+ )
211
+ video.close()
212
 
213
  except Exception as e:
214
  print(f"Video error: {e}")
215
  video_path = None
216
 
217
  # Cleanup
218
+ for path in template_paths:
219
  try:
220
  os.remove(path)
221
  except:
222
  pass
223
 
224
+ video_status = "HD Video Generated!" if video_path else "Video Generation Failed - Check MoviePy/FFmpeg"
225
  success_msg = f"""
226
+ SUCCESS! Generated for {len(names_list)} workouts!
227
+ PDF with generated screenshot templates
228
+ {video_status}
229
+ Images processed: {available_images}
 
 
230
  """
231
 
232
  return pdf_path, video_path, success_msg
233
 
234
  except Exception as e:
235
+ return None, None, f"Error: {str(e)}"
236
 
237
+ # UI
238
+ with gr.Blocks(title="Gym Workout Generator") as demo:
 
 
 
 
 
 
 
 
 
239
 
240
+ gr.Markdown("# Gym Workout Generator\nPDF + HD Video + Screenshot Templates")
 
 
 
 
 
241
 
242
  gr.Markdown("""
243
+ ### How to Use:
244
+ 1. Enter Title
245
+ 2. Add Workout Names (comma separated)
246
+ 3. Upload Images/Screenshots (optional - placeholders for missing)
247
+ 4. Get PDF + Video with generated templates!
248
+
249
+ Tip: Uploaded images get auto-converted to gym screenshot templates!
250
  """)
251
 
252
+ main_title = gr.Textbox(label="MAIN PROGRAM TITLE *")
253
+ workout_names = gr.Textbox(label="WORKOUT NAMES * (Comma separated)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
 
255
+ images_input = gr.File(label="Images/Screenshots (Optional)", file_count="multiple", file_types=["image"])
256
 
257
+ generate_btn = gr.Button("Generate")
 
 
258
 
259
+ pdf_output = gr.File(label="PDF")
260
+ video_output = gr.File(label="HD Video")
261
+ status = gr.Markdown("Ready...")
262
 
263
  generate_btn.click(
264
+ fn=generate_content,
265
  inputs=[main_title, workout_names, images_input],
266
  outputs=[pdf_output, video_output, status]
267
  )