|
import io |
|
import os |
|
import re |
|
import glob |
|
from datetime import datetime |
|
from pathlib import Path |
|
|
|
import streamlit as st |
|
import pandas as pd |
|
from PIL import Image |
|
from reportlab.pdfgen import canvas |
|
from reportlab.lib.utils import ImageReader |
|
import mistune |
|
from gtts import gTTS |
|
|
|
|
|
st.set_page_config(page_title="PDF & Code Interpreter", layout="wide", page_icon="π") |
|
|
|
def delete_asset(path): |
|
try: |
|
os.remove(path) |
|
except: |
|
pass |
|
st.rerun() |
|
|
|
|
|
tab1, tab2 = st.tabs(["π PDF Composer", "π§ͺ CodeInterpreter"]) |
|
|
|
with tab1: |
|
st.header("π PDF Composer & Voice Generator π") |
|
|
|
md_file = st.file_uploader("Upload Markdown (.md)", type=["md"]) |
|
if md_file: |
|
md_text = md_file.getvalue().decode("utf-8") |
|
else: |
|
md_text = st.text_area("Or enter markdown text directly", height=200) |
|
|
|
renderer = mistune.HTMLRenderer() |
|
markdown = mistune.create_markdown(renderer=renderer) |
|
html = markdown(md_text or "") |
|
plain_text = re.sub(r'<[^>]+>', '', html) |
|
|
|
if st.button("π Generate Voice MP3 from Text"): |
|
if plain_text.strip(): |
|
voice_file = f"voice_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp3" |
|
tts = gTTS(text=plain_text, lang='en') |
|
tts.save(voice_file) |
|
st.audio(voice_file) |
|
else: |
|
st.warning("No text to generate voice from.") |
|
|
|
imgs = st.file_uploader("Upload Images for PDF", type=["png", "jpg", "jpeg"], accept_multiple_files=True) |
|
ordered_images = [] |
|
if imgs: |
|
df_imgs = pd.DataFrame([{"name": f.name, "order": i} for i, f in enumerate(imgs)]) |
|
edited = st.data_editor(df_imgs, use_container_width=True) |
|
for _, row in edited.sort_values('order').iterrows(): |
|
for f in imgs: |
|
if f.name == row['name']: |
|
ordered_images.append(f) |
|
break |
|
|
|
if st.button("ποΈ Generate PDF with Markdown & Images"): |
|
buf = io.BytesIO() |
|
c = canvas.Canvas(buf) |
|
y = 800 |
|
for line in plain_text.split('\n'): |
|
c.drawString(40, y, line) |
|
y -= 15 |
|
if y < 50: |
|
c.showPage() |
|
y = 800 |
|
for img_f in ordered_images: |
|
try: |
|
img = Image.open(img_f) |
|
w, h = img.size |
|
c.showPage() |
|
c.drawImage(ImageReader(img), 0, 0, w, h, preserveAspectRatio=True, mask='auto') |
|
except: |
|
continue |
|
c.save() |
|
buf.seek(0) |
|
pdf_name = f"doc_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf" |
|
st.download_button("β¬οΈ Download PDF", data=buf, file_name=pdf_name, mime="application/pdf") |
|
|
|
st.markdown("---") |
|
st.subheader("π Available Assets") |
|
assets = sorted(glob.glob("*.*")) |
|
for a in assets: |
|
ext = a.split('.')[-1].lower() |
|
cols = st.columns([3, 1, 1]) |
|
cols[0].write(a) |
|
if ext == 'pdf': |
|
with open(a, 'rb') as fp: |
|
cols[1].download_button("π₯", data=fp, file_name=a, mime="application/pdf") |
|
elif ext == 'mp3': |
|
cols[1].audio(a) |
|
cols[2].button("ποΈ", key=f"del_{a}", on_click=delete_asset, args=(a,)) |
|
|
|
with tab2: |
|
st.header("π§ͺ Python Code Executor & Demo") |
|
import io, sys |
|
from contextlib import redirect_stdout |
|
|
|
DEFAULT_CODE = '''import streamlit as st |
|
import random |
|
|
|
st.title("π Demo App") |
|
st.markdown("Random number and color demo") |
|
|
|
col1, col2 = st.columns(2) |
|
with col1: |
|
num = st.number_input("Number:", 1, 100, 10) |
|
mul = st.slider("Multiplier:", 1, 10, 2) |
|
if st.button("Calc"): |
|
st.write(num * mul) |
|
with col2: |
|
color = st.color_picker("Pick color","#ff0000") |
|
st.markdown(f'<div style="background:{color};padding:10px;">Color</div>', unsafe_allow_html=True) |
|
''' |
|
|
|
def extract_python_code(md: str) -> list: |
|
return re.findall(r"```python\s*(.*?)```", md, re.DOTALL) |
|
|
|
def execute_code(code: str) -> tuple: |
|
buf = io.StringIO() |
|
local_vars = {} |
|
try: |
|
with redirect_stdout(buf): |
|
exec(code, {}, local_vars) |
|
return buf.getvalue(), None |
|
except Exception as e: |
|
return None, str(e) |
|
|
|
up = st.file_uploader("Upload .py or .md", type=['py', 'md']) |
|
if 'code' not in st.session_state: |
|
st.session_state.code = DEFAULT_CODE |
|
|
|
if up: |
|
text = up.getvalue().decode() |
|
if up.type == 'text/markdown': |
|
codes = extract_python_code(text) |
|
st.session_state.code = codes[0] if codes else '' |
|
else: |
|
st.session_state.code = text |
|
st.code(st.session_state.code, language='python') |
|
else: |
|
st.session_state.code = st.text_area("π» Code Editor", value=st.session_state.code, height=300) |
|
|
|
c1, c2 = st.columns([1, 1]) |
|
if c1.button("βΆοΈ Run Code"): |
|
out, err = execute_code(st.session_state.code) |
|
if err: |
|
st.error(err) |
|
elif out: |
|
st.code(out) |
|
else: |
|
st.success("Executed with no output.") |
|
if c2.button("ποΈ Clear Code"): |
|
st.session_state.code = '' |
|
st.rerun() |