lokesh341 commited on
Commit
6fef8b3
Β·
verified Β·
1 Parent(s): a9f2340

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -156
app.py CHANGED
@@ -1,10 +1,19 @@
1
- # app.py - Gym Workout Plan PDF (No Emoji Errors - Text Only)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
5
  import os
6
  import re
7
  from datetime import datetime
 
 
 
 
 
 
 
 
 
8
 
9
  class GymWorkoutPDF(FPDF):
10
  def footer(self):
@@ -12,266 +21,226 @@ class GymWorkoutPDF(FPDF):
12
  self.set_font('Arial', 'I', 7)
13
  self.cell(0, 8, f'GYM WORKOUT PLAN | Page {self.page_no()}', 0, 0, 'C')
14
 
15
- def clean_text_for_pdf(text, max_length=45):
16
- """Clean text - NO EMOJIS, only ASCII + basic Unicode"""
17
  if not text:
18
  return "Workout"
19
  text = str(text)
20
 
21
- # Remove ALL emojis and unsupported Unicode
22
  emoji_pattern = re.compile(
23
  "["
24
- u"\U0001F600-\U0001F64F" # emoticons
25
- u"\U0001F300-\U0001F5FF" # symbols & pictographs
26
- u"\U0001F680-\U0001F6FF" # transport & map symbols
27
- u"\U0001F1E0-\U0001F1FF" # flags
28
- u"\U00002702-\U000027B0"
29
- u"\U000024C2-\U0001F251"
30
  "]+",
31
  flags=re.UNICODE
32
  )
33
  text = emoji_pattern.sub('', text)
34
 
35
- # Basic replacements
36
  replacements = {
37
- '–': '-', 'β€”': '-', 'β€œ': '"', '”': '"', 'β€˜': "'", '’': "'",
38
- 'β€’': '*', 'Β°': 'deg', 'Γ—': 'x', 'βœ“': 'OK'
39
  }
40
  for old, new in replacements.items():
41
  text = text.replace(old, new)
42
 
43
- # Keep only safe ASCII + basic chars
44
- text = re.sub(r'[^\x00-\x7F\u0020-\u007E\u00A0-\u00FF]', '', text)
45
  text = re.sub(r'\s+', ' ', text.strip())
46
  return text[:max_length]
47
 
48
- def generate_workout_pdf(main_title, workout_names, image_files):
49
  if not main_title.strip():
50
- return None, "Error: Main Title Required!"
51
 
52
  names_list = [n.strip() for n in workout_names.split(',') if n.strip()]
53
 
54
  if not names_list:
55
- return None, "Error: Add Workout Names!"
56
 
57
  if len(names_list) > 30:
58
- return None, "Error: Max 30 Workouts!"
59
 
60
  if not image_files or len(names_list) != len(image_files):
61
- return None, f"Error: Need {len(names_list)} Workout Images!"
62
 
63
  try:
 
64
  pdf = GymWorkoutPDF()
65
  pdf.set_margins(18, 18, 18)
66
- pdf.set_auto_page_break(auto=True, margin=18)
67
-
68
- clean_main_title = clean_text_for_pdf(main_title, 50)
69
 
70
- # MAIN TITLE PAGE - NO EMOJIS
71
  pdf.add_page()
72
  pdf.set_font('Arial', 'B', 20)
73
  pdf.set_text_color(0, 128, 0)
74
  pdf.cell(0, 18, "GYM WORKOUT PROGRAM", ln=1, align='C')
75
-
76
  pdf.set_font('Arial', 'B', 16)
77
  pdf.cell(0, 15, clean_main_title.upper(), ln=1, align='C')
78
 
79
  pdf.ln(15)
80
- pdf.set_font('Arial', 'B', 12)
81
- pdf.cell(0, 10, "==================================================", ln=1, align='C')
82
  pdf.set_font('Arial', '', 11)
83
  pdf.cell(0, 10, f"TOTAL WORKOUTS: {len(names_list)}", ln=1, align='C')
84
- pdf.cell(0, 10, f"CREATED: {datetime.now().strftime('%d/%m/%Y %H:%M')}", ln=1, align='C')
85
- pdf.set_font('Arial', 'B', 12)
86
- pdf.cell(0, 10, "==================================================", ln=1, align='C')
87
 
88
- # WORKOUT PAGES
89
  for i, (workout_name, img_path) in enumerate(zip(names_list, image_files)):
90
  pdf.add_page()
91
-
92
- # WORKOUT NUMBER + NAME
93
  pdf.set_font('Arial', 'B', 16)
94
  pdf.set_text_color(0, 128, 0)
95
  pdf.cell(0, 14, f"WORKOUT {i+1}", ln=1, align='C')
96
 
97
  pdf.set_font('Arial', 'B', 14)
98
- clean_workout_name = clean_text_for_pdf(workout_name, 40)
99
- pdf.multi_cell(0, 12, clean_workout_name.upper(), align='C')
100
-
101
- # WORKOUT IMAGE
102
- pdf.ln(10)
103
- img_width = 110
104
- img_height = 130
105
- page_width = pdf.w - 36
106
- x_pos = (page_width - img_width) / 2 + 18
107
 
108
- image_success = False
109
  if os.path.exists(img_path):
110
  try:
111
- pdf.image(img_path, x=x_pos, y=pdf.get_y(), w=img_width, h=img_height)
112
- image_success = True
 
113
  pdf.set_draw_color(0, 128, 0)
114
- pdf.set_line_width(0.5)
115
- pdf.rect(x_pos, pdf.get_y(), img_width, img_height, 'D')
116
  except:
117
  pass
118
-
119
- if not image_success:
120
- pdf.set_fill_color(240, 248, 240)
121
- pdf.set_draw_color(0, 128, 0)
122
- pdf.rect(x_pos, pdf.get_y(), img_width, img_height, 'FD')
123
- pdf.set_font('Arial', 'B', 11)
124
- pdf.set_text_color(0, 128, 0)
125
- pdf.set_xy(x_pos + 25, pdf.get_y() + 50)
126
- pdf.cell(img_width - 50, 10, "WORKOUT", ln=1, align='C')
127
- pdf.set_xy(x_pos + 25, pdf.get_y() + 70)
128
- pdf.cell(img_width - 50, 10, f"IMAGE {i+1}", ln=1, align='C')
129
-
130
- # WORKOUT DETAILS - NO EMOJIS
131
- pdf.set_y(pdf.get_y() + img_height + 15)
132
- pdf.set_font('Arial', 'B', 12)
133
- pdf.set_text_color(0, 128, 0)
134
- pdf.cell(0, 10, "WORKOUT EXECUTION PLAN", ln=1, align='C')
135
-
136
- pdf.set_draw_color(0, 128, 0)
137
- pdf.line(18, pdf.get_y(), pdf.w - 18, pdf.get_y())
138
- pdf.ln(8)
139
-
140
- pdf.set_font('Arial', '', 9)
141
- pdf.set_text_color(60, 60, 60)
142
-
143
- details = [
144
- f"EXERCISES FOR {clean_workout_name.upper()}:",
145
- "SETS x REPS: ________________________________",
146
- "REST PERIODS: ________________________________",
147
- "INTENSITY LEVEL: ____________________________",
148
- "HYDRATION BREAKS: ___________________________",
149
- "PROGRESS TRACKING: __________________________",
150
- "EQUIPMENT NEEDED: ___________________________"
151
- ]
152
-
153
- for detail in details:
154
- pdf.ln(6)
155
- pdf.cell(0, 8, detail, ln=1, align='L')
156
-
157
- pdf.ln(12)
158
- pdf.set_font('Arial', 'B', 11)
159
- pdf.set_text_color(0, 128, 0)
160
- pdf.cell(0, 10, f"WORKOUT {i+1}: {clean_workout_name.upper()}", ln=1, align='C')
161
- pdf.set_font('Arial', '', 8)
162
- pdf.cell(0, 8, "PRINT AND TRAIN HARD!", ln=1, align='C')
163
 
164
  timestamp = int(datetime.now().timestamp())
165
  pdf_path = f"workout_plan_{timestamp}.pdf"
166
  pdf.output(pdf_path)
167
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  success_msg = f"""
169
- SUCCESS! WORKOUT PLAN GENERATED!
170
- Main Title: {clean_main_title[:30]}...
171
- {len(names_list)} Workouts Generated
172
- Structure: Title Page -> Workout Names + Images
173
- Images: 110x130px | Names: 14pt
174
- {len(names_list) + 1} Pages - Ready for Gym!
175
  """
176
- return pdf_path, success_msg
 
177
 
178
  except Exception as e:
179
- return None, f"Error: {str(e)}"
180
 
181
- # SAFE UI - NO EMOJIS IN TEXT
182
  with gr.Blocks(
183
- title="Gym Workout Plan Generator",
184
  theme=gr.themes.Soft(primary_hue="green"),
185
  css="""
186
  .gradio-container {max-width: 1000px !important;}
187
- .header {background: linear-gradient(135deg, #16a34a, #15803d); color: white; padding: 25px; border-radius: 15px; text-align: center;}
188
- .input-box {background: #f0fdf4; padding: 18px; border-radius: 10px; border: 2px solid #dcfce7; margin: 10px 0;}
189
- .btn-generate {background: linear-gradient(45deg, #16a34a, #15803d) !important; font-size: 18px; padding: 12px;}
190
- .status {padding: 15px; border-radius: 10px; font-size: 15px; margin: 12px 0;}
191
- .success {background: #dcfce7; color: #166534; border-left: 5px solid #16a34a;}
192
- .workout-info {background: #ecfdf5; padding: 12px; border-radius: 8px; border-left: 4px solid #16a34a;}
193
- .preview-workouts {background: #f0fdf4; padding: 10px; border-radius: 6px; font-family: monospace;}
194
  """
195
  ) as demo:
196
 
197
  gr.HTML("""
198
  <div class="header">
199
- <h2>Gym Workout Plan Generator</h2>
200
- <p><strong>Main Title -> Workout Names -> Images | No Emoji Errors</strong></p>
201
  </div>
202
  """)
203
 
204
  gr.Markdown("""
205
- ### Structure:
206
- 1. FIRST: Main Program Title Page
207
- 2. NEXT: Individual Workout Pages
208
- - Workout 1 Name + Image
209
- - Workout 2 Name + Image
210
- - Workout 3 Name + Image...
211
  """)
212
 
213
- gr.HTML('<div class="workout-info">Tip: Enter workout names like "Chest Day", "Leg Day", "Back Workout" - NO EMOJIS</div>')
214
-
215
  with gr.Row():
216
  with gr.Column(scale=2):
217
- main_title_input = gr.Textbox(
218
  label="MAIN PROGRAM TITLE *",
219
  placeholder="12 Week Muscle Building Program",
220
  lines=2,
221
  elem_classes="input-box"
222
  )
223
 
224
- workout_names_input = gr.Textbox(
225
  label="WORKOUT NAMES * (Comma separated)",
226
- placeholder="Chest Day, Leg Day, Back Biceps, Cardio Session",
227
- lines=4,
228
  elem_classes="input-box"
229
  )
230
-
231
- workout_preview = gr.Markdown("", elem_classes="preview-workouts")
232
 
233
  with gr.Column(scale=1):
234
  images_input = gr.File(
235
  label="Workout Images *",
236
  file_count="multiple",
237
- file_types=[".jpg", ".jpeg", ".png", ".webp"],
238
  elem_classes="input-box"
239
  )
240
 
241
- with gr.Row():
242
- generate_btn = gr.Button("Generate Workout Plan PDF", variant="primary", elem_classes="btn-generate")
243
- clear_btn = gr.Button("Clear All", variant="secondary")
244
-
245
- pdf_output = gr.File(label="Download Workout Plan PDF", elem_classes="input-box")
246
- status_output = gr.Markdown("Ready to generate workout plan...", elem_classes="status success")
247
 
248
- # Events
249
- def preview_workouts(workout_names):
250
- names_list = [clean_text_for_pdf(n.strip()) for n in workout_names.split(',') if n.strip()]
251
- if names_list:
252
- preview = "**Workouts in PDF:**\n"
253
- for i, name in enumerate(names_list[:5], 1):
254
- preview += f"Workout {i}: {name}\n"
255
- if len(names_list) > 5:
256
- preview += f"... and {len(names_list)-5} more"
257
- return preview
258
- return "No workouts detected"
259
 
260
- workout_names_input.change(
261
- fn=preview_workouts,
262
- inputs=[workout_names_input],
263
- outputs=[workout_preview]
264
- )
265
 
266
  generate_btn.click(
267
- fn=generate_workout_pdf,
268
- inputs=[main_title_input, workout_names_input, images_input],
269
- outputs=[pdf_output, status_output]
270
- )
271
-
272
- clear_btn.click(
273
- fn=lambda: ("", "", None, "", "Form cleared!"),
274
- outputs=[main_title_input, workout_names_input, images_input, workout_preview, status_output]
275
  )
276
 
277
  if __name__ == "__main__":
 
1
+ # app.py - Gym Workout Plan PDF + HD Video Generator (MoviePy Fixed)
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
+
10
+ # Video generation with MoviePy (Fixed dependencies)
11
+ try:
12
+ from moviepy.editor import ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip
13
+ VIDEO_ENABLED = True
14
+ except ImportError:
15
+ VIDEO_ENABLED = False
16
+ print("MoviePy not available - Video generation disabled")
17
 
18
  class GymWorkoutPDF(FPDF):
19
  def footer(self):
 
21
  self.set_font('Arial', 'I', 7)
22
  self.cell(0, 8, f'GYM WORKOUT PLAN | Page {self.page_no()}', 0, 0, 'C')
23
 
24
+ def clean_text_for_pdf_video(text, max_length=45):
25
+ """Clean text for both PDF and video"""
26
  if not text:
27
  return "Workout"
28
  text = str(text)
29
 
30
+ # Remove emojis and unsupported chars
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
 
 
42
  replacements = {
43
+ '–': '-', 'β€”': '-', 'β€œ': '"', '”': '"', 'β€’': '*', 'Β°': 'deg'
 
44
  }
45
  for old, new in replacements.items():
46
  text = text.replace(old, new)
47
 
48
+ text = re.sub(r'[^\x00-\x7F\u0020-\u007E]', '', text)
 
49
  text = re.sub(r'\s+', ' ', text.strip())
50
  return text[:max_length]
51
 
52
+ def generate_gym_content(main_title, workout_names, image_files):
53
  if not main_title.strip():
54
+ return None, None, "Error: Main Title Required!"
55
 
56
  names_list = [n.strip() for n in workout_names.split(',') if n.strip()]
57
 
58
  if not names_list:
59
+ return None, None, "Error: Add Workout Names!"
60
 
61
  if len(names_list) > 30:
62
+ return None, None, "Error: Max 30 Workouts!"
63
 
64
  if not image_files or len(names_list) != len(image_files):
65
+ return None, None, f"Error: Need {len(names_list)} Images!"
66
 
67
  try:
68
+ clean_main_title = clean_text_for_pdf_video(main_title, 50)
69
  pdf = GymWorkoutPDF()
70
  pdf.set_margins(18, 18, 18)
 
 
 
71
 
72
+ # PDF GENERATION
73
  pdf.add_page()
74
  pdf.set_font('Arial', 'B', 20)
75
  pdf.set_text_color(0, 128, 0)
76
  pdf.cell(0, 18, "GYM WORKOUT PROGRAM", ln=1, align='C')
 
77
  pdf.set_font('Arial', 'B', 16)
78
  pdf.cell(0, 15, clean_main_title.upper(), ln=1, align='C')
79
 
80
  pdf.ln(15)
 
 
81
  pdf.set_font('Arial', '', 11)
82
  pdf.cell(0, 10, f"TOTAL WORKOUTS: {len(names_list)}", ln=1, align='C')
83
+ pdf.cell(0, 10, f"CREATED: {datetime.now().strftime('%d/%m/%Y')}", ln=1, align='C')
 
 
84
 
85
+ # Workout pages in PDF
86
  for i, (workout_name, img_path) in enumerate(zip(names_list, image_files)):
87
  pdf.add_page()
 
 
88
  pdf.set_font('Arial', 'B', 16)
89
  pdf.set_text_color(0, 128, 0)
90
  pdf.cell(0, 14, f"WORKOUT {i+1}", ln=1, align='C')
91
 
92
  pdf.set_font('Arial', 'B', 14)
93
+ clean_name = clean_text_for_pdf_video(workout_name, 40)
94
+ pdf.multi_cell(0, 12, clean_name.upper(), align='C')
 
 
 
 
 
 
 
95
 
96
+ # Image in PDF
97
  if os.path.exists(img_path):
98
  try:
99
+ img_width = 110
100
+ x_pos = (pdf.w - img_width) / 2
101
+ pdf.image(img_path, x=x_pos, y=pdf.get_y(), w=img_width, h=130)
102
  pdf.set_draw_color(0, 128, 0)
103
+ pdf.rect(x_pos, pdf.get_y(), img_width, 130, 'D')
 
104
  except:
105
  pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
  timestamp = int(datetime.now().timestamp())
108
  pdf_path = f"workout_plan_{timestamp}.pdf"
109
  pdf.output(pdf_path)
110
 
111
+ # VIDEO GENERATION (HD 1080p)
112
+ video_path = None
113
+ if VIDEO_ENABLED and image_files:
114
+ try:
115
+ clips = []
116
+ hd_width, hd_height = 1920, 1080
117
+ duration_per_slide = 5 # 5 seconds per workout
118
+
119
+ # Title slide
120
+ title_text = TextClip(
121
+ f"GYM WORKOUT PROGRAM\n{clean_main_title.upper()}",
122
+ fontsize=80, color='white', font='Arial-Bold',
123
+ size=(hd_width, 300), bg_color='darkgreen'
124
+ ).set_duration(3).set_position('center')
125
+ title_bg = ImageClip(image_files[0]).resize(height=hd_height).set_duration(3)
126
+ title_clip = CompositeVideoClip([title_bg, title_text])
127
+ clips.append(title_clip)
128
+
129
+ # Workout slides
130
+ for i, (workout_name, img_path) in enumerate(zip(names_list, image_files)):
131
+ clean_name = clean_text_for_pdf_video(workout_name, 40)
132
+
133
+ # Image background
134
+ img_clip = ImageClip(img_path).resize(height=hd_height-200)
135
+ img_clip = img_clip.set_position('center').set_duration(duration_per_slide)
136
+
137
+ # Workout text overlay
138
+ text_content = f"WORKOUT {i+1}\n{clean_name.upper()}"
139
+ text_clip = TextClip(
140
+ text_content,
141
+ fontsize=60, color='white', font='Arial-Bold',
142
+ size=(hd_width-400, 200), bg_color='rgba(0,128,0,0.7)'
143
+ ).set_position(('center', hd_height-250)).set_duration(duration_per_slide)
144
+
145
+ # Composite slide
146
+ slide = CompositeVideoClip([img_clip, text_clip], size=(hd_width, hd_height))
147
+ clips.append(slide)
148
+
149
+ # Generate final video
150
+ video = concatenate_videoclips(clips, method="compose")
151
+ video_path = f"workout_video_{timestamp}.mp4"
152
+ video.write_videofile(
153
+ video_path,
154
+ fps=24,
155
+ codec='libx264',
156
+ audio=False,
157
+ temp_audiofile='temp-audio.m4a',
158
+ remove_temp=True
159
+ )
160
+
161
+ except Exception as video_error:
162
+ print(f"Video generation failed: {video_error}")
163
+ video_path = None
164
+
165
  success_msg = f"""
166
+ SUCCESS! GYM CONTENT GENERATED!
167
+ PDF: {len(names_list) + 1} pages created
168
+ Video: {'HD 1080p generated!' if video_path else 'Disabled (MoviePy not available)'}
169
+ Workouts: {len(names_list)} total
170
+ Ready for gym use!
 
171
  """
172
+
173
+ return pdf_path, video_path, success_msg
174
 
175
  except Exception as e:
176
+ return None, None, f"Error: {str(e)}"
177
 
178
+ # UI with PDF + Video Output
179
  with gr.Blocks(
180
+ title="Gym Workout Generator",
181
  theme=gr.themes.Soft(primary_hue="green"),
182
  css="""
183
  .gradio-container {max-width: 1000px !important;}
184
+ .header {background: linear-gradient(135deg, #16a34a, #15803d); color: white; padding: 25px; border-radius: 15px;}
185
+ .input-box {background: #f0fdf4; padding: 18px; border-radius: 10px; border: 2px solid #dcfce7;}
186
+ .btn-generate {background: linear-gradient(45deg, #16a34a, #15803d) !important; font-size: 18px;}
187
+ .output-section {background: #f0fdf4; padding: 15px; border-radius: 10px; margin: 10px 0;}
 
 
 
188
  """
189
  ) as demo:
190
 
191
  gr.HTML("""
192
  <div class="header">
193
+ <h2>Gym Workout Generator</h2>
194
+ <p><strong>PDF + HD Video (1080p) | Professional Gym Content</strong></p>
195
  </div>
196
  """)
197
 
198
  gr.Markdown("""
199
+ ### Features:
200
+ - PDF: Professional workout plans with images
201
+ - Video: HD 1080p slideshow (5 sec per workout)
202
+ - Structure: Title β†’ Workout 1, 2, 3... with images
203
+ - No emoji errors - clean text only
 
204
  """)
205
 
 
 
206
  with gr.Row():
207
  with gr.Column(scale=2):
208
+ main_title = gr.Textbox(
209
  label="MAIN PROGRAM TITLE *",
210
  placeholder="12 Week Muscle Building Program",
211
  lines=2,
212
  elem_classes="input-box"
213
  )
214
 
215
+ workout_names = gr.Textbox(
216
  label="WORKOUT NAMES * (Comma separated)",
217
+ placeholder="Chest Day, Leg Day, Back Workout, Cardio",
218
+ lines=3,
219
  elem_classes="input-box"
220
  )
 
 
221
 
222
  with gr.Column(scale=1):
223
  images_input = gr.File(
224
  label="Workout Images *",
225
  file_count="multiple",
226
+ file_types=[".jpg", ".jpeg", ".png"],
227
  elem_classes="input-box"
228
  )
229
 
230
+ generate_btn = gr.Button("Generate PDF + HD Video", variant="primary", elem_classes="btn-generate")
 
 
 
 
 
231
 
232
+ with gr.Row():
233
+ with gr.Column():
234
+ pdf_output = gr.File(label="Download PDF", elem_classes="output-section")
235
+ with gr.Column():
236
+ video_output = gr.File(label="Download HD Video", elem_classes="output-section")
 
 
 
 
 
 
237
 
238
+ status = gr.Markdown("Ready to generate...", elem_classes="output-section")
 
 
 
 
239
 
240
  generate_btn.click(
241
+ fn=generate_gym_content,
242
+ inputs=[main_title, workout_names, images_input],
243
+ outputs=[pdf_output, video_output, status]
 
 
 
 
 
244
  )
245
 
246
  if __name__ == "__main__":