Python_Toolkit / app.py
Steelheartx's picture
Update app.py
d9823c3 verified
Raw
History Blame Contribute Delete
6.35 kB
from fasthtml.common import *
from PyPDF2 import PdfReader, PdfWriter, PdfMerger
import os, io, uuid, platform, subprocess
# 1. Setup 'static' folder (FastHTML auto-serves this directory)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
UPLOAD_DIR = os.path.join(BASE_DIR, "pdf_outputs")
if not os.path.exists(UPLOAD_DIR):
os.makedirs(UPLOAD_DIR)
# 2. FastHTML App Setup
app, rt = fast_app(
hdrs=(
Link(rel="stylesheet", href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"),
Style("""
:root { --pico-font-size: 90%; } /* Shrinks everything by 10% */
h4 { margin-bottom: 0.5rem; font-size: 1.1rem; } /* Slims down card titles */
.card { padding: 1rem; } /* Reduces internal card spacing */
button { padding: 0.5rem 1rem; font-size: 0.9rem; } /* Slimmer buttons */
""")
),
live=False
)
@rt("/")
def get():
return Titled("Modern PDF Toolbox",
Container(
P("Upload your PDFs and choose an operation."),
Grid(
Card(H4("Split PDF"),
Form(Input(type="file", name="file", accept=".pdf", required=True),
Button("Split into Pages", cls="secondary"),
hx_post="/split", hx_target="#result", enctype="multipart/form-data")),
Card(H4("Merge PDFs"),
Form(Input(type="file", name="files", accept=".pdf", multiple=True, required=True),
Button("Merge Files"),
hx_post="/merge", hx_target="#result", enctype="multipart/form-data")),
),
Grid(
Card(H4("Rotate PDF"),
Form(Input(type="file", name="file", accept=".pdf", required=True),
Group(
Button("90° CW", name="angle", value="90", cls="outline"),
Button("180°", name="angle", value="180", cls="outline"),
Button("270° CW", name="angle", value="270", cls="outline"),
),
hx_post="/rotate", hx_target="#result", enctype="multipart/form-data")),
Card(H4("Extract Range"),
Form(Input(type="file", name="file", accept=".pdf", required=True),
Group(Input(type="number", name="start", placeholder="Start"),
Input(type="number", name="end", placeholder="End")),
Button("Extract", cls="secondary"),
hx_post="/extract", hx_target="#result", enctype="multipart/form-data")),
),
Hr(),
Section(id="result"),
Hr(),
# --- Utility Section with the new button ---
Div(
Group(
Button("List Cached Files", hx_post="/open_folder", hx_target="#result", cls="outline"),
Button("Clear Cache", hx_post="/clear_cache", hx_target="#result", cls="contrast outline"),
),
style="margin-top: 20px;"
)
)
)
# --- UTILITY ROUTES ---
@rt("/open_folder")
def post():
files = os.listdir(UPLOAD_DIR)
if not files:
return P("The cache is currently empty.", style="color: gray;")
# Create a list of links for every file in the folder
item_list = []
for f in files:
item_list.append(
Li(
A(f, href=f"/get-file/{f}", target="_blank"),
style="font-size: 0.85rem;"
)
)
return Div(
H5("Files in Cache:"),
Ul(*item_list),
style="border: 1px solid #ccc; padding: 1rem; border-radius: 8px; margin-top: 1rem;"
)
@rt("/clear_cache")
def post():
files_deleted = 0
for f in os.listdir(UPLOAD_DIR):
file_path = os.path.join(UPLOAD_DIR, f)
if os.path.isfile(file_path):
os.remove(file_path)
files_deleted += 1
return Div(f"🧹 Cache cleared! {files_deleted} files removed.", cls="pico-color-green-500", style="font-weight: bold;")
# --- PDF OPERATION ROUTES ---
@rt("/split")
async def post(file: UploadFile):
reader = PdfReader(io.BytesIO(await file.read()))
sid = uuid.uuid4().hex[:6]
links = []
for i, page in enumerate(reader.pages):
writer = PdfWriter()
writer.add_page(page)
fname = f"{sid}_p{i+1}.pdf"
with open(os.path.join(UPLOAD_DIR, fname), "wb") as f:
writer.write(f)
links.append(Li(A(f"Download Page {i+1}", href=f"/pdf_outputs/{fname}", target="_blank")))
return Div(H3(f"Split Complete ({len(reader.pages)} pages)"), Ul(*links))
@rt("/merge")
async def post(files: list[UploadFile]):
merger = PdfMerger()
for f in files: merger.append(io.BytesIO(await f.read()))
fname = f"merged_{uuid.uuid4().hex[:6]}.pdf"
with open(os.path.join(UPLOAD_DIR, fname), "wb") as f: merger.write(f)
return Div(H3("Merge Complete!"), A("Download Combined PDF", href=f"/pdf_outputs/{fname}", role="button", target="_blank"))
@rt("/rotate")
async def post(file: UploadFile, angle: int):
reader = PdfReader(io.BytesIO(await file.read()))
writer = PdfWriter()
for page in reader.pages:
page.rotate(angle)
writer.add_page(page)
fname = f"rotated_{uuid.uuid4().hex[:6]}.pdf"
with open(os.path.join(UPLOAD_DIR, fname), "wb") as f: writer.write(f)
return Div(H3("Rotation Complete!"), A(f"View Rotated PDF", href=f"/pdf_outputs/{fname}", role="button", target="_blank"))
@rt("/extract")
async def post(file: UploadFile, start: int, end: int):
reader = PdfReader(io.BytesIO(await file.read()))
writer = PdfWriter()
for i in range(max(0, start-1), min(end, len(reader.pages))): writer.add_page(reader.pages[i])
fname = f"extract_{uuid.uuid4().hex[:6]}.pdf"
with open(os.path.join(UPLOAD_DIR, fname), "wb") as f: writer.write(f)
return Div(H3("Extraction Complete!"), A("Download Segment", href=f"/pdf_outputs/{fname}", role="button", target="_blank"))
if __name__ == "__main__":
import uvicorn
# The '0.0.0.0' tells the server to listen to the outside world
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", 7860)))