Spaces:
Sleeping
Sleeping
| 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 | |
| ) | |
| 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 --- | |
| 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;" | |
| ) | |
| 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 --- | |
| 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)) | |
| 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")) | |
| 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")) | |
| 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))) |