Skip to content

Instantly share code, notes, and snippets.

@pswalia2u
Last active August 14, 2026 08:25
Show Gist options
  • Select an option

  • Save pswalia2u/3ae86e24a9ba59bdced011eaa26e2f2b to your computer and use it in GitHub Desktop.

Select an option

Save pswalia2u/3ae86e24a9ba59bdced011eaa26e2f2b to your computer and use it in GitHub Desktop.
instant pdf merger app
#!/bin/bash
set -e
echo "[*] Setting up Python virtual environment..."
python3 -m venv venv
source venv/bin/activate
echo "[*] Installing dependencies..."
pip install --quiet fastapi uvicorn pypdf python-multipart
echo "[*] Generating main.py..."
cat << 'EOF' > main.py
from fastapi import FastAPI, File, UploadFile, HTTPException
from fastapi.responses import Response, HTMLResponse
from pypdf import PdfWriter, PdfReader
import io
app = FastAPI(title="PDF Merge API with GUI")
@app.get("/")
async def serve_gui():
html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PDF Merger Tool</title>
<style>
body { font-family: system-ui, sans-serif; background: #f4f4f9; padding: 2rem; }
.container { max-width: 600px; margin: 0 auto; background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
h2 { margin-top: 0; }
.file-input { margin-bottom: 1rem; }
button { background: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 1rem; }
button:hover { background: #0056b3; }
#status { margin-top: 1rem; font-weight: bold; color: #333; }
</style>
</head>
<body>
<div class="container">
<h2>Merge PDF Files</h2>
<form id="uploadForm">
<input type="file" id="filePicker" class="file-input" name="files" multiple accept="application/pdf" required>
<br>
<button type="submit">Merge and Download</button>
</form>
<div id="status"></div>
</div>
<script>
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
e.preventDefault();
const statusDiv = document.getElementById('status');
const filePicker = document.getElementById('filePicker');
if (filePicker.files.length < 2) {
statusDiv.style.color = "red";
statusDiv.innerText = "Error: Please select at least two PDF files.";
return;
}
statusDiv.style.color = "blue";
statusDiv.innerText = "Processing files...";
const formData = new FormData();
for (const file of filePicker.files) {
formData.append('files', file);
}
try {
const response = await fetch('/merge', {
method: 'POST',
body: formData
});
if (response.ok) {
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = 'merged.pdf';
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(downloadUrl);
statusDiv.style.color = "green";
statusDiv.innerText = "Merge successful! Download starting...";
} else {
const errText = await response.text();
statusDiv.style.color = "red";
statusDiv.innerText = "Server Error: " + errText;
}
} catch (error) {
statusDiv.style.color = "red";
statusDiv.innerText = "Network Error: Could not connect to the API.";
}
});
</script>
</body>
</html>
"""
return HTMLResponse(content=html_content)
@app.post("/merge")
async def merge_pdfs(files: list[UploadFile] = File(...)):
if len(files) < 2:
raise HTTPException(status_code=400, detail="At least two PDF files are required to merge.")
writer = PdfWriter()
try:
for file in files:
if not file.filename.lower().endswith('.pdf'):
raise HTTPException(status_code=400, detail=f"File {file.filename} is not a PDF.")
content = await file.read()
reader = PdfReader(io.BytesIO(content))
for page in reader.pages:
writer.add_page(page)
output_buffer = io.BytesIO()
writer.write(output_buffer)
output_buffer.seek(0)
return Response(
content=output_buffer.getvalue(),
media_type="application/pdf",
headers={"Content-Disposition": "attachment; filename=merged.pdf"}
)
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error processing PDFs: {str(e)}")
EOF
echo "[*] Scheduling browser to launch in 2 seconds..."
(sleep 2 && python3 -m webbrowser "http://127.0.0.1:8000") &
echo "[*] Starting the API and GUI on http://127.0.0.1:8000"
uvicorn main:app --host 127.0.0.1 --port 8000
@pswalia2u

pswalia2u commented Aug 13, 2026

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment