lokesh341 commited on
Commit
fef25f1
ยท
verified ยท
1 Parent(s): 89973db

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -64
app.py CHANGED
@@ -1,4 +1,4 @@
1
- # app.py - Fast & Reliable PDF Generator (Fixed ByteArray Error)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
@@ -6,7 +6,8 @@ import os
6
  import tempfile
7
  import re
8
  from datetime import datetime
9
- from pathlib import Path
 
10
 
11
  class SimplePDF(FPDF):
12
  def footer(self):
@@ -15,53 +16,48 @@ class SimplePDF(FPDF):
15
  self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
16
 
17
  def clean_text(text, max_length=100):
18
- """Clean text for PDF compatibility"""
19
  if not text:
20
  return ""
21
-
22
- # Replace problematic Unicode characters
23
  replacements = {
24
  'โ€“': '-', 'โ€”': '-', 'โ€œ': '"', 'โ€': '"', 'โ€˜': "'", 'โ€™': "'",
25
  'โ€ข': '-', 'ยฐ': 'ยฐ', 'โ„ข': 'TM', 'ยฎ': 'R', 'ยฉ': 'C',
26
  'โ‚ฌ': 'EUR', 'ยฃ': 'GBP', 'ยฅ': 'JPY'
27
  }
28
-
29
  for old, new in replacements.items():
30
  text = text.replace(old, new)
31
-
32
- # Remove other problematic characters
33
  text = re.sub(r'[^\w\s\-.,!?\(\)\[\]:;0-9ยฐ]', '', text)
34
  text = ' '.join(text.split())[:max_length]
35
  return text.strip()
36
 
37
- def generate_pdf(title, plan, names, image_files):
38
- # Fast validation
39
  if not title or not title.strip():
40
- return None, "โŒ Title required!"
41
-
42
- if not names or not names.strip():
43
- return None, "โŒ Names required!"
44
 
45
  names_list = [n.strip() for n in names.split(',') if n.strip()]
46
 
47
  if not names_list:
48
- return None, "โŒ At least one name needed!"
49
 
50
  if not image_files:
51
- return None, "โŒ Images required!"
52
 
53
  if len(names_list) != len(image_files):
54
- return None, f"โŒ {len(names_list)} names vs {len(image_files)} images"
 
 
 
 
55
 
56
  try:
 
57
  pdf = SimplePDF()
58
  pdf.set_margins(15, 15, 15)
59
 
60
- # Clean inputs
61
  clean_title = clean_text(title)
62
  clean_plan = clean_text(plan, 400)
63
 
64
- # Title Page
65
  pdf.add_page()
66
  pdf.set_font('Arial', 'B', 20)
67
  pdf.cell(0, 15, clean_title, ln=1, align='C')
@@ -76,10 +72,8 @@ def generate_pdf(title, plan, names, image_files):
76
  pdf.cell(0, 8, f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}", ln=1, align='C')
77
  pdf.cell(0, 8, f"Participants: {len(names_list)}", ln=1, align='C')
78
 
79
- # Participant pages
80
  for i, (name, img_path) in enumerate(zip(names_list, image_files)):
81
  pdf.add_page()
82
-
83
  clean_name = clean_text(name, 40)
84
  pdf.set_font('Arial', 'B', 16)
85
  pdf.cell(0, 12, clean_name.upper(), ln=1, align='C')
@@ -87,7 +81,6 @@ def generate_pdf(title, plan, names, image_files):
87
  pdf.set_font('Arial', '', 11)
88
  pdf.cell(0, 8, f"ID: {i+1}/{len(names_list)}", ln=1, align='C')
89
 
90
- # Add image safely
91
  image_added = False
92
  if os.path.exists(img_path):
93
  try:
@@ -99,74 +92,151 @@ def generate_pdf(title, plan, names, image_files):
99
  pass
100
 
101
  if not image_added:
102
- # Placeholder if image fails
103
  pdf.set_font('Arial', '', 12)
104
  pdf.cell(0, 10, "[IMAGE PLACEHOLDER]", ln=1, align='C')
105
- pdf.set_draw_color(200, 200, 200)
106
- pdf.rect(50, 50, 100, 100, 'D')
 
 
 
 
 
 
 
 
 
107
 
108
- # Add name below image
109
- pdf.ln(120)
110
- pdf.set_font('Arial', '', 10)
111
- pdf.cell(0, 8, f"Name: {clean_name}", ln=1, align='C')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # FIXED: Direct file output (no BytesIO complications)
114
- output_path = f"output_{int(datetime.now().timestamp())}.pdf"
115
- pdf.output(output_path)
116
 
117
- success_msg = f"โœ… SUCCESS!\n๐Ÿ“„ {clean_title}\n๐Ÿ‘ฅ {len(names_list)} participants"
118
- return output_path, success_msg
119
 
120
  except Exception as e:
121
- return None, f"โŒ Error: {str(e)}"
122
 
123
- # Fast & Clean UI
124
- with gr.Blocks(title="Fast PDF Generator") as demo:
125
- gr.Markdown("# ๐Ÿš€ Fast PDF Generator")
126
- gr.Markdown("**Title + Plan + Names + Images โ†’ Professional PDF**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
- with gr.Row():
129
  with gr.Column(scale=2):
130
  title_input = gr.Textbox(
131
- label="๐Ÿ“› Title *",
132
- placeholder="Document Title",
133
- lines=1
 
134
  )
135
  plan_input = gr.Textbox(
136
- label="๐Ÿ“‹ Plan (optional)",
137
- placeholder="Description or plan details",
138
- lines=3
139
  )
140
  names_input = gr.Textbox(
141
- label="๐Ÿ‘ฅ Names *",
142
- placeholder="Name1, Name2, Name3",
143
- lines=2
144
  )
145
-
146
- with gr.Column(scale=1):
147
  images_input = gr.File(
148
- label="๐Ÿ–ผ๏ธ Images *",
149
  file_count="multiple",
150
- file_types=[".jpg", ".jpeg", ".png"]
 
 
 
 
 
 
 
 
 
151
  )
 
 
152
 
153
- with gr.Row():
154
- generate_btn = gr.Button("๐Ÿš€ GENERATE PDF", variant="primary")
155
- clear_btn = gr.Button("๐Ÿ—‘๏ธ Clear", variant="secondary")
156
 
157
- status_output = gr.Markdown("Ready...")
158
- pdf_output = gr.File(label="๐Ÿ“ฅ Download PDF")
159
 
160
- # Events
161
  generate_btn.click(
162
- fn=generate_pdf,
163
- inputs=[title_input, plan_input, names_input, images_input],
164
- outputs=[pdf_output, status_output]
165
  )
166
 
167
  clear_btn.click(
168
- fn=lambda: ("", "", "", None, "Cleared!"),
169
- outputs=[title_input, plan_input, names_input, images_input, status_output]
170
  )
171
 
172
  if __name__ == "__main__":
 
1
+ # app.py - Enhanced PDF & Video Generator (HD Video with Names Below Images)
2
 
3
  import gradio as gr
4
  from fpdf import FPDF
 
6
  import tempfile
7
  import re
8
  from datetime import datetime
9
+ from moviepy.editor import ImageClip, concatenate_videoclips, TextClip, CompositeVideoClip
10
+ from PIL import Image # For image processing if needed
11
 
12
  class SimplePDF(FPDF):
13
  def footer(self):
 
16
  self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
17
 
18
  def clean_text(text, max_length=100):
19
+ """Clean text for PDF and video compatibility"""
20
  if not text:
21
  return ""
 
 
22
  replacements = {
23
  'โ€“': '-', 'โ€”': '-', 'โ€œ': '"', 'โ€': '"', 'โ€˜': "'", 'โ€™': "'",
24
  'โ€ข': '-', 'ยฐ': 'ยฐ', 'โ„ข': 'TM', 'ยฎ': 'R', 'ยฉ': 'C',
25
  'โ‚ฌ': 'EUR', 'ยฃ': 'GBP', 'ยฅ': 'JPY'
26
  }
 
27
  for old, new in replacements.items():
28
  text = text.replace(old, new)
 
 
29
  text = re.sub(r'[^\w\s\-.,!?\(\)\[\]:;0-9ยฐ]', '', text)
30
  text = ' '.join(text.split())[:max_length]
31
  return text.strip()
32
 
33
+ def generate_outputs(title, plan, names, image_files, duration_per_slide):
34
+ # Validation
35
  if not title or not title.strip():
36
+ return None, None, "โŒ Title required!"
 
 
 
37
 
38
  names_list = [n.strip() for n in names.split(',') if n.strip()]
39
 
40
  if not names_list:
41
+ return None, None, "โŒ At least one name needed!"
42
 
43
  if not image_files:
44
+ return None, None, "โŒ Images required!"
45
 
46
  if len(names_list) != len(image_files):
47
+ return None, None, f"โŒ {len(names_list)} names vs {len(image_files)} images"
48
+
49
+ # Limit to 30 items max
50
+ if len(names_list) > 30:
51
+ return None, None, "โŒ Max 30 items allowed!"
52
 
53
  try:
54
+ # PDF Generation
55
  pdf = SimplePDF()
56
  pdf.set_margins(15, 15, 15)
57
 
 
58
  clean_title = clean_text(title)
59
  clean_plan = clean_text(plan, 400)
60
 
 
61
  pdf.add_page()
62
  pdf.set_font('Arial', 'B', 20)
63
  pdf.cell(0, 15, clean_title, ln=1, align='C')
 
72
  pdf.cell(0, 8, f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M')}", ln=1, align='C')
73
  pdf.cell(0, 8, f"Participants: {len(names_list)}", ln=1, align='C')
74
 
 
75
  for i, (name, img_path) in enumerate(zip(names_list, image_files)):
76
  pdf.add_page()
 
77
  clean_name = clean_text(name, 40)
78
  pdf.set_font('Arial', 'B', 16)
79
  pdf.cell(0, 12, clean_name.upper(), ln=1, align='C')
 
81
  pdf.set_font('Arial', '', 11)
82
  pdf.cell(0, 8, f"ID: {i+1}/{len(names_list)}", ln=1, align='C')
83
 
 
84
  image_added = False
85
  if os.path.exists(img_path):
86
  try:
 
92
  pass
93
 
94
  if not image_added:
 
95
  pdf.set_font('Arial', '', 12)
96
  pdf.cell(0, 10, "[IMAGE PLACEHOLDER]", ln=1, align='C')
97
+
98
+ pdf_path = f"pdf_{int(datetime.now().timestamp())}.pdf"
99
+ pdf.output(pdf_path)
100
+
101
+ # Video Generation - Full HD 1920x1080
102
+ clips = []
103
+ hd_width, hd_height = 1920, 1080
104
+ text_height = 150 # Space for name below image
105
+
106
+ for i, (name, img_path) in enumerate(zip(names_list, image_files)):
107
+ clean_name = clean_text(name, 40)
108
 
109
+ # Load image
110
+ if os.path.exists(img_path):
111
+ try:
112
+ # Create image clip
113
+ img_clip = ImageClip(img_path).resize(height=hd_height - text_height - 50) # Resize to fit above text
114
+ img_clip = img_clip.set_position(("center", 25)) # Position at top with margin
115
+
116
+ # Text clip for name below
117
+ text_clip = TextClip(
118
+ f"{clean_name.upper()}\nID: {i+1}",
119
+ fontsize=80, color='white', font='Arial-Bold',
120
+ size=(hd_width, text_height), bg_color='black', method='caption'
121
+ ).set_position(("center", hd_height - text_height - 25))
122
+
123
+ # Composite
124
+ composite = CompositeVideoClip([img_clip, text_clip], size=(hd_width, hd_height))
125
+ composite = composite.set_duration(duration_per_slide)
126
+ clips.append(composite)
127
+ except Exception as e:
128
+ # Fallback placeholder clip
129
+ text_clip = TextClip(
130
+ f"{clean_name.upper()}\nID: {i+1}\n[Image Not Available]",
131
+ fontsize=60, color='white', bg_color='black'
132
+ ).set_duration(duration_per_slide)
133
+ clips.append(text_clip)
134
+ else:
135
+ # Placeholder if no image
136
+ text_clip = TextClip(
137
+ f"{clean_name.upper()}\nID: {i+1}\n[No Image]",
138
+ fontsize=60, color='white', bg_color='black'
139
+ ).set_duration(duration_per_slide)
140
+ clips.append(text_clip)
141
+
142
+ # Concatenate all clips into video
143
+ video = concatenate_videoclips(clips, method="compose")
144
+
145
+ # Add title intro slide if title
146
+ if clean_title:
147
+ title_clip = TextClip(
148
+ clean_title.upper(),
149
+ fontsize=100, color='white', bg_color='blue',
150
+ size=(hd_width, hd_height)
151
+ ).set_duration(3) # 3 sec intro
152
+ video = concatenate_videoclips([title_clip, video])
153
 
154
+ video_path = f"video_{int(datetime.now().timestamp())}.mp4"
155
+ video.write_videofile(video_path, fps=24, codec='libx264', audio=False) # HD silent video
 
156
 
157
+ success_msg = f"โœ… Generated!\n๐Ÿ“„ PDF & ๐ŸŽฅ HD Video (1920x1080)\n๐Ÿ‘ฅ {len(names_list)} items (IDs 1 to {len(names_list)})"
158
+ return pdf_path, video_path, success_msg
159
 
160
  except Exception as e:
161
+ return None, None, f"โŒ Error: {str(e)}"
162
 
163
+ # Enhanced UI/UX with Better Layout & Instructions
164
+ with gr.Blocks(
165
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="gray", neutral_hue="slate"),
166
+ css="""
167
+ .gradio-container {max-width: 1200px; margin: auto; padding: 20px;}
168
+ .input-label {font-weight: bold; font-size: 1.2em;}
169
+ .output-section {margin-top: 20px; padding: 15px; background: #f0f4f8; border-radius: 10px;}
170
+ .status {font-size: 1.1em; color: #333;}
171
+ """
172
+ ) as demo:
173
+ gr.Markdown(
174
+ """
175
+ # ๐Ÿš€ HD PDF & Video Generator
176
+ **Create Professional PDF & Full HD Video Slideshow**
177
+
178
+ ### Instructions:
179
+ - **Title**: Document/Video title (required)
180
+ - **Plan**: Optional description
181
+ - **Names**: Comma-separated (e.g., "John Doe, Jane Smith") - Max 30
182
+ - **Images**: Upload matching images (uploaded below names input for UX)
183
+ - **Slide Duration**: Seconds per slide in video
184
+ - Video: Full HD 1920x1080, name below image, IDs 1 to N
185
+ - Click Generate for PDF & Video downloads
186
+ """
187
+ )
188
 
189
+ with gr.Row(equal_height=True):
190
  with gr.Column(scale=2):
191
  title_input = gr.Textbox(
192
+ label="๐Ÿ“› Title (Required)",
193
+ placeholder="Enter title here...",
194
+ lines=1,
195
+ elem_classes="input-label"
196
  )
197
  plan_input = gr.Textbox(
198
+ label="๐Ÿ“‹ Plan/Description (Optional)",
199
+ placeholder="Enter details here...",
200
+ lines=4
201
  )
202
  names_input = gr.Textbox(
203
+ label="๐Ÿ‘ฅ Names (Comma-Separated, Required)",
204
+ placeholder="Name1, Name2, Name3 (up to 30)",
205
+ lines=3
206
  )
 
 
207
  images_input = gr.File(
208
+ label="๐Ÿ–ผ๏ธ Upload Images (Match Names Count)",
209
  file_count="multiple",
210
+ file_types=["image"]
211
+ )
212
+
213
+ with gr.Column(scale=1):
214
+ duration_input = gr.Slider(
215
+ minimum=1,
216
+ maximum=30,
217
+ step=1,
218
+ value=5,
219
+ label="โฑ๏ธ Duration per Slide (Seconds)"
220
  )
221
+ generate_btn = gr.Button("๐Ÿš€ Generate PDF & Video", variant="primary", size="lg")
222
+ clear_btn = gr.Button("๐Ÿ—‘๏ธ Clear All", variant="secondary")
223
 
224
+ with gr.Row(elem_classes="output-section"):
225
+ pdf_output = gr.File(label="๐Ÿ“„ Download PDF")
226
+ video_output = gr.File(label="๐ŸŽฅ Download HD Video (MP4)")
227
 
228
+ status_output = gr.Markdown("Ready to generate...", elem_classes="status")
 
229
 
230
+ # Event handlers
231
  generate_btn.click(
232
+ fn=generate_outputs,
233
+ inputs=[title_input, plan_input, names_input, images_input, duration_input],
234
+ outputs=[pdf_output, video_output, status_output]
235
  )
236
 
237
  clear_btn.click(
238
+ fn=lambda: ("", "", "", None, 5, None, None, "Cleared!"),
239
+ outputs=[title_input, plan_input, names_input, images_input, duration_input, pdf_output, video_output, status_output]
240
  )
241
 
242
  if __name__ == "__main__":