broadfield-dev commited on
Commit
f331adb
·
verified ·
1 Parent(s): 41632eb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -27
app.py CHANGED
@@ -1,21 +1,32 @@
1
- from flask import Flask, request, render_template, send_file
2
  import ast
3
  import io
4
  import zipfile
5
  from datetime import datetime
 
 
 
 
6
 
7
  app = Flask(__name__)
8
 
9
- # Mapping of Gradio components to Flask-compatible HTML inputs
10
  COMPONENT_MAP = {
11
  'gr.Textbox': {'html_type': 'input', 'attributes': {'type': 'text', 'class': 'form-control'}},
12
- 'gr.Slider': {'html_type': 'input', 'attributes': {'type': 'range', 'class': 'form-range'}},
13
  'gr.Image': {'html_type': 'input', 'attributes': {'type': 'file', 'accept': 'image/*', 'class': 'form-control'}},
14
  'gr.Dropdown': {'html_type': 'select', 'attributes': {'class': 'form-select'}},
15
  'gr.Checkbox': {'html_type': 'input', 'attributes': {'type': 'checkbox', 'class': 'form-check-input'}},
16
  'gr.Radio': {'html_type': 'input', 'attributes': {'type': 'radio', 'class': 'form-check-input'}},
17
  'gr.Label': {'html_type': 'div', 'attributes': {'class': 'output-label'}},
18
- # Add more mappings as needed for other Gradio components
 
 
 
 
 
 
 
19
  }
20
 
21
  def parse_gradio_script(script):
@@ -25,7 +36,6 @@ def parse_gradio_script(script):
25
 
26
  for node in ast.walk(tree):
27
  if isinstance(node, ast.Call) and hasattr(node.func, 'attr'):
28
- # Detect Interface or Blocks
29
  if node.func.attr == 'Interface':
30
  components['is_blocks'] = False
31
  for kw in node.keywords:
@@ -43,8 +53,6 @@ def parse_gradio_script(script):
43
  components['outputs'] = [ast.unparse(kw.value)]
44
  elif node.func.attr == 'Blocks':
45
  components['is_blocks'] = True
46
- # For Blocks, we'll need to parse the context (simplified here)
47
- # This assumes inputs/outputs are defined in event listeners
48
  for subnode in ast.walk(node):
49
  if isinstance(subnode, ast.Call) and hasattr(subnode.func, 'attr') and subnode.func.attr == 'click':
50
  components['function'] = ast.unparse(subnode.args[0])
@@ -59,6 +67,7 @@ def generate_flask_app(script, components):
59
  import io
60
  from PIL import Image
61
  import base64
 
62
 
63
  app = Flask(__name__)
64
 
@@ -82,6 +91,20 @@ def index():
82
  if file:
83
  img = Image.open(file)
84
  inputs.append(img)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  """
86
  else:
87
  flask_code += f"""
@@ -110,6 +133,7 @@ def index():
110
  .form-group { margin-bottom: 20px; }
111
  .btn-submit { width: 100%; }
112
  .output { margin-top: 20px; padding: 15px; background: #e9ecef; border-radius: 5px; }
 
113
  </style>
114
  </head>
115
  <body>
@@ -129,11 +153,14 @@ def index():
129
  <{comp_info['html_type']} name="input_{i}" id="input_{i}" {attrs}>
130
  """
131
  if comp_type == 'gr.Dropdown':
132
- # Simplified dropdown options (could be parsed from script)
133
  html_template += """
134
  <option value="option1">Option 1</option>
135
  <option value="option2">Option 2</option>
136
  </select>
 
 
 
 
137
  """
138
  else:
139
  html_template += f"""
@@ -162,6 +189,24 @@ def index():
162
  {% if result %}
163
  <img src="data:image/png;base64,{{ result|to_base64 }}" alt="Output Image" class="img-fluid">
164
  {% endif %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  """
166
  else:
167
  html_template += """
@@ -179,42 +224,91 @@ def index():
179
  </html>
180
  """
181
 
182
- # Add a filter for base64 encoding of images
183
  flask_code += """
184
  @app.template_filter('to_base64')
185
- def to_base64(img):
186
- if isinstance(img, Image.Image):
187
  buffered = io.BytesIO()
188
- img.save(buffered, format="PNG")
189
  return base64.b64encode(buffered.getvalue()).decode('utf-8')
190
- return str(img)
 
 
191
  """
192
 
193
- # Create a ZIP file with the Flask app
194
- buffer = io.BytesIO()
195
- with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
196
- zip_file.writestr('app.py', flask_code)
197
- zip_file.writestr('templates/index.html', html_template)
198
- zip_file.writestr('requirements.txt', 'flask\npillow')
199
- buffer.seek(0)
200
- return buffer
 
 
 
 
 
 
 
201
 
202
  @app.route('/', methods=['GET', 'POST'])
203
  def convert_gradio():
 
 
 
204
  if request.method == 'POST':
205
  gradio_script = request.form['gradio_script']
206
  try:
207
  components = parse_gradio_script(gradio_script)
208
- flask_app_zip = generate_flask_app(gradio_script, components)
209
- return send_file(
210
- flask_app_zip,
211
- mimetype='application/zip',
212
- as_attachment=True,
213
- download_name=f'flask_app_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  )
215
  except Exception as e:
216
  return render_template('index.html', error=str(e))
217
  return render_template('index.html')
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
  if __name__ == '__main__':
220
  app.run(host="0.0.0.0", port=7860, debug=True)
 
1
+ from flask import Flask, request, render_template, send_file, Response
2
  import ast
3
  import io
4
  import zipfile
5
  from datetime import datetime
6
+ import subprocess
7
+ import time
8
+ import os
9
+ import threading
10
 
11
  app = Flask(__name__)
12
 
13
+ # Expanded mapping of Gradio components to Flask-compatible HTML inputs
14
  COMPONENT_MAP = {
15
  'gr.Textbox': {'html_type': 'input', 'attributes': {'type': 'text', 'class': 'form-control'}},
16
+ 'gr.Slider': {'html_type': 'input', 'attributes': {'type': 'range', 'class': 'form-range', 'min': '0', 'max': '100'}},
17
  'gr.Image': {'html_type': 'input', 'attributes': {'type': 'file', 'accept': 'image/*', 'class': 'form-control'}},
18
  'gr.Dropdown': {'html_type': 'select', 'attributes': {'class': 'form-select'}},
19
  'gr.Checkbox': {'html_type': 'input', 'attributes': {'type': 'checkbox', 'class': 'form-check-input'}},
20
  'gr.Radio': {'html_type': 'input', 'attributes': {'type': 'radio', 'class': 'form-check-input'}},
21
  'gr.Label': {'html_type': 'div', 'attributes': {'class': 'output-label'}},
22
+ 'gr.Audio': {'html_type': 'input', 'attributes': {'type': 'file', 'accept': 'audio/*', 'class': 'form-control'}},
23
+ 'gr.Video': {'html_type': 'input', 'attributes': {'type': 'file', 'accept': 'video/*', 'class': 'form-control'}},
24
+ 'gr.File': {'html_type': 'input', 'attributes': {'type': 'file', 'class': 'form-control'}},
25
+ 'gr.Number': {'html_type': 'input', 'attributes': {'type': 'number', 'class': 'form-control'}},
26
+ 'gr.ColorPicker': {'html_type': 'input', 'attributes': {'type': 'color', 'class': 'form-control'}},
27
+ 'gr.Markdown': {'html_type': 'div', 'attributes': {'class': 'markdown-output'}}, # For static text output
28
+ 'gr.Dataframe': {'html_type': 'textarea', 'attributes': {'class': 'form-control', 'readonly': 'true'}}, # Simplified output
29
+ # Add more components as needed
30
  }
31
 
32
  def parse_gradio_script(script):
 
36
 
37
  for node in ast.walk(tree):
38
  if isinstance(node, ast.Call) and hasattr(node.func, 'attr'):
 
39
  if node.func.attr == 'Interface':
40
  components['is_blocks'] = False
41
  for kw in node.keywords:
 
53
  components['outputs'] = [ast.unparse(kw.value)]
54
  elif node.func.attr == 'Blocks':
55
  components['is_blocks'] = True
 
 
56
  for subnode in ast.walk(node):
57
  if isinstance(subnode, ast.Call) and hasattr(subnode.func, 'attr') and subnode.func.attr == 'click':
58
  components['function'] = ast.unparse(subnode.args[0])
 
67
  import io
68
  from PIL import Image
69
  import base64
70
+ import pandas as pd
71
 
72
  app = Flask(__name__)
73
 
 
91
  if file:
92
  img = Image.open(file)
93
  inputs.append(img)
94
+ """
95
+ elif comp_type == 'gr.Audio' or comp_type == 'gr.Video' or comp_type == 'gr.File':
96
+ flask_code += f"""
97
+ file = request.files.get('input_{i}')
98
+ if file:
99
+ inputs.append(file.read())
100
+ """
101
+ elif comp_type == 'gr.Checkbox':
102
+ flask_code += f"""
103
+ inputs.append(request.form.get('input_{i}') == 'on')
104
+ """
105
+ elif comp_type == 'gr.Number':
106
+ flask_code += f"""
107
+ inputs.append(float(request.form.get('input_{i}', 0)))
108
  """
109
  else:
110
  flask_code += f"""
 
133
  .form-group { margin-bottom: 20px; }
134
  .btn-submit { width: 100%; }
135
  .output { margin-top: 20px; padding: 15px; background: #e9ecef; border-radius: 5px; }
136
+ .markdown-output { white-space: pre-wrap; }
137
  </style>
138
  </head>
139
  <body>
 
153
  <{comp_info['html_type']} name="input_{i}" id="input_{i}" {attrs}>
154
  """
155
  if comp_type == 'gr.Dropdown':
 
156
  html_template += """
157
  <option value="option1">Option 1</option>
158
  <option value="option2">Option 2</option>
159
  </select>
160
+ """
161
+ elif comp_type == 'gr.Radio':
162
+ html_template += f"""
163
+ </{comp_info['html_type']}> <label class="form-check-label" for="input_{i}">Option</label>
164
  """
165
  else:
166
  html_template += f"""
 
189
  {% if result %}
190
  <img src="data:image/png;base64,{{ result|to_base64 }}" alt="Output Image" class="img-fluid">
191
  {% endif %}
192
+ """
193
+ elif comp_type == 'gr.Audio':
194
+ html_template += """
195
+ {% if result %}
196
+ <audio controls><source src="data:audio/wav;base64,{{ result|to_base64 }}" type="audio/wav"></audio>
197
+ {% endif %}
198
+ """
199
+ elif comp_type == 'gr.Video':
200
+ html_template += """
201
+ {% if result %}
202
+ <video controls><source src="data:video/mp4;base64,{{ result|to_base64 }}" type="video/mp4"></video>
203
+ {% endif %}
204
+ """
205
+ elif comp_type == 'gr.Dataframe':
206
+ html_template += """
207
+ {% if result %}
208
+ <pre>{{ result.to_html() if result.__class__.__name__ == 'DataFrame' else result }}</pre>
209
+ {% endif %}
210
  """
211
  else:
212
  html_template += """
 
224
  </html>
225
  """
226
 
227
+ # Add filters for base64 encoding
228
  flask_code += """
229
  @app.template_filter('to_base64')
230
+ def to_base64(data):
231
+ if isinstance(data, Image.Image):
232
  buffered = io.BytesIO()
233
+ data.save(buffered, format="PNG")
234
  return base64.b64encode(buffered.getvalue()).decode('utf-8')
235
+ elif isinstance(data, bytes):
236
+ return base64.b64encode(data).decode('utf-8')
237
+ return str(data)
238
  """
239
 
240
+ requirements = "flask\npillow\npandas\n"
241
+
242
+ return flask_code, html_template, requirements
243
+
244
+ def run_preview(flask_code, port=5001):
245
+ """Run the generated Flask app in a subprocess for preview."""
246
+ with open('temp_app.py', 'w') as f:
247
+ f.write(flask_code)
248
+ os.makedirs('templates', exist_ok=True)
249
+ with open('templates/index.html', 'w') as f:
250
+ f.write(html_template)
251
+
252
+ proc = subprocess.Popen(['python', 'temp_app.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
253
+ time.sleep(2) # Give it time to start
254
+ return proc, f'http://localhost:{port}'
255
 
256
  @app.route('/', methods=['GET', 'POST'])
257
  def convert_gradio():
258
+ global preview_process
259
+ preview_process = None
260
+
261
  if request.method == 'POST':
262
  gradio_script = request.form['gradio_script']
263
  try:
264
  components = parse_gradio_script(gradio_script)
265
+ flask_code, html_template, requirements = generate_flask_app(gradio_script, components)
266
+
267
+ # Create ZIP file
268
+ buffer = io.BytesIO()
269
+ with zipfile.ZipFile(buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
270
+ zip_file.writestr('app.py', flask_code)
271
+ zip_file.writestr('templates/index.html', html_template)
272
+ zip_file.writestr('requirements.txt', requirements)
273
+ buffer.seek(0)
274
+
275
+ # Start preview in a subprocess
276
+ preview_process, preview_url = run_preview(flask_code)
277
+
278
+ return render_template(
279
+ 'result.html',
280
+ flask_code=flask_code,
281
+ html_template=html_template,
282
+ requirements=requirements,
283
+ zip_buffer=buffer.getvalue(),
284
+ preview_url=preview_url
285
  )
286
  except Exception as e:
287
  return render_template('index.html', error=str(e))
288
  return render_template('index.html')
289
 
290
+ @app.route('/download/<filename>')
291
+ def download_file(filename):
292
+ if filename == 'app.py':
293
+ content = request.args.get('content')
294
+ return Response(content, mimetype='text/plain', headers={"Content-Disposition": f"attachment;filename={filename}"})
295
+ elif filename == 'index.html':
296
+ content = request.args.get('content')
297
+ return Response(content, mimetype='text/html', headers={"Content-Disposition": f"attachment;filename={filename}"})
298
+ elif filename == 'requirements.txt':
299
+ content = request.args.get('content')
300
+ return Response(content, mimetype='text/plain', headers={"Content-Disposition": f"attachment;filename={filename}"})
301
+ elif filename == 'zip':
302
+ buffer = io.BytesIO(request.args.get('content').encode())
303
+ buffer.seek(0)
304
+ return send_file(buffer, mimetype='application/zip', as_attachment=True, download_name=f'flask_app_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip')
305
+ return "File not found", 404
306
+
307
+ @app.teardown_appcontext
308
+ def cleanup(exc):
309
+ global preview_process
310
+ if preview_process:
311
+ preview_process.terminate()
312
+
313
  if __name__ == '__main__':
314
  app.run(host="0.0.0.0", port=7860, debug=True)