uxoxo/eb2ab
0
1#!/usr/bin/env python32"""3LLM Ebook Processor - Text Preprocessing Pipeline for TTS4Processes ebooks with LLM instructions, then converts to audiobook5 6Features:7- Multi-format ebook support (EPUB, PDF, MOBI, TXT, etc.)8- Smart text chunking strategies9- Cost & time estimates BEFORE processing10- LLM integration (OpenAI, Anthropic, Ollama)11- TTS integration with progress tracking12- Direct download links for audiobooks13 14Author: Claude Code15Date: 2025-10-1716"""17 18import gradio as gr19import os20import sys21from pathlib import Path22 23# Add lib directory to path for imports24sys.path.insert(0, str(Path(__file__).parent))25 26# Import our processing modules27from lib.ebook_extractor import extract_ebook_text28from lib.text_chunker import chunk_text29from lib.estimator import estimate_costs30from lib.llm_processor import process_with_llm31from lib.tts_integration import submit_to_tts, poll_tts_status32from lib.download_manager import get_download_link33 34# Import event handlers35from app_llm_processor_handlers import (36 handle_analyze,37 handle_confirm_processing,38 handle_llm_processing,39 handle_tts_conversion,40 handle_generate_downloads,41 current_job42)43 44 45def create_app():46 """Create the Gradio application"""47 48 with gr.Blocks(49 title="LLM Ebook Processor + TTS",50 theme=gr.themes.Soft(),51 css="""52 .estimate-box {53 background: #f0f9ff;54 border: 2px solid #0ea5e9;55 border-radius: 8px;56 padding: 20px;57 margin: 10px 0;58 }59 .cost-estimate {60 font-size: 24px;61 font-weight: bold;62 color: #0ea5e9;63 }64 .time-estimate {65 font-size: 20px;66 color: #059669;67 }68 """69 ) as app:70 71 gr.Markdown("""72 # π LLM Ebook Processor + TTS73 74 Transform your ebooks with AI, then convert to audiobooks!75 76 **Complete Pipeline:**77 1. Upload ebook β 2. Configure processing β 3. See estimates β78 4. LLM processing β 5. TTS conversion β 6. Download audiobook79 """)80 81 # =================================================================82 # STEP 1: Upload & Configure83 # =================================================================84 85 with gr.Tab("1οΈβ£ Upload & Configure"):86 gr.Markdown("### π Upload Your Ebook")87 88 ebook_file = gr.File(89 label="Ebook File",90 file_types=[".epub", ".pdf", ".mobi", ".txt", ".html", ".rtf"],91 type="filepath"92 )93 94 gr.Markdown("### π§ Chunking Strategy")95 96 chunk_strategy = gr.Radio(97 choices=[98 "By Chapter (Semantic)",99 "Fixed Size with Overlap",100 "Sentence Boundaries"101 ],102 value="By Chapter (Semantic)",103 label="How to split the book"104 )105 106 with gr.Row():107 chunk_size = gr.Slider(108 minimum=500,109 maximum=8000,110 value=4000,111 step=500,112 label="Chunk Size (tokens)",113 info="Only for Fixed Size strategy"114 )115 116 chunk_overlap = gr.Slider(117 minimum=0,118 maximum=500,119 value=200,120 step=50,121 label="Overlap (tokens)",122 info="Maintains context between chunks"123 )124 125 gr.Markdown("### π Processing Instructions")126 127 instruction_template = gr.Dropdown(128 choices=[129 "Custom (enter below)",130 "Modernize Language",131 "Simplify for Children",132 "Translate to Spanish",133 "Convert to Casual Tone",134 "Remove Old Grammar"135 ],136 value="Custom (enter below)",137 label="Instruction Template"138 )139 140 processing_instruction = gr.Textbox(141 label="Custom Instructions",142 placeholder="e.g., Convert this Victorian-era English to modern lay English while retaining all original meaning and literary nuance",143 lines=3,144 value="Convert this Victorian-era English to modern lay English while retaining all original meaning and literary nuance"145 )146 147 gr.Markdown("### π€ LLM Provider")148 149 llm_provider = gr.Radio(150 choices=[151 "Claude 3 Haiku (Fastest, Cheapest)",152 "Claude 3.5 Sonnet (Balanced)",153 "GPT-4o (Best Quality)",154 "GPT-3.5 Turbo (Fast & Cheap)",155 "Ollama (Free, Local - Slower)"156 ],157 value="Claude 3 Haiku (Fastest, Cheapest)",158 label="Choose LLM Provider"159 )160 161 gr.Markdown("### ποΈ Text-to-Speech")162 163 enable_tts = gr.Checkbox(164 label="Generate audiobook after LLM processing",165 value=True166 )167 168 with gr.Row(visible=True) as tts_options:169 tts_voice = gr.Dropdown(170 choices=["Morgan Freeman", "David Attenborough", "Custom Voice"],171 value="Morgan Freeman",172 label="Voice"173 )174 175 tts_format = gr.Dropdown(176 choices=["M4B", "MP3", "WAV"],177 value="M4B",178 label="Audio Format"179 )180 181 # Button to proceed to estimates182 analyze_btn = gr.Button(183 "π Analyze & Show Estimates",184 variant="primary",185 size="lg"186 )187 188 # =================================================================189 # STEP 2: Review Estimates190 # =================================================================191 192 with gr.Tab("2οΈβ£ Review Estimates") as estimates_tab:193 gr.Markdown("### π Processing Estimates")194 195 with gr.Column(elem_classes="estimate-box"):196 book_info = gr.Markdown("Upload a book to see estimates...")197 198 cost_breakdown = gr.Markdown()199 time_breakdown = gr.Markdown()200 201 alternative_options = gr.Markdown()202 203 with gr.Row():204 cancel_btn = gr.Button("β Cancel", variant="stop")205 edit_btn = gr.Button("βοΈ Edit Settings")206 confirm_btn = gr.Button(207 "β
Confirm & Start Processing",208 variant="primary",209 size="lg"210 )211 212 # =================================================================213 # STEP 3: LLM Processing214 # =================================================================215 216 with gr.Tab("3οΈβ£ LLM Processing") as llm_tab:217 gr.Markdown("### π Processing Book with AI")218 219 llm_status = gr.Markdown("**Phase 1/2:** LLM Text Processing")220 221 llm_progress = gr.Progress()222 llm_progress_bar = gr.Slider(223 minimum=0,224 maximum=100,225 value=0,226 label="Progress",227 interactive=False228 )229 230 llm_current_chunk = gr.Textbox(231 label="Current",232 value="Waiting to start...",233 interactive=False234 )235 236 with gr.Row():237 llm_time_info = gr.Textbox(238 label="β±οΈ Time",239 value="Elapsed: 0s | Remaining: --",240 interactive=False241 )242 243 llm_cost_info = gr.Textbox(244 label="π° Cost",245 value="Spent: $0.00 | Estimated: $0.00",246 interactive=False247 )248 249 with gr.Row():250 pause_llm_btn = gr.Button("βΈοΈ Pause")251 cancel_llm_btn = gr.Button("β Cancel", variant="stop")252 253 # =================================================================254 # STEP 4: TTS Processing255 # =================================================================256 257 with gr.Tab("4οΈβ£ TTS Conversion") as tts_tab:258 gr.Markdown("### π Converting to Audiobook")259 260 tts_status = gr.Markdown("**Phase 2/2:** Text-to-Speech Conversion")261 262 tts_progress_bar = gr.Slider(263 minimum=0,264 maximum=100,265 value=0,266 label="Progress",267 interactive=False268 )269 270 tts_current_chapter = gr.Textbox(271 label="Current",272 value="Waiting for LLM to complete...",273 interactive=False274 )275 276 with gr.Row():277 tts_time_info = gr.Textbox(278 label="β±οΈ Time",279 value="Total: 0 min | Remaining: --",280 interactive=False281 )282 283 tts_cost_info = gr.Textbox(284 label="π° Total Cost",285 value="LLM: $0.00 | TTS: $0.00 | Total: $0.00",286 interactive=False287 )288 289 with gr.Row():290 pause_tts_btn = gr.Button("βΈοΈ Pause")291 cancel_tts_btn = gr.Button("β Cancel", variant="stop")292 293 # =================================================================294 # STEP 5: Download Results295 # =================================================================296 297 with gr.Tab("5οΈβ£ Download") as download_tab:298 gr.Markdown("### β
Processing Complete!")299 300 completion_summary = gr.Markdown()301 302 gr.Markdown("### π₯ Downloads")303 304 with gr.Row():305 processed_text_file = gr.File(306 label="π Processed Text (.txt)",307 interactive=False308 )309 310 processed_epub_file = gr.File(311 label="π Processed EPUB",312 interactive=False313 )314 315 audiobook_file = gr.File(316 label="π§ Audiobook (M4B)",317 interactive=False318 )319 320 with gr.Row():321 mp3_file = gr.File(label="MP3", interactive=False)322 wav_file = gr.File(label="WAV", interactive=False)323 ogg_file = gr.File(label="OGG", interactive=False)324 325 processing_report = gr.Textbox(326 label="π Processing Summary",327 lines=10,328 interactive=False329 )330 331 with gr.Row():332 process_another_btn = gr.Button(333 "π Process Another Book",334 variant="primary"335 )336 save_template_btn = gr.Button("β Save Settings as Template")337 338 # =================================================================339 # Event Handlers340 # =================================================================341 342 # Step 1: Analyze button - Extract, chunk, and estimate costs343 analyze_btn.click(344 fn=handle_analyze,345 inputs=[346 ebook_file,347 chunk_strategy,348 chunk_size,349 chunk_overlap,350 llm_provider,351 enable_tts352 ],353 outputs=[354 book_info,355 cost_breakdown,356 time_breakdown,357 alternative_options358 ]359 )360 361 # Step 2: Confirm button - Start LLM processing362 def start_processing(instruction, provider):363 # Validate first364 validation_msg = handle_confirm_processing(instruction, provider)365 366 # If validation passes, start LLM processing367 if "β
" in validation_msg:368 # Store settings in current_job369 current_job['llm_provider'] = provider370 return handle_llm_processing(instruction, provider)371 else:372 # Return validation error373 return (validation_msg, "Elapsed: 0s | Waiting", "Spent: $0.00 | Waiting")374 375 confirm_btn.click(376 fn=start_processing,377 inputs=[378 processing_instruction,379 llm_provider380 ],381 outputs=[382 llm_status,383 llm_time_info,384 llm_cost_info385 ]386 )387 388 # Step 3: After LLM completes, auto-start TTS (if enabled)389 def auto_start_tts(voice, format_type, tts_enabled):390 if tts_enabled:391 current_job['tts_voice'] = voice392 current_job['tts_format'] = format_type393 return handle_tts_conversion(voice, format_type)394 else:395 # Skip TTS, go straight to downloads396 return (397 "### βοΈ TTS Skipped\n\nTTS was disabled. Proceed to download processed text.",398 "Total: 0 min | Skipped",399 "LLM: $0.00 | TTS: $0.00 | Total: $0.00"400 )401 402 # Wire TTS auto-start to a button for manual trigger403 # (In a real implementation, this would be triggered automatically after LLM completes)404 # For now, we'll need a manual trigger button405 406 # Step 4: Generate downloads407 def prepare_downloads():408 return handle_generate_downloads()409 410 # Add a helper function to load the download tab with data411 def on_download_tab_select():412 if current_job.get('audio_file') or current_job.get('processed_text'):413 return handle_generate_downloads()414 else:415 return (416 "### β³ Waiting for Processing\n\nComplete LLM and TTS processing first.",417 None, None, None, None, ""418 )419 420 # Wire up download tab to auto-populate when selected421 download_tab.select(422 fn=on_download_tab_select,423 outputs=[424 completion_summary,425 processed_text_file,426 audiobook_file,427 mp3_file,428 wav_file,429 processing_report430 ]431 )432 433 # Navigation helpers434 def go_to_tab(tab_name):435 """Helper to switch tabs programmatically"""436 pass # Gradio handles this with .select()437 438 # Process another book - reset state439 def reset_job():440 current_job.clear()441 current_job.update({442 'metadata': None,443 'chapters': None,444 'chunks': None,445 'estimate': None,446 'processed_text': None,447 'job_id': None448 })449 return "Upload a new book to start..."450 451 process_another_btn.click(452 fn=reset_job,453 outputs=[book_info]454 )455 456 return app457 458 459if __name__ == "__main__":460 app = create_app()461 app.launch(462 server_name="0.0.0.0",463 server_port=7861, # Different port from main ebook2audiobook464 share=False,465 show_error=True466 )467 