CoolFace
Apppublic

Marek4321/BabelSlide_2.0

sourceHugging Faceapache-2.0updated 8mo agoView on Hugging Face
2likes
app.py416 linesDownload Raw Back to root
1#!/usr/bin/env python32"""3BabelSlide v2.0 - Professional Document Translator4Streamlit application for translating PDF, DOCX, and PPTX documents using AI5"""6 7import streamlit as st8import tempfile9from pathlib import Path10import sys11import os12 13from translators.chatgpt_translator import ChatGPTTranslator14from translators.deepseek_translator import DeepSeekTranslator15from processors.pdf_processor import PDFProcessor16from processors.docx_processor import DOCXProcessor17from processors.pptx_processor import PPTXProcessor18from utils.constants import LANGUAGES, API_PROVIDERS19from utils.validator import FileValidator20from utils.logger import setup_logger, ProcessLogger21from core.exceptions import (22    BabelSlideException, 23    ValidationError, 24    UnsupportedFileError, 25    FileSizeError,26    APIKeyError,27    TranslationError,28    ProcessorError29)30 31class BabelSlideStreamlitApp:32    """Streamlit interface for BabelSlide application"""33    34    def __init__(self):35        self.logger = setup_logger("BabelSlideUI")36        self.process_logger = ProcessLogger(self.logger)37        38        # Initialize session state39        if 'processing' not in st.session_state:40            st.session_state.processing = False41        if 'translation_result' not in st.session_state:42            st.session_state.translation_result = None43        if 'review_result' not in st.session_state:44            st.session_state.review_result = None45    46    def setup_page_config(self):47        """Configure Streamlit page"""48        st.set_page_config(49            page_title="BabelSlide - Document Translator",50            page_icon="๐ŸŒ",51            layout="wide",52            initial_sidebar_state="expanded"53        )54        55        # Custom CSS56        st.markdown("""57        <style>58        .main-header {59            text-align: center;60            padding: 2rem 0;61            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);62            color: white;63            border-radius: 12px;64            margin-bottom: 2rem;65        }66        67        .success-box {68            background: #d1fae5;69            border: 1px solid #10b981;70            border-radius: 8px;71            padding: 1rem;72            margin: 1rem 0;73        }74        75        .error-box {76            background: #fef2f2;77            border: 1px solid #ef4444;78            border-radius: 8px;79            padding: 1rem;80            margin: 1rem 0;81        }82        83        .info-box {84            background: #eff6ff;85            border: 1px solid #3b82f6;86            border-radius: 8px;87            padding: 1rem;88            margin: 1rem 0;89        }90        91        .stButton > button {92            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);93            color: white;94            border: none;95            border-radius: 8px;96            padding: 0.5rem 2rem;97            font-weight: 600;98        }99        </style>100        """, unsafe_allow_html=True)101    102    def render_header(self):103        """Render application header"""104        st.markdown("""105        <div class="main-header">106            <h1>๐ŸŒ BabelSlide v2.0</h1>107            <p>Professional Document Translation using AI โ€ข PDF โ€ข DOCX โ€ข PPTX</p>108        </div>109        """, unsafe_allow_html=True)110    111    def render_sidebar(self):112        """Render configuration sidebar"""113        st.sidebar.markdown("## โš™๏ธ Configuration")114        115        # API Provider116        api_provider = st.sidebar.selectbox(117            "AI Provider",118            options=list(API_PROVIDERS.keys()),119            index=0,120            help="Choose your preferred translation AI"121        )122        123        # API Key124        api_key = st.sidebar.text_input(125            "API Key",126            type="password",127            placeholder="Enter your API key (sk-... for OpenAI)",128            help="Your API key is never stored permanently"129        )130        131        st.sidebar.markdown("---")132        133        # Languages134        col1, col2 = st.sidebar.columns(2)135        136        with col1:137            source_lang = st.selectbox(138                "Source Language",139                options=list(LANGUAGES.keys()),140                index=list(LANGUAGES.keys()).index("English"),141                help="Language of the original document"142            )143        144        with col2:145            target_lang = st.selectbox(146                "Target Language",147                options=list(LANGUAGES.keys()),148                index=list(LANGUAGES.keys()).index("Polish"),149                help="Language to translate to"150            )151        152        st.sidebar.markdown("---")153        st.sidebar.markdown("### ๐Ÿ“ Supported Formats")154        st.sidebar.info("โ€ข PDF documents\nโ€ข DOCX (Word) files\nโ€ข PPTX (PowerPoint) presentations")155        st.sidebar.warning("Maximum file size: 50 MB")156        157        return api_provider, api_key, source_lang, target_lang158    159    def render_file_upload(self):160        """Render file upload section"""161        st.markdown("## ๐Ÿ“„ Document Upload")162        163        uploaded_file = st.file_uploader(164            "Choose a document to translate",165            type=['pdf', 'docx', 'pptx'],166            help="Upload PDF, DOCX, or PPTX files (max 50 MB)",167            accept_multiple_files=False168        )169        170        if uploaded_file:171            col1, col2, col3 = st.columns([2, 1, 1])172            with col1:173                st.info(f"๐Ÿ“ **File:** {uploaded_file.name}")174            with col2:175                file_size = len(uploaded_file.getvalue()) / (1024 * 1024)176                st.info(f"๐Ÿ“ **Size:** {file_size:.1f} MB")177            with col3:178                file_type = uploaded_file.name.split('.')[-1].upper()179                st.info(f"๐Ÿ“‹ **Type:** {file_type}")180        181        return uploaded_file182    183    def validate_inputs(self, file, api_provider, api_key, source_lang, target_lang):184        """Validate all inputs before processing"""185        errors = []186        187        if not file:188            errors.append("Please upload a document")189        190        if not api_key or not api_key.strip():191            errors.append("Please provide an API key")192        193        if source_lang == target_lang:194            errors.append("Source and target languages must be different")195        196        # Validate file if provided197        if file:198            try:199                # Create temporary file for validation200                with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file.name.split('.')[-1]}") as tmp_file:201                    tmp_file.write(file.getvalue())202                    tmp_file_path = Path(tmp_file.name)203                204                FileValidator.validate_file(tmp_file_path)205                tmp_file_path.unlink()  # Clean up206                207            except (ValidationError, UnsupportedFileError, FileSizeError) as e:208                errors.append(f"File validation error: {str(e)}")209        210        # Validate API key format211        try:212            if api_key:213                FileValidator.validate_api_key(api_key.strip(), api_provider)214        except ValidationError as e:215            errors.append(f"API key error: {str(e)}")216        217        return errors218    219    def process_document(self, file, api_provider, api_key, source_lang, target_lang):220        """Process document translation"""221        try:222            # Create temporary file223            with tempfile.NamedTemporaryFile(delete=False, suffix=f".{file.name.split('.')[-1]}") as tmp_file:224                tmp_file.write(file.getvalue())225                tmp_file_path = Path(tmp_file.name)226            227            # Create translator228            if api_provider == "ChatGPT":229                translator = ChatGPTTranslator(api_key.strip())230            elif api_provider == "DeepSeek":231                translator = DeepSeekTranslator(api_key.strip())232            else:233                raise ValueError(f"Unsupported provider: {api_provider}")234            235            # Create processor based on file extension236            extension = tmp_file_path.suffix.lower()237            if extension == '.pdf':238                processor = PDFProcessor(translator)239            elif extension == '.docx':240                processor = DOCXProcessor(translator)241            elif extension == '.pptx':242                processor = PPTXProcessor(translator)243            else:244                raise ValueError(f"Unsupported file format: {extension}")245            246            # Progress tracking247            progress_bar = st.progress(0)248            status_text = st.empty()249            250            def progress_callback(progress_val, desc):251                progress_bar.progress(progress_val)252                status_text.text(desc)253            254            # Process document255            status_text.text("Starting translation...")256            output_path, summary_text = processor.process_document(257                tmp_file_path,258                source_lang,259                target_lang,260                progress_callback261            )262            263            # Generate review264            status_text.text("Generating review...")265            review_text = self.generate_review(summary_text, source_lang, translator)266            267            # Clean up temp file268            tmp_file_path.unlink()269            270            progress_bar.progress(1.0)271            status_text.text("โœ… Translation completed!")272            273            return output_path, review_text, summary_text274            275        except Exception as e:276            self.logger.error(f"Translation error: {str(e)}")277            raise278    279    def generate_review(self, translated_text: str, source_lang: str, translator) -> str:280        """Generate translation review"""281        try:282            system_prompt = f"""Generate a comprehensive translation review in {source_lang} covering:283            1. Translation quality assessment284            2. Coherence and consistency285            3. Technical terminology accuracy  286            4. Overall readability287            5. Recommendations for improvement288            289            Keep the review concise but informative."""290            291            # Use translator's API to generate review292            review = translator._make_translation_request(293                f"Review this translated text:\n\n{translated_text[:2000]}...",294                "English",295                source_lang296            )297            298            return translator._clean_translation_output(review)299            300        except Exception as e:301            return f"Review generation failed: {str(e)}"302    303    def render_results(self):304        """Render translation results"""305        if st.session_state.translation_result:306            st.markdown("## ๐Ÿ“ฅ Results")307            308            col1, col2 = st.columns(2)309            310            with col1:311                st.markdown("### ๐Ÿ“„ Translated Document")312                if st.session_state.translation_result:313                    with open(st.session_state.translation_result, 'rb') as file:314                        st.download_button(315                            label="โฌ‡๏ธ Download Translated Document",316                            data=file.read(),317                            file_name=Path(st.session_state.translation_result).name,318                            mime="application/octet-stream"319                        )320            321            with col2:322                st.markdown("### ๐Ÿ“‹ Translation Review")323                if st.session_state.review_result:324                    st.download_button(325                        label="โฌ‡๏ธ Download Review",326                        data=st.session_state.review_result,327                        file_name="translation_review.txt",328                        mime="text/plain"329                    )330            331            # Summary332            if hasattr(st.session_state, 'summary_text') and st.session_state.summary_text:333                st.markdown("### ๐Ÿ“ Translation Summary")334                with st.expander("View Summary", expanded=False):335                    st.text_area(336                        "Summary",337                        value=st.session_state.summary_text[:1000] + "..." if len(st.session_state.summary_text) > 1000 else st.session_state.summary_text,338                        height=200,339                        disabled=True,340                        label_visibility="collapsed"341                    )342    343    def run(self):344        """Main application loop"""345        self.setup_page_config()346        self.render_header()347        348        # Sidebar configuration349        api_provider, api_key, source_lang, target_lang = self.render_sidebar()350        351        # Main content352        uploaded_file = self.render_file_upload()353        354        # Translation button355        st.markdown("---")356        col1, col2, col3 = st.columns([1, 2, 1])357        with col2:358            translate_button = st.button(359                "๐Ÿš€ Translate Document",360                disabled=st.session_state.processing,361                use_container_width=True362            )363        364        # Process translation365        if translate_button:366            # Validate inputs367            errors = self.validate_inputs(uploaded_file, api_provider, api_key, source_lang, target_lang)368            369            if errors:370                st.error("โŒ **Please fix the following errors:**")371                for error in errors:372                    st.error(f"โ€ข {error}")373            else:374                st.session_state.processing = True375                376                try:377                    with st.spinner("Translating document..."):378                        output_path, review_text, summary_text = self.process_document(379                            uploaded_file, api_provider, api_key, source_lang, target_lang380                        )381                    382                    # Store results383                    st.session_state.translation_result = output_path384                    st.session_state.review_result = review_text385                    st.session_state.summary_text = summary_text386                    387                    st.success(f"โœ… **Translation completed successfully!**\n\n"388                              f"๐Ÿ“„ **File:** {uploaded_file.name}\n"389                              f"๐Ÿ”„ **Translation:** {source_lang} โ†’ {target_lang}\n"390                              f"๐Ÿค– **Provider:** {api_provider}")391                    392                    # Auto-refresh to show results393                    st.rerun()394                    395                except Exception as e:396                    st.error(f"โŒ **Translation failed:** {str(e)}")397                398                finally:399                    st.session_state.processing = False400        401        # Show results if available402        self.render_results()403        404        # Footer405        st.markdown("---")406        st.markdown(407            "<div style='text-align: center; color: #666;'>"408            "<strong>BabelSlide v2.0</strong> โ€ข Professional document translation โ€ข Built for global communication"409            "</div>",410            unsafe_allow_html=True411        )412 413# Main entry point414if __name__ == "__main__":415    app = BabelSlideStreamlitApp()416    app.run()