admin commited on
Commit
284d622
·
1 Parent(s): 95b6e87

update stuff

Browse files
Files changed (3) hide show
  1. demo.md +14 -0
  2. src/modules/bitly-api.min.py +243 -96
  3. style.css +56 -0
demo.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ replace:
3
+ - name: Taro Tanaka
4
+ - group: Google Inc.
5
+ - email: [email protected]
6
+ - phone: 090 1234 5678
7
+ - link: https://example.com
8
+ ---
9
+ Name: {name}
10
+ Group: {group}
11
+ Contact:
12
+ - Email: {email}
13
+ - Phone: {phone}
14
+ - Link: {link}
src/modules/bitly-api.min.py CHANGED
@@ -9,6 +9,11 @@ import shutil
9
  import qrcode
10
  from io import BytesIO
11
  import time
 
 
 
 
 
12
  import gradio as gr
13
 
14
  description = f'''
@@ -23,7 +28,7 @@ For more information, see
23
  BITLY_ACCESS_TOKEN = os.getenv('BITLY_ACCESS_TOKEN', None)
24
  BITLY_API_URL = "https://api-ssl.bitly.com/v4"
25
 
26
- class ShortenUrl:
27
  def __init__(self, access_token=None, api_url=None):
28
  self.access_token = access_token if access_token else BITLY_ACCESS_TOKEN
29
  self.api_url = api_url if api_url else BITLY_API_URL
@@ -88,105 +93,228 @@ class ShortenUrl:
88
  except Exception as e:
89
  return None, None, {"error": str(e)}
90
 
91
- def shorten_and_generate_qr(access_token, api_url, text, shorten, generate_qr):
92
- bitly = ShortenUrl(access_token, api_url)
93
- urls = re.findall(r'http[s]?://\S+', text)
94
- results = []
95
- ts, tmpd = Interface.get_tempdir()
96
-
97
- markdown_links = text
98
- all_json_responses = []
99
- qr_svgs = []
100
- qr_pngs = []
101
-
102
- for idx, url in enumerate(urls):
103
- shorten_url, shorten_response_json = None, None
104
- png_file_name = f'qr_code_{idx}.png'
105
- png_output_path = os.path.join(tmpd, png_file_name)
106
- try:
107
- if shorten:
108
- shorten_url, shorten_response_json = bitly.shorten(url)
109
- all_json_responses.append(shorten_response_json)
110
-
111
- if shorten and shorten_url:
112
- url_to_process = shorten_url
113
- else:
114
- url_to_process = url
115
-
116
- if generate_qr and url_to_process:
117
- if shorten_url:
118
- # qr_html, qr_png, qr_json = bitly.generate_qr(url_to_process)
119
- qr_html, qr_png, qr_json = bitly.generate_qr_local(url_to_process)
120
- else:
121
- qr_html, qr_png, qr_json = bitly.generate_qr_local(url_to_process)
122
 
123
- if qr_png:
124
- markdown_links = markdown_links.replace(
125
- # url, f'{qr_html} [{url_to_process}]({url}) '
126
- url, f'<img src="{png_file_name}" height="40"> [{url}](https://{url_to_process}) '
127
- )
128
- qr_pngs.append(qr_png)
129
- qr_svgs.append(qr_html)
130
- all_json_responses.append(qr_json)
 
 
 
 
 
 
 
131
  else:
132
- results.append(("Error generating QR code", url_to_process))
133
- elif generate_qr:
134
- qr_html, qr_png, qr_json = bitly.generate_qr_local(url)
135
- if qr_png:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  markdown_links = markdown_links.replace(
137
- # url, f'{qr_html} [{url}]({url}) '
138
- url, f'<img src="{png_file_name}" height="40") [{url}]({url}) '
139
  )
140
- qr_pngs.append(qr_png)
141
- qr_svgs.append(qr_html)
142
- all_json_responses.append(qr_json)
143
  else:
144
- results.append(("Error generating QR code", url))
145
- elif shorten_url:
146
- markdown_links = markdown_links.replace(
147
- url, f'[{shorten_url}]({url}) '
148
- )
149
- results.append((None, shorten_url))
150
- else:
151
- results.append(("Error shortening URL", url))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  except Exception as e:
153
- results.append((str(e), url))
154
 
155
- try:
156
- json_output_path = os.path.join(tmpd, 'response.json')
157
- with open(json_output_path, 'w') as json_file:
158
- json.dump(all_json_responses, json_file)
159
 
160
- markdown_output_path = os.path.join(tmpd, 'links.md')
161
- with open(markdown_output_path, 'w') as markdown_file:
162
- markdown_file.write(markdown_links)
163
 
164
- qr_svg_output_paths = []
165
- for idx, svg in enumerate(qr_svgs):
166
- svg_output_path = os.path.join(tmpd, f'qr_code_{idx}.html')
167
- with open(svg_output_path, 'w') as svg_file:
168
- svg_file.write(svg)
169
- qr_svg_output_paths.append(svg_output_path)
170
 
171
- qr_png_output_paths = []
172
- for idx, png in enumerate(qr_pngs):
173
- png_output_path = os.path.join(tmpd, f'qr_code_{idx}.png')
174
- with open(png_output_path, 'wb') as png_file:
175
- png_file.write(png)
176
- qr_png_output_paths.append(png_output_path)
177
-
178
- zip_output_path = os.path.join(tmpd, f"{ts}.zip")
179
- zip_content_list = [
180
- json_output_path,
181
- markdown_output_path,
182
- *qr_svg_output_paths,
183
- *qr_png_output_paths
184
- ]
185
- zip_output_path = Interface.create_zip(zip_content_list, zip_output_path)
186
- except Exception as e:
187
- return f"An error occurred: {str(e)}", None, None, []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
- return markdown_links, qr_png_output_paths, zip_output_path, all_json_responses
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
  class Interface:
192
  @staticmethod
@@ -220,11 +348,15 @@ with gr.Blocks() as demo:
220
  access_token = gr.Textbox(label="Access Token", placeholder="***")
221
  api_url = gr.Textbox(label="API base URL", placeholder="https://")
222
  url_input = gr.TextArea(label="Enter Text with URLs")
 
223
  with gr.Row():
224
  shorten_checkbox = gr.Checkbox(label="Shorten URL", value=True)
225
  qr_checkbox = gr.Checkbox(label="Generate QR Code", value=True)
226
  submit_button = gr.Button("Process URLs", variant='primary')
227
-
 
 
 
228
  with gr.Column():
229
  file_output = gr.File(label="Download ZIP")
230
  with gr.Accordion(open=True, label="Gallery"):
@@ -234,14 +366,29 @@ with gr.Blocks() as demo:
234
  with gr.Accordion(open=False, label="Preview"):
235
  preview_output = gr.TextArea(label="Raw Text")
236
 
237
- gr.Examples([
238
- ['http://example.com'],
239
- ['http://example.com\nhttp://toscrape.com\nhttp://books.toscrape.com'],
240
- ], inputs=url_input)
 
 
 
 
 
 
 
 
241
 
242
  submit_button.click(
243
- shorten_and_generate_qr,
244
- inputs=[access_token, api_url, url_input, shorten_checkbox, qr_checkbox],
 
 
 
 
 
 
 
245
  outputs=[preview_output, gallery_output, file_output, json_output]
246
  )
247
 
 
9
  import qrcode
10
  from io import BytesIO
11
  import time
12
+ import textwrap
13
+ import markdown
14
+ import frontmatter
15
+ from PIL import Image, ImageDraw, ImageFont, ImageOps
16
+ from io import BytesIO
17
  import gradio as gr
18
 
19
  description = f'''
 
28
  BITLY_ACCESS_TOKEN = os.getenv('BITLY_ACCESS_TOKEN', None)
29
  BITLY_API_URL = "https://api-ssl.bitly.com/v4"
30
 
31
+ class UrlProcessor:
32
  def __init__(self, access_token=None, api_url=None):
33
  self.access_token = access_token if access_token else BITLY_ACCESS_TOKEN
34
  self.api_url = api_url if api_url else BITLY_API_URL
 
93
  except Exception as e:
94
  return None, None, {"error": str(e)}
95
 
96
+ @staticmethod
97
+ def shorten_and_generate_qr(access_token, api_url, text, shorten, generate_qr, common_images, individual_images):
98
+ bitly = UrlProcessor(access_token, api_url)
99
+ urls = re.findall(r'http[s]?://\S+', text)
100
+ results = []
101
+ ts, tmpd = Interface.get_tempdir()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
 
103
+ markdown_links = text
104
+ all_json_responses = []
105
+ qr_svgs = []
106
+ qr_pngs = []
107
+ card_htmls = []
108
+
109
+ for idx, url in enumerate(urls):
110
+ shorten_url, shorten_response_json = None, None
111
+ try:
112
+ if shorten:
113
+ shorten_url, shorten_response_json = bitly.shorten(url)
114
+ all_json_responses.append(shorten_response_json)
115
+
116
+ if shorten and shorten_url:
117
+ url_to_process = shorten_url
118
  else:
119
+ url_to_process = url
120
+
121
+ if generate_qr and url_to_process:
122
+ if shorten_url:
123
+ qr_html, qr_png, qr_json = bitly.generate_qr_local(url_to_process)
124
+ else:
125
+ qr_html, qr_png, qr_json = bitly.generate_qr_local(url_to_process)
126
+
127
+ if qr_png:
128
+ qr_png_path = os.path.join(tmpd, f'qr_code_{idx}.png')
129
+ with open(qr_png_path, 'wb') as f:
130
+ f.write(qr_png)
131
+ markdown_links = markdown_links.replace(
132
+ url, f'<img src="{qr_png_path}" height="40"> [{url_to_process}]({url}) '
133
+ )
134
+ qr_pngs.append(qr_png_path)
135
+ qr_svgs.append(qr_html)
136
+ all_json_responses.append(qr_json)
137
+ else:
138
+ results.append(("Error generating QR code", url_to_process))
139
+ elif generate_qr:
140
+ qr_html, qr_png, qr_json = bitly.generate_qr_local(url)
141
+ if qr_png:
142
+ qr_png_path = os.path.join(tmpd, f'qr_code_{idx}.png')
143
+ with open(qr_png_path, 'wb') as f:
144
+ f.write(qr_png)
145
+ markdown_links = markdown_links.replace(
146
+ url, f'<img src="{qr_png_path}" height="40"> [{url}]({url}) '
147
+ )
148
+ qr_pngs.append(qr_png_path)
149
+ qr_svgs.append(qr_html)
150
+ all_json_responses.append(qr_json)
151
+ else:
152
+ results.append(("Error generating QR code", url))
153
+ elif shorten_url:
154
  markdown_links = markdown_links.replace(
155
+ url, f'[{shorten_url}]({url}) '
 
156
  )
157
+ results.append((None, shorten_url))
 
 
158
  else:
159
+ results.append(("Error shortening URL", url))
160
+ except Exception as e:
161
+ results.append((str(e), url))
162
+
163
+ layer = ImageProcessor.create_layer()
164
+
165
+ for idx, png in enumerate(qr_pngs):
166
+ try:
167
+ png = ImageProcessor.combine_images(layer, [png])
168
+ if common_images:
169
+ png = ImageProcessor.combine_images(png, common_images)
170
+ if individual_images:
171
+ for im in individual_images:
172
+ png = ImageProcessor.combine_images(png, [im.name])
173
+ with open(png, "rb") as image_file:
174
+ encoded_string = base64.b64encode(image_file.read()).decode()
175
+
176
+ card_html = f'<img src="data:image/png;base64,{encoded_string}" alt="Card"> '
177
+ card_htmls.append(card_html)
178
+ except Exception as e:
179
+ raise Exception(f"Error combining images: {e}")
180
+
181
+
182
+ try:
183
+ json_output_path = os.path.join(tmpd, 'response.json')
184
+ with open(json_output_path, 'w') as json_file:
185
+ json.dump(all_json_responses, json_file)
186
+
187
+ markdown_output_path = os.path.join(tmpd, 'links.md')
188
+ with open(markdown_output_path, 'w') as markdown_file:
189
+ markdown_file.write(markdown_links)
190
+
191
+ qr_svg_output_paths = []
192
+ for idx, svg in enumerate(qr_svgs):
193
+ svg_output_path = os.path.join(tmpd, f'qr_code_{idx}.html')
194
+ with open(svg_output_path, 'w') as svg_file:
195
+ svg_file.write(svg)
196
+ qr_svg_output_paths.append(svg_output_path)
197
+
198
+ qr_png_output_paths = []
199
+ for idx, png in enumerate(qr_pngs):
200
+ qr_png_output_paths.append(png)
201
+
202
+ card_output_paths = []
203
+ for idx, card_html in enumerate(card_htmls):
204
+ card_output_path = os.path.join(tmpd, f'card_{idx}.html')
205
+ with open(card_output_path, 'w') as card_file:
206
+ card_file.write(card_html)
207
+ card_output_paths.append(card_output_path)
208
+
209
+ index_html = '<html><head><link rel="stylesheet" type="text/css" href="style.css"></head><body>'
210
+ for card_html in card_htmls:
211
+ index_html += card_html
212
+ index_html += '</body></html>'
213
+
214
+ index_output_path = os.path.join(tmpd, 'index.html')
215
+ with open(index_output_path, 'w') as index_file:
216
+ index_file.write(index_html)
217
+
218
+ zip_output_path = os.path.join(tmpd, f"{ts}.zip")
219
+ zip_content_list = [
220
+ json_output_path,
221
+ markdown_output_path,
222
+ *qr_svg_output_paths,
223
+ *qr_png_output_paths,
224
+ *card_output_paths,
225
+ index_output_path
226
+ ]
227
+ zip_output_path = Interface.create_zip(zip_content_list, zip_output_path)
228
  except Exception as e:
229
+ return f"An error occurred: {str(e)}", None, None, []
230
 
231
+ return markdown_links, qr_png_output_paths, zip_output_path, all_json_responses
 
 
 
232
 
 
 
 
233
 
234
+ class TextProcessor:
235
+ def apply_frontmatter(text, replacements):
236
+ for key, value in replacements.items():
237
+ text = text.replace(f'{{{key}}}', value)
238
+ return text
 
239
 
240
+ def apply_style(input_markdown_path, style_css_path):
241
+ with open(input_markdown_path, 'r') as file:
242
+ md_content = file.read()
243
+
244
+ fm = frontmatter.load(input_markdown_path)
245
+ replacements = {key: list(item.values())[0] \
246
+ for item in fm.metadata['replace'] for key in item}
247
+ processed_text = TextProcessor.apply_frontmatter(md_content, replacements)
248
+
249
+ with open(style_css_path, 'r') as file:
250
+ style_css = file.read()
251
+
252
+ html_content = markdown.markdown(processed_text)
253
+ html_content = f'<html><head><style>{style_css}</style></head><body>{html_content}</body></html>'
254
+
255
+ temp_html_path = os.path.join(tempfile.gettempdir(), 'processed.html')
256
+ with open(temp_html_path, 'w') as file:
257
+ file.write(html_content)
258
+
259
+ return temp_html_path
260
+
261
+ class ImageProcessor:
262
+
263
+
264
+ def create_layer(layout='horizontal', size=(91 * 3.7795, 55 * 3.7795)):
265
+ size = (int(size[0]), int(size[1])) # Convert to integers
266
+ img = Image.new('RGBA', size, (255, 255, 255, 0))
267
+ draw = ImageDraw.Draw(img)
268
+ if layout == 'vertical':
269
+ draw.rectangle([(0, 0), (size[0], size[1])], outline="black", width=3)
270
+ elif layout == 'horizontal':
271
+ draw.rectangle([(0, 0), (size[1], size[0])], outline="black", width=3)
272
 
273
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmpfile:
274
+ img.save(tmpfile.name, format="PNG")
275
+ return tmpfile.name
276
+
277
+ @staticmethod
278
+ def combine_images(base_image_path, overlay_image_paths):
279
+ with open(base_image_path, 'rb') as f:
280
+ base_img = Image.open(f).convert("RGBA")
281
+ for overlay_path in overlay_image_paths:
282
+ with open(overlay_path, 'rb') as f:
283
+ overlay_img = Image.open(f).convert("RGBA")
284
+ # overlay_img = overlay_img.resize(base_img.size, Image.NEAREST )
285
+ base_img = ImageOps.contain(overlay_img, base_img.size)
286
+
287
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmpfile:
288
+ base_img.save(tmpfile.name, format="PNG")
289
+ return tmpfile.name
290
+
291
+ # @staticmethod
292
+ # def insert_txt(png_path, text):
293
+ # img = Image.open(png_path)
294
+ # draw = ImageDraw.Draw(img)
295
+ # font = ImageFont.load_default() # Update to a specific font if needed
296
+ # width, height = img.size
297
+ # text_width, text_height = draw.textlength(text, font=font)
298
+ # text_position = (width - text_width - 10, height - text_height - 10)
299
+ # draw.text(text_position, text, font=font, fill="black")
300
+
301
+ # with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmpfile:
302
+ # img.save(tmpfile.name, format="PNG")
303
+ # return tmpfile.name
304
+
305
+ # @staticmethod
306
+ # def insert_txt(png_path, text):
307
+ # img = Image.open(png_path)
308
+ # draw = ImageDraw.Draw(img)
309
+ # font = ImageFont.load_default() # Update to a specific font if needed
310
+ # width, height = img.size
311
+ # text_width, text_height = draw.textlength(text, font=font)
312
+ # text_position = (width - text_width - 10, height - text_height - 10)
313
+ # draw.text(text_position, text, font=font, fill="black")
314
+
315
+ # with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmpfile:
316
+ # img.save(tmpfile.name, format="PNG")
317
+ # return tmpfile.name
318
 
319
  class Interface:
320
  @staticmethod
 
348
  access_token = gr.Textbox(label="Access Token", placeholder="***")
349
  api_url = gr.Textbox(label="API base URL", placeholder="https://")
350
  url_input = gr.TextArea(label="Enter Text with URLs")
351
+
352
  with gr.Row():
353
  shorten_checkbox = gr.Checkbox(label="Shorten URL", value=True)
354
  qr_checkbox = gr.Checkbox(label="Generate QR Code", value=True)
355
  submit_button = gr.Button("Process URLs", variant='primary')
356
+ with gr.Accordion(open=False, label="Insert"):
357
+ with gr.Row():
358
+ cm_img_input = gr.Files(label='Insert common images')
359
+ idv_img_input = gr.Files(label='Insert individual images')
360
  with gr.Column():
361
  file_output = gr.File(label="Download ZIP")
362
  with gr.Accordion(open=True, label="Gallery"):
 
366
  with gr.Accordion(open=False, label="Preview"):
367
  preview_output = gr.TextArea(label="Raw Text")
368
 
369
+ example_temp_html = TextProcessor.apply_style('demo.md', 'style.css')
370
+
371
+ examples = [
372
+ ['http://example.com\nhttp://toscrape.com\nhttp://books.toscrape.com',
373
+ ['img/number-1.png'],
374
+ ['img/number-2.png']
375
+ ]
376
+ ]
377
+ inputs = [
378
+ url_input, cm_img_input, idv_img_input
379
+ ]
380
+ gr.Examples(examples, inputs)
381
 
382
  submit_button.click(
383
+ UrlProcessor.shorten_and_generate_qr,
384
+ inputs=[
385
+ access_token,
386
+ api_url,
387
+ url_input,
388
+ shorten_checkbox,
389
+ qr_checkbox,
390
+ cm_img_input,
391
+ idv_img_input],
392
  outputs=[preview_output, gallery_output, file_output, json_output]
393
  )
394
 
style.css ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ h1 {
2
+ font-weight: bold;
3
+ }
4
+
5
+ /* フォントとマージンの設定 */
6
+ p {
7
+ font-family: 'MS Mincho', serif;
8
+ margin: 10%;
9
+ }
10
+
11
+ /* 画像の透明度設定 */
12
+ img {
13
+ opacity: 0.5;
14
+ }
15
+
16
+ /* QRコードの位置設定 */
17
+ .qr {
18
+ position: absolute;
19
+ bottom: 10px;
20
+ right: 10px;
21
+ margin: 10%;
22
+ }
23
+
24
+ /* カードのサイズとグリッドレイアウト設定 */
25
+ .card {
26
+ width: 91mm;
27
+ height: 55mm;
28
+ display: grid;
29
+ grid-template-columns: 1fr;
30
+ grid-template-rows: 1fr;
31
+ align-items: center;
32
+ justify-items: center;
33
+ border: 1px solid black;
34
+ margin: 5mm;
35
+ position: relative;
36
+ }
37
+
38
+ /* 印刷時のA4用紙設定 */
39
+ @media print {
40
+ .a4 {
41
+ page-break-after: always;
42
+ width: 210mm;
43
+ height: 297mm;
44
+ display: grid;
45
+ grid-template-columns: repeat(2, 1fr);
46
+ grid-template-rows: repeat(5, 1fr);
47
+ gap: 0;
48
+ }
49
+
50
+ .card {
51
+ margin: 0;
52
+ border: none;
53
+ width: 100%;
54
+ height: 100%;
55
+ }
56
+ }