lokesh341 commited on
Commit
8e263bd
·
verified ·
1 Parent(s): e00674c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -133
app.py CHANGED
@@ -1,19 +1,17 @@
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,35 +19,30 @@ class GymWorkoutPDF(FPDF):
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
 
@@ -62,14 +55,14 @@ def generate_gym_content(main_title, workout_names, image_files):
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)
@@ -82,18 +75,16 @@ def generate_gym_content(main_title, workout_names, image_files):
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
@@ -108,137 +99,70 @@ def generate_gym_content(main_title, workout_names, image_files):
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 = 30 # 30 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
  )
 
1
+ # app.py - Gym Workout Plan PDF + Video (Fixed Errors)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
5
  import os
6
  import re
7
  from datetime import datetime
 
8
 
9
+ # Video generation
10
  try:
11
  from moviepy.editor import ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip
12
  VIDEO_ENABLED = True
13
  except ImportError:
14
  VIDEO_ENABLED = False
 
15
 
16
  class GymWorkoutPDF(FPDF):
17
  def footer(self):
 
19
  self.set_font('Arial', 'I', 7)
20
  self.cell(0, 8, f'GYM WORKOUT PLAN | Page {self.page_no()}', 0, 0, 'C')
21
 
22
+ def clean_text(text, max_length=45):
 
23
  if not text:
24
  return "Workout"
25
  text = str(text)
 
 
26
  emoji_pattern = re.compile(
27
  "["
28
  u"\U0001F600-\U0001F64F"
29
+ u"\U0001F300-\U0001F5FF"
30
  u"\U0001F680-\U0001F6FF"
31
  u"\U0001F1E0-\U0001F1FF"
32
  "]+",
33
  flags=re.UNICODE
34
  )
35
  text = emoji_pattern.sub('', text)
 
36
  replacements = {
37
  '–': '-', '—': '-', '“': '"', '”': '"', '•': '*', '°': 'deg'
38
  }
39
  for old, new in replacements.items():
40
  text = text.replace(old, new)
 
41
  text = re.sub(r'[^\x00-\x7F\u0020-\u007E]', '', text)
42
  text = re.sub(r'\s+', ' ', text.strip())
43
  return text[:max_length]
44
 
45
+ def generate_content(main_title, workout_names, image_files):
46
  if not main_title.strip():
47
  return None, None, "Error: Main Title Required!"
48
 
 
55
  return None, None, "Error: Max 30 Workouts!"
56
 
57
  if not image_files or len(names_list) != len(image_files):
58
+ return None, None, f"Error: Number of workout names ({len(names_list)}) must match images ({len(image_files)})!"
59
 
60
  try:
61
+ clean_main_title = clean_text(main_title, 50)
62
  pdf = GymWorkoutPDF()
63
  pdf.set_margins(18, 18, 18)
64
 
65
+ # PDF
66
  pdf.add_page()
67
  pdf.set_font('Arial', 'B', 20)
68
  pdf.set_text_color(0, 128, 0)
 
75
  pdf.cell(0, 10, f"TOTAL WORKOUTS: {len(names_list)}", ln=1, align='C')
76
  pdf.cell(0, 10, f"CREATED: {datetime.now().strftime('%d/%m/%Y')}", ln=1, align='C')
77
 
 
78
  for i, (workout_name, img_path) in enumerate(zip(names_list, image_files)):
79
  pdf.add_page()
80
  pdf.set_font('Arial', 'B', 16)
81
  pdf.set_text_color(0, 128, 0)
82
  pdf.cell(0, 14, f"WORKOUT {i+1}", ln=1, align='C')
83
 
84
+ clean_name = clean_text(workout_name, 40)
85
  pdf.set_font('Arial', 'B', 14)
 
86
  pdf.multi_cell(0, 12, clean_name.upper(), align='C')
87
 
 
88
  if os.path.exists(img_path):
89
  try:
90
  img_width = 110
 
99
  pdf_path = f"workout_plan_{timestamp}.pdf"
100
  pdf.output(pdf_path)
101
 
102
+ # VIDEO
103
  video_path = None
104
+ if VIDEO_ENABLED:
105
+ clips = []
106
+ hd_width, hd_height = 1920, 1080
107
+ duration_per_slide = 5
108
+
109
+ # Title slide
110
+ title_text = TextClip(
111
+ f"GYM WORKOUT PROGRAM\n{clean_main_title.upper()}",
112
+ fontsize=80, color='white', font='Arial-Bold',
113
+ size=(hd_width, 300), bg_color='darkgreen'
114
+ ).set_duration(3).set_position('center')
115
+ title_clip = CompositeVideoClip([title_text], size=(hd_width, hd_height))
116
+ clips.append(title_clip)
117
+
118
+ # Workout slides
119
+ for i, (workout_name, img_path) in enumerate(zip(names_list, image_files)):
120
+ clean_name = clean_text(workout_name, 40)
121
+ img_clip = ImageClip(img_path).resize(height=hd_height-200)
122
+ img_clip = img_clip.set_position('center').set_duration(duration_per_slide)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ text_clip = TextClip(
125
+ f"WORKOUT {i+1}\n{clean_name.upper()}",
126
+ fontsize=60, color='white', font='Arial-Bold',
127
+ size=(hd_width-400, 200), bg_color='rgba(0,128,0,0.7)'
128
+ ).set_position(('center', hd_height-250)).set_duration(duration_per_slide)
 
 
 
 
 
 
129
 
130
+ slide = CompositeVideoClip([img_clip, text_clip], size=(hd_width, hd_height))
131
+ clips.append(slide)
132
+
133
+ video = concatenate_videoclips(clips, method="compose")
134
+ video_path = f"workout_video_{timestamp}.mp4"
135
+ video.write_videofile(
136
+ video_path,
137
+ fps=24,
138
+ codec='libx264',
139
+ audio=False
140
+ )
141
 
142
+ success_msg = f"SUCCESS! Generated PDF and {'Video' if video_path else 'No Video (Install MoviePy)'} for {len(names_list)} workouts!"
143
  return pdf_path, video_path, success_msg
144
 
145
  except Exception as e:
146
  return None, None, f"Error: {str(e)}"
147
 
148
+ # UI
149
+ with gr.Blocks(title="Gym Workout Generator") as demo:
150
+ gr.Markdown("# Gym Workout Generator\nPDF + HD Video | No Errors")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
  with gr.Row():
153
+ main_title = gr.Textbox(label="MAIN TITLE *")
154
+ workout_names = gr.Textbox(label="WORKOUT NAMES * (Comma separated)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ images_input = gr.File(label="Images * (Match workout count)", file_count="multiple", file_types=["image"])
157
 
158
+ generate_btn = gr.Button("Generate")
 
 
 
 
159
 
160
+ pdf_output = gr.File(label="PDF")
161
+ video_output = gr.File(label="HD Video")
162
+ status = gr.Markdown("Ready...")
163
 
164
  generate_btn.click(
165
+ fn=generate_content,
166
  inputs=[main_title, workout_names, images_input],
167
  outputs=[pdf_output, video_output, status]
168
  )