Spaces:
Sleeping
Sleeping
| import os | |
| import re | |
| import json | |
| import base64 | |
| import shutil | |
| import qrcode | |
| import requests | |
| import markdown | |
| from markdown.extensions.codehilite import CodeHiliteExtension | |
| from markdown.extensions.toc import TocExtension | |
| from markdown.extensions.sane_lists import SaneListExtension | |
| from markdown.extensions.nl2br import Nl2BrExtension | |
| from markdown.extensions.fenced_code import FencedCodeExtension | |
| from markdown.extensions.wikilinks import WikiLinkExtension | |
| from markdown.extensions.footnotes import FootnoteExtension | |
| from markdown.extensions.tables import TableExtension | |
| import itertools | |
| import frontmatter | |
| from io import BytesIO | |
| from PIL import ImageOps | |
| from utils import Utils | |
| from main import Image, ImageProcessor, TextProcessor | |
| BITLY_ACCESS_TOKEN = os.getenv('BITLY_ACCESS_TOKEN', None) | |
| BITLY_API_URL = "https://api-ssl.bitly.com/v4" | |
| class UrlProcessor: | |
| def __init__(self, access_token=None, api_url=None): | |
| self.access_token = access_token if access_token else BITLY_ACCESS_TOKEN | |
| self.api_url = api_url if api_url else BITLY_API_URL | |
| def shorten(self, long_url): | |
| try: | |
| url = self.api_url + "/shorten" | |
| headers = { | |
| "Authorization": f"Bearer {self.access_token}", | |
| "Content-Type": "application/json" | |
| } | |
| data = { | |
| "long_url": long_url | |
| } | |
| response = requests.post(url, json=data, headers=headers) | |
| response.raise_for_status() | |
| return response.json().get("id"), response.json() | |
| except requests.exceptions.RequestException as e: | |
| return None, {"error": str(e)} | |
| def generate_qr(self, shorten_url): # DEPRECATED | |
| try: | |
| qr_url = self.api_url + f"/bitlinks/{shorten_url}/qr" | |
| headers = { | |
| "Authorization": f"Bearer {self.access_token}", | |
| } | |
| params = { | |
| "customization": { | |
| "exclude_bitly_logo": True # TODO | |
| } | |
| } | |
| response = requests.get(qr_url, headers=headers, params=params) | |
| response.raise_for_status() | |
| qr_code_data = response.json().get("qr_code") | |
| qr_code_png_base64 = qr_code_data.split(',')[1] | |
| qr_code_png = base64.b64decode(qr_code_png_base64) | |
| qr_code_html = f'<img src="{qr_code_data}" alt="QR Code" height="40px">' | |
| return qr_code_html, qr_code_png, response.json() | |
| except requests.exceptions.RequestException as e: | |
| return None, None, {"error": str(e)} | |
| def generate_qr_local(self, url): | |
| try: | |
| qr = qrcode.QRCode( | |
| version=1, | |
| error_correction=qrcode.constants.ERROR_CORRECT_L, | |
| box_size=10, | |
| border=4, | |
| ) | |
| qr.add_data(url) | |
| qr.make(fit=True) | |
| img = qr.make_image(fill_color="black", back_color="white") | |
| buffered = BytesIO() | |
| img.save(buffered, format="PNG") | |
| qr_code_png = base64.b64encode(buffered.getvalue()) | |
| img_str = qr_code_png.decode('ascii') | |
| qr_code_html = f'<nobr><img src="data:image/png;base64,{img_str}" alt="QR Code" height="40px"></nobr>' | |
| json_str = {"url": url} | |
| return qr_code_html, buffered.getvalue(), json_str | |
| except Exception as e: | |
| return None, None, {"error": str(e)} | |
| def shorten_and_generate_qr(access_token, api_url, text, shorten, generate_qr, common_images, individual_images, sx, sy, scale, df): | |
| bitly = UrlProcessor(access_token, api_url) | |
| index_path = 'index.html' | |
| css_path = 'style.css' | |
| results = [] | |
| ts, tmpd = Utils.get_tempdir() | |
| all_json_responses = [] | |
| qr_svgs = [] | |
| qr_pngs = [] | |
| card_htmls = [] | |
| card_pngs = [] | |
| layer_output_paths = [] | |
| # TODO: Rotate Text follows each files? | |
| txt = '' | |
| if text['files']: | |
| for f in text['files']: | |
| with open(f) as f: | |
| txt += f.read() + '\n\n' | |
| if text['text']: | |
| txt += text['text'] | |
| urls = re.findall(r'http[s]?://\S+', txt) | |
| markdown_links = txt | |
| if urls is not None: | |
| urls = urls if urls is not None else [] | |
| for idx, url in enumerate(urls): | |
| shorten_url, shorten_response_json = None, None | |
| try: | |
| if shorten: | |
| shorten_url, shorten_response_json = bitly.shorten(url) | |
| all_json_responses.append(shorten_response_json) | |
| if shorten and shorten_url: | |
| url_to_process = shorten_url | |
| else: | |
| url_to_process = url | |
| if generate_qr and url_to_process: | |
| if shorten_url: | |
| qr_html, qr_png, qr_json = bitly.generate_qr_local(url_to_process) | |
| else: | |
| qr_html, qr_png, qr_json = bitly.generate_qr_local(url_to_process) | |
| if qr_png: | |
| os.makedirs(f'{tmpd}/img/qr', exist_ok=True) | |
| qr_png_path = f'img/qr/QR{idx:05d}.png' | |
| qr_path = os.path.join(tmpd, qr_png_path) | |
| with open(qr_path, 'wb') as f: | |
| f.write(qr_png) | |
| markdown_links = markdown_links.replace( | |
| url, f'<img src="{qr_png_path}" height="40"> [{url_to_process}]({url}) ' | |
| ) | |
| qr_pngs.append(qr_path) | |
| qr_svgs.append(qr_html) | |
| all_json_responses.append(qr_json) | |
| else: | |
| results.append(("Error generating QR code", url_to_process)) | |
| elif generate_qr: | |
| qr_html, qr_png, qr_json = bitly.generate_qr_local(url) | |
| if qr_png: | |
| os.makedirs(f'{tmpd}/img/qr', exist_ok=True) | |
| qr_png_path = f'img/qr/QR{idx:05d}.png' | |
| qr_path = os.path.join(tmpd, qr_png_path) | |
| with open(qr_path, 'wb') as f: | |
| f.write(qr_png) | |
| markdown_links = markdown_links.replace( | |
| url, f'<img src="{qr_png_path}" height="40"> [{url}]({url}) ' | |
| ) | |
| qr_pngs.append(qr_path) | |
| qr_svgs.append(qr_html) | |
| all_json_responses.append(qr_json) | |
| else: | |
| results.append(("Error generating QR code", url)) | |
| elif shorten_url: | |
| markdown_links = markdown_links.replace( | |
| url, f'[{shorten_url}]({url}) ' | |
| ) | |
| results.append((None, shorten_url)) | |
| else: | |
| results.append(("Error shortening URL", url)) | |
| except Exception as e: | |
| results.append((str(e), url)) | |
| size = (int(sx * 3.7795 * scale), int(sy * 3.7795 * scale)) | |
| processor = ImageProcessor(size) | |
| layer = processor.create_layer() | |
| os.makedirs(f'{tmpd}/img/cards', exist_ok=True) | |
| if qr_pngs and individual_images: | |
| num_iterations = len(qr_pngs) * len(individual_images) | |
| elif qr_pngs: | |
| num_iterations = len(qr_pngs) | |
| elif individual_images: | |
| num_iterations = len(individual_images) | |
| else: | |
| num_iterations = 1 | |
| # -------------------content start -------------------------------- | |
| index_html = f'<html><head><link rel="stylesheet" type="text/css" href="{css_path}"></head><body>\n' | |
| processed_text = '' | |
| md = markdown.Markdown() | |
| for idx, file in enumerate(text['files']): | |
| fm = frontmatter.load(file) # TODO | |
| replacements = {key: list(item.values())[0] \ | |
| for item in fm.metadata['replace'] for key in item} | |
| processed_text += TextProcessor.apply_frontmatter(fm.content, replacements) | |
| for idx in range(num_iterations): | |
| qr_idx = idx % len(qr_pngs) if qr_pngs else 0 | |
| indv_idx = idx // len(qr_pngs) if individual_images else 0 | |
| qr = qr_pngs[qr_idx] if qr_pngs else None | |
| indv = individual_images[indv_idx] if individual_images else None | |
| if qr or indv: | |
| try: | |
| pos_x = int(int(df.loc[idx, 'PosX']) * scale) | |
| pos_y = int(int(df.loc[idx, 'PosY']) * scale) | |
| pos_px = int(int(df.loc[idx, 'PosPx']) * scale) | |
| pos_py = int(int(df.loc[idx, 'PosPy']) * scale) | |
| size_x = int(int(df.loc[idx, 'SizeX']) * scale) | |
| size_y = int(int(df.loc[idx, 'SizeY']) * scale) | |
| size_px = int(int(df.loc[idx, 'SizePx']) * scale) | |
| size_py = int(int(df.loc[idx, 'SizePy']) * scale) | |
| if common_images: | |
| # os.makedirs(f'{tmpd}/img/layer/common', exist_ok=True) | |
| for common_img in common_images: | |
| layer = processor.combine_images(layer, common_img, size, (0, 0)) | |
| # layer_path = os.path.join(tmpd, f'img/layer/common/CM{idx:05d}.png') | |
| # layer.save(layer_path, format="PNG") | |
| # layer_output_paths.append(layer_path) | |
| if indv: | |
| # os.makedirs(f'{tmpd}/img/layer/individual', exist_ok=True) | |
| layer = processor.combine_images(layer, indv, (size_x, size_y), (pos_x, pos_y)) | |
| # layer_path = os.path.join(tmpd, f'img/layer/individual/ID{idx:05d}.png') | |
| # layer.save(layer_path, format="PNG") | |
| # layer_output_paths.append(layer_path) | |
| # if anno_txt: | |
| # layer = processor.combine_txt(layer, anno_txt, (size_px, size_py), (pos_px, pos_py)) | |
| if qr: | |
| # os.makedirs(f'{tmpd}/img/layer/qr', exist_ok=True) | |
| layer = processor.combine_images(layer, qr, (size_px, size_py), (pos_px, pos_py)) | |
| # layer_path = os.path.join(tmpd, f'img/layer/qr/QR{idx:05d}.png') | |
| # layer.save(layer_path, format="PNG") | |
| # layer_output_paths.append(layer_path) | |
| card_png_path = f'img/cards/CD{idx:05d}.png' | |
| card_path = os.path.join(tmpd, card_png_path) | |
| layer.save(card_path, format="PNG") | |
| card_pngs.append(card_path) | |
| with open(card_path, "rb") as image_file: | |
| encoded_string = base64.b64encode(image_file.read()).decode() | |
| html_txt = md.convert(processed_text) | |
| card_html = f'<div class="card">\n{html_txt}\n<img src="data:image/png;base64,{encoded_string}" alt="Card">\n</div>\n' | |
| card_htmls.append(card_html) | |
| except Exception as e: | |
| raise Exception(f"Error combining images: {e}") | |
| for card_html in card_htmls: | |
| index_html += card_html | |
| index_html += '</body></html>\n' | |
| # -------------------content end -------------------------------- | |
| try: | |
| json_output_path = os.path.join(tmpd, 'response.json') | |
| with open(json_output_path, 'w') as json_file: | |
| json.dump(all_json_responses, json_file) | |
| markdown_output_path = os.path.join(tmpd, 'index.md') | |
| with open(markdown_output_path, 'w') as markdown_file: | |
| markdown_file.write(markdown_links) | |
| qr_svg_output_paths = [] | |
| os.makedirs(f'{tmpd}/chunks/qr', exist_ok=True) | |
| for idx, svg in enumerate(qr_svgs): | |
| svg_output_path = os.path.join(tmpd, f'chunks/qr/QR{idx:05d}.html') | |
| with open(svg_output_path, 'w') as svg_file: | |
| svg_file.write(svg) | |
| qr_svg_output_paths.append(svg_output_path) | |
| qr_png_output_paths = [] | |
| for idx, png in enumerate(qr_pngs): | |
| qr_png_output_paths.append(png) | |
| card_svg_output_paths = [] | |
| os.makedirs(f'{tmpd}/chunks/cards', exist_ok=True) | |
| for idx, card_html in enumerate(card_htmls): | |
| card_output_path = os.path.join(tmpd, f'chunks/cards/CD{idx:05d}.html') | |
| with open(card_output_path, 'w') as card_file: | |
| card_file.write(card_html) | |
| card_svg_output_paths.append(card_output_path) | |
| card_png_output_paths = [] | |
| for idx, png in enumerate(card_pngs): | |
| card_png_output_paths.append(png) | |
| index_output_path = os.path.join(tmpd, index_path) | |
| with open(index_output_path, 'w') as index_file: | |
| index_file.write(index_html) | |
| css_output_path = os.path.join(tmpd, css_path) | |
| shutil.copy(css_path, css_output_path) | |
| zip_output_path = os.path.join(tmpd, f"{ts}.zip") | |
| zip_content_list = [ | |
| json_output_path, | |
| markdown_output_path, | |
| *layer_output_paths, | |
| *qr_svg_output_paths, | |
| *qr_png_output_paths, | |
| *card_svg_output_paths, | |
| *card_png_output_paths, | |
| index_output_path, | |
| css_output_path | |
| ] | |
| zip_output_path = Utils.create_zip(zip_content_list, zip_output_path) | |
| except Exception as e: | |
| return f"An error occurred: {str(e)}", None, None, [] | |
| return markdown_links, card_png_output_paths, zip_output_path, all_json_responses | |