jerrrycans commited on
Commit
b073f50
·
verified ·
1 Parent(s): e3e75ca

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +18 -50
app.py CHANGED
@@ -3,11 +3,9 @@ import requests
3
  import base64
4
  import io
5
  import json
6
- from urllib.parse import unquote
7
 
8
  app = Flask(__name__)
9
 
10
- # Style configurations
11
  STYLES = {
12
  'pixel': {
13
  'prompt': 'Turn this image into the Pixel style.',
@@ -84,21 +82,16 @@ STYLES = {
84
  }
85
 
86
  def upload_base64_image(base64_data):
87
- """Upload base64 image to jerrrycans-file.hf.space"""
88
  try:
89
  header, data = base64_data.split(',', 1)
90
  image_data = base64.b64decode(data)
91
-
92
  files = {'file': ('generated_image.png', io.BytesIO(image_data), 'image/png')}
93
  headers = {
94
  'Origin': 'https://jerrrycans-file.hf.space',
95
  'Referer': 'https://jerrrycans-file.hf.space/',
96
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
97
  }
98
-
99
- response = requests.post('https://jerrrycans-file.hf.space/upload',
100
- files=files, headers=headers)
101
-
102
  if response.status_code == 200:
103
  result = response.json()
104
  return f"https://jerrrycans-file.hf.space{result['url']}"
@@ -106,46 +99,35 @@ def upload_base64_image(base64_data):
106
  print(f"Upload error: {e}")
107
  return None
108
 
109
- def get_style_config(style, custom_prompt=None, custom_lora_url=None):
110
- """Get style configuration, allowing custom prompt to override style prompt."""
111
- style_config = STYLES.get(style.lower())
112
-
113
- if style_config:
114
- # Style found: use its LoRA, but allow prompt to be overridden
115
  return {
116
- 'prompt': custom_prompt or style_config['prompt'],
117
- 'lora_url': custom_lora_url or style_config['lora_url']
118
  }
 
 
119
  else:
120
- # No style found: treat 'style' as the prompt (legacy behavior)
121
- return {
122
- 'prompt': custom_prompt or style,
123
- 'lora_url': custom_lora_url
124
- }
125
 
126
  def process_stream_response(response):
127
- """Process streaming response from FAL API"""
128
  uploaded_images = []
129
  buffer = ''
130
-
131
  for chunk in response.iter_content(chunk_size=1024, decode_unicode=True):
132
  if chunk:
133
  buffer += chunk
134
  lines = buffer.split('\n')
135
  buffer = lines.pop()
136
-
137
  for line in lines:
138
  if line.startswith('data: ') and len(line) > 6:
139
  try:
140
  data_content = line[6:].strip()
141
  if not data_content:
142
  continue
143
-
144
  data = json.loads(data_content)
145
-
146
  if (data.get('json', {}).get('type') in ['progress', 'completed'] and
147
  data.get('json', {}).get('data', {}).get('images')):
148
-
149
  for image in data['json']['data']['images']:
150
  if image.get('url', '').startswith('data:image/'):
151
  uploaded_url = upload_base64_image(image['url'])
@@ -153,51 +135,38 @@ def process_stream_response(response):
153
  uploaded_images.append(uploaded_url)
154
  except:
155
  continue
156
-
157
  return uploaded_images
158
 
159
  @app.route('/api/transform', methods=['POST'])
160
  def transform_image():
161
  try:
162
  data = request.get_json()
163
-
164
  if not data:
165
  return jsonify({'success': False, 'error': 'No JSON data provided'}), 400
166
-
167
  image_url = data.get('image_url')
168
  style = data.get('style')
169
- custom_prompt = data.get('custom_prompt') # New field
170
  custom_lora_url = data.get('lora_url')
171
-
172
- if not image_url:
173
- return jsonify({'success': False, 'error': 'image_url is required'}), 400
174
-
175
- if not style:
176
- return jsonify({'success': False, 'error': 'style is required'}), 400
177
-
178
- if not image_url.startswith(('http://', 'https://')):
179
- return jsonify({'success': False, 'error': 'Invalid image URL'}), 400
180
-
181
  style_config = get_style_config(style, custom_prompt, custom_lora_url)
182
-
 
183
  generate_params = {
184
  'json': {
185
  'imageUrl': image_url,
186
  'prompt': style_config['prompt']
187
  }
188
  }
189
-
190
- if style_config['lora_url']:
191
  generate_params['json']['loraUrl'] = style_config['lora_url']
192
-
193
  generate_url = f"https://fal-kontext-demo.vercel.app/api/trpc/generateImageStream?input={requests.utils.quote(json.dumps(generate_params))}"
194
-
195
  response = requests.get(generate_url, stream=True)
196
  if response.status_code != 200:
197
  return jsonify({'success': False, 'error': 'Failed to generate styled image'}), 500
198
-
199
  uploaded_images = process_stream_response(response)
200
-
201
  return jsonify({
202
  'success': True,
203
  'originalImage': image_url,
@@ -206,7 +175,6 @@ def transform_image():
206
  'generatedImages': uploaded_images,
207
  'count': len(uploaded_images)
208
  })
209
-
210
  except Exception as e:
211
  return jsonify({'success': False, 'error': str(e)}), 500
212
 
@@ -215,7 +183,7 @@ def index():
215
  return jsonify({
216
  'message': 'Image Style Transfer API',
217
  'usage': 'POST /api/transform with JSON body: {"image_url": "...", "style": "...", "custom_prompt": "(optional)", "lora_url": "(optional)"}',
218
- 'note': 'If "custom_prompt" is provided, it will override the default prompt for the selected "style", while still using the style\'s LoRA.',
219
  'styles': list(STYLES.keys())
220
  })
221
 
 
3
  import base64
4
  import io
5
  import json
 
6
 
7
  app = Flask(__name__)
8
 
 
9
  STYLES = {
10
  'pixel': {
11
  'prompt': 'Turn this image into the Pixel style.',
 
82
  }
83
 
84
  def upload_base64_image(base64_data):
 
85
  try:
86
  header, data = base64_data.split(',', 1)
87
  image_data = base64.b64decode(data)
 
88
  files = {'file': ('generated_image.png', io.BytesIO(image_data), 'image/png')}
89
  headers = {
90
  'Origin': 'https://jerrrycans-file.hf.space',
91
  'Referer': 'https://jerrrycans-file.hf.space/',
92
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
93
  }
94
+ response = requests.post('https://jerrrycans-file.hf.space/upload', files=files, headers=headers)
 
 
 
95
  if response.status_code == 200:
96
  result = response.json()
97
  return f"https://jerrrycans-file.hf.space{result['url']}"
 
99
  print(f"Upload error: {e}")
100
  return None
101
 
102
+ def get_style_config(style=None, custom_prompt=None, custom_lora_url=None):
103
+ if style and style.lower() in STYLES:
104
+ sc = STYLES[style.lower()]
 
 
 
105
  return {
106
+ 'prompt': custom_prompt or sc['prompt'],
107
+ 'lora_url': custom_lora_url or sc['lora_url']
108
  }
109
+ elif custom_prompt:
110
+ return {'prompt': custom_prompt, 'lora_url': custom_lora_url}
111
  else:
112
+ return None
 
 
 
 
113
 
114
  def process_stream_response(response):
 
115
  uploaded_images = []
116
  buffer = ''
 
117
  for chunk in response.iter_content(chunk_size=1024, decode_unicode=True):
118
  if chunk:
119
  buffer += chunk
120
  lines = buffer.split('\n')
121
  buffer = lines.pop()
 
122
  for line in lines:
123
  if line.startswith('data: ') and len(line) > 6:
124
  try:
125
  data_content = line[6:].strip()
126
  if not data_content:
127
  continue
 
128
  data = json.loads(data_content)
 
129
  if (data.get('json', {}).get('type') in ['progress', 'completed'] and
130
  data.get('json', {}).get('data', {}).get('images')):
 
131
  for image in data['json']['data']['images']:
132
  if image.get('url', '').startswith('data:image/'):
133
  uploaded_url = upload_base64_image(image['url'])
 
135
  uploaded_images.append(uploaded_url)
136
  except:
137
  continue
 
138
  return uploaded_images
139
 
140
  @app.route('/api/transform', methods=['POST'])
141
  def transform_image():
142
  try:
143
  data = request.get_json()
 
144
  if not data:
145
  return jsonify({'success': False, 'error': 'No JSON data provided'}), 400
 
146
  image_url = data.get('image_url')
147
  style = data.get('style')
148
+ custom_prompt = data.get('custom_prompt')
149
  custom_lora_url = data.get('lora_url')
150
+ if not image_url or not image_url.startswith(('http://', 'https://')):
151
+ return jsonify({'success': False, 'error': 'Valid image_url is required'}), 400
152
+ if not (style or custom_prompt):
153
+ return jsonify({'success': False, 'error': 'Either style or custom_prompt is required'}), 400
 
 
 
 
 
 
154
  style_config = get_style_config(style, custom_prompt, custom_lora_url)
155
+ if not style_config or not style_config.get('prompt'):
156
+ return jsonify({'success': False, 'error': 'Valid style or custom_prompt required'}), 400
157
  generate_params = {
158
  'json': {
159
  'imageUrl': image_url,
160
  'prompt': style_config['prompt']
161
  }
162
  }
163
+ if style_config.get('lora_url'):
 
164
  generate_params['json']['loraUrl'] = style_config['lora_url']
 
165
  generate_url = f"https://fal-kontext-demo.vercel.app/api/trpc/generateImageStream?input={requests.utils.quote(json.dumps(generate_params))}"
 
166
  response = requests.get(generate_url, stream=True)
167
  if response.status_code != 200:
168
  return jsonify({'success': False, 'error': 'Failed to generate styled image'}), 500
 
169
  uploaded_images = process_stream_response(response)
 
170
  return jsonify({
171
  'success': True,
172
  'originalImage': image_url,
 
175
  'generatedImages': uploaded_images,
176
  'count': len(uploaded_images)
177
  })
 
178
  except Exception as e:
179
  return jsonify({'success': False, 'error': str(e)}), 500
180
 
 
183
  return jsonify({
184
  'message': 'Image Style Transfer API',
185
  'usage': 'POST /api/transform with JSON body: {"image_url": "...", "style": "...", "custom_prompt": "(optional)", "lora_url": "(optional)"}',
186
+ 'note': 'Either "style" or "custom_prompt" must be provided. If "custom_prompt" is provided, it overrides the default prompt for a known "style"; if only "custom_prompt" is given, style can be omitted.',
187
  'styles': list(STYLES.keys())
188
  })
189