Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| import tempfile | |
| import gradio as gr | |
| # Add backend directory to sys.path so internal imports work seamlessly | |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), 'backend'))) | |
| from backend.pdf_processor import convert_pdf_to_images | |
| from backend.services.export_service import ExportService | |
| def process_pdf(pdf_file, mineru_api_base, mineru_token, baidu_api_key, baidu_secret_key, max_workers, progress=gr.Progress()): | |
| if not pdf_file: | |
| return None, "Error: No file uploaded." | |
| if not mineru_token: | |
| # Fallback to HF Secrets using exact screenshot names or defaults | |
| mineru_token = os.environ.get("MinerU_Token", os.environ.get("MINERU_TOKEN", "")) | |
| if not mineru_token: | |
| return None, "Error: MinerU Token is required." | |
| if not baidu_api_key: | |
| baidu_api_key = os.environ.get("Baidu_API_Key", os.environ.get("BAIDU_API_KEY", "")) | |
| if not baidu_secret_key: | |
| baidu_secret_key = os.environ.get("Baidu_Secret_Key", os.environ.get("BAIDU_SECRET_KEY", "")) | |
| if not mineru_api_base: | |
| mineru_api_base = os.environ.get("MinerU_API_Base", os.environ.get("MINERU_API_BASE", "https://asemxin2000-mineru.hf.space")) | |
| # Set up environment variables that the backend relies on | |
| os.environ['MINERU_TOKEN'] = mineru_token | |
| os.environ['MINERU_API_BASE'] = mineru_api_base | |
| os.environ['BAIDU_API_KEY'] = baidu_api_key | |
| os.environ['BAIDU_SECRET_KEY'] = baidu_secret_key | |
| # We'll use a local temp upload folder | |
| upload_dir = tempfile.mkdtemp() | |
| os.environ['UPLOAD_FOLDER'] = upload_dir | |
| try: | |
| progress(0, desc="Converting PDF to images...") | |
| # 1. Convert PDF to high-res images | |
| image_paths = convert_pdf_to_images(pdf_file.name, output_dir=upload_dir, dpi=300) | |
| if not image_paths: | |
| return None, "Error: Failed to extract images from PDF." | |
| progress(0.1, desc=f"Extracted {len(image_paths)} pages.") | |
| # Progress callback adapter | |
| def progress_adapter(step: str, message: str, percent: int): | |
| # Gradio progress takes float [0, 1] | |
| # Since pdf to image took 10%, we map the remaining 90% (percent / 100 * 0.9 + 0.1) | |
| mapped_progress = (percent / 100.0) * 0.9 + 0.1 | |
| progress(mapped_progress, desc=f"[{step}] {message}") | |
| # 2. Run the main PPTX conversion pipeline exactly as designed in banana-slides | |
| # Defaulting export strategies to hybrid which combines MinerU and Baidu OCR | |
| pptx_bytes, warnings = ExportService.create_editable_pptx_with_recursive_analysis( | |
| image_paths=image_paths, | |
| output_file=None, # Return bytes | |
| slide_width_pixels=1920, | |
| slide_height_pixels=1080, | |
| max_depth=1, # Depth 1 is generally enough for presentations | |
| max_workers=int(max_workers), | |
| progress_callback=progress_adapter, | |
| export_extractor_method='hybrid' if baidu_api_key else 'mineru', | |
| export_inpaint_method='baidu' if baidu_api_key else 'generative', # generative requires AI model, let's just stick to baidu if we have it | |
| fail_fast=False # tolerate warnings | |
| ) | |
| if not pptx_bytes: | |
| return None, "Error: PPTX generation returned empty bytes." | |
| # Save to a temporary file | |
| output_pptx_path = os.path.join(upload_dir, "output_presentation.pptx") | |
| with open(output_pptx_path, "wb") as f: | |
| f.write(pptx_bytes) | |
| warning_msg = f"Done! {len(warnings.to_summary())} warnings encountered." if warnings.has_warnings() else "Done successfully!" | |
| return output_pptx_path, warning_msg | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| return None, f"An error occurred: {str(e)}" | |
| # Define the Gradio Interface | |
| with gr.Blocks(title="PDF to Editable PPTX") as demo: | |
| gr.Markdown("# 🍌📄 PDF to Editable PPTX Converter") | |
| gr.Markdown("Convert your PDF slides into high-fidelity editable PowerPoint files using OpenXLab MinerU and Baidu OCR APIs.") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| pdf_input = gr.File(label="Upload PDF", file_types=[".pdf"]) | |
| max_workers = gr.Slider(minimum=1, maximum=16, value=2, step=1, label="Max Workers (Parallel Processing)") | |
| with gr.Accordion("API Keys (Optional if set in Space Secrets)", open=False): | |
| mineru_api_base = gr.Textbox(label="MinerU API Base", value=os.environ.get("MinerU_API_Base", os.environ.get("MINERU_API_BASE", "https://asemxin2000-mineru.hf.space")), placeholder="https://asemxin2000-mineru.hf.space", info="MinerU 服务地址(HF Space 或 mineru.net)") | |
| mineru_token = gr.Textbox(label="MinerU Token", type="password", value=os.environ.get("MinerU_Token", os.environ.get("MINERU_TOKEN", ""))) | |
| baidu_api_key = gr.Textbox(label="Baidu API Key", type="password", value=os.environ.get("Baidu_API_Key", os.environ.get("BAIDU_API_KEY", ""))) | |
| baidu_secret = gr.Textbox(label="Baidu Secret Key", type="password", value=os.environ.get("Baidu_Secret_Key", os.environ.get("BAIDU_SECRET_KEY", ""))) | |
| convert_btn = gr.Button("Convert to PPTX", variant="primary") | |
| with gr.Column(scale=1): | |
| status_box = gr.Textbox(label="Status", interactive=False) | |
| pptx_output = gr.File(label="Download PPTX") | |
| convert_btn.click( | |
| fn=process_pdf, | |
| inputs=[pdf_input, mineru_api_base, mineru_token, baidu_api_key, baidu_secret, max_workers], | |
| outputs=[pptx_output, status_box] | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch() | |