import streamlit as st import os import logging import warnings from io import BytesIO import tempfile import zipfile from pathlib import Path import glob import platform import subprocess from main import ( load_workbook_with_possible_passwords, copy_excel_file, remove_all_protection_tags, setup_logging ) # Configure page st.set_page_config( page_title="Office Protection Remover", page_icon="🔓", layout="wide" ) # Setup logging setup_logging() warnings.filterwarnings('ignore', category=UserWarning, module='openpyxl.reader.workbook') def save_uploaded_file(uploaded_file): """Save an uploaded file to a temporary location""" with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(uploaded_file.name)[1]) as tmp_file: tmp_file.write(uploaded_file.getvalue()) return tmp_file.name def get_file_path(title="Select File", file_types=[("Text Files", "*.txt")]): """Get file path using native file dialog when possible""" try: if platform.system() == "Windows": import tkinter as tk from tkinter import filedialog root = tk.Tk() root.withdraw() # Hide the main window root.attributes('-topmost', True) # Bring dialog to front path = filedialog.askopenfilename(title=title, filetypes=file_types) return path if path else None elif platform.system() == "Linux": # Use zenity for Linux systems try: result = subprocess.run( ['zenity', '--file-selection', '--title', title], capture_output=True, text=True ) return result.stdout.strip() if result.returncode == 0 else None except FileNotFoundError: return None elif platform.system() == "Darwin": # macOS try: result = subprocess.run( ['osascript', '-e', f'choose file with prompt "{title}"'], capture_output=True, text=True ) return result.stdout.strip() if result.returncode == 0 else None except FileNotFoundError: return None except Exception as e: st.error(f"Error opening file dialog: {str(e)}") return None return None def get_directory_path(title="Select Directory"): """Get directory path using native file dialog when possible""" try: if platform.system() == "Windows": import tkinter as tk from tkinter import filedialog root = tk.Tk() root.withdraw() # Hide the main window root.attributes('-topmost', True) # Bring dialog to front path = filedialog.askdirectory(title=title) return path if path else None elif platform.system() == "Linux": # Use zenity for Linux systems try: result = subprocess.run( ['zenity', '--file-selection', '--directory', '--title', title], capture_output=True, text=True ) return result.stdout.strip() if result.returncode == 0 else None except FileNotFoundError: return None elif platform.system() == "Darwin": # macOS try: result = subprocess.run( ['osascript', '-e', f'choose folder with prompt "{title}"'], capture_output=True, text=True ) return result.stdout.strip() if result.returncode == 0 else None except FileNotFoundError: return None except Exception as e: st.error(f"Error opening file dialog: {str(e)}") return None return None def create_zip_file(files_dict): """Create a zip file containing all processed files""" zip_buffer = BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: for filename, content in files_dict.items(): zip_file.writestr(filename, content) return zip_buffer # Main UI st.title("🔓 Office File Protection Remover") st.write("Remove protection from Excel and Word files easily") # Sidebar with st.sidebar: st.header("Settings") file_type = st.radio( "Choose file type:", ("Excel", "Word"), help="Select the type of files you want to process" ) # Main content area st.header(f"{file_type} File Processing") col1, col2 = st.columns(2) with col1: # Input method selection input_method = st.radio( "Choose input method:", ("Upload Files", "Select Directory"), help="Upload files directly or process from local directory" ) if input_method == "Upload Files": uploaded_files = st.file_uploader( f"Upload {file_type} files", type=["xlsx", "xlsm"] if file_type == "Excel" else ["docx", "docm"], accept_multiple_files=True, help=f"You can upload multiple {file_type} files" ) else: # Select Directory source_dir = st.text_input("Source Directory Path", value=st.session_state.get('source_dir', ''), help="Enter the full path to the directory containing files to process") source_browse = st.button("Browse Source Directory") if source_browse: path = get_directory_path("Select Source Directory") if path: st.session_state['source_dir'] = path st.experimental_rerun() dest_dir = st.text_input("Destination Directory Path", value=st.session_state.get('dest_dir', ''), help="Enter the full path where processed files will be saved") dest_browse = st.button("Browse Destination Directory") if dest_browse: path = get_directory_path("Select Destination Directory") if path: st.session_state['dest_dir'] = path st.experimental_rerun() with col2: if file_type == "Excel": password_option = st.radio( "Password Option:", ("No Password", "Single Password", "Password File") ) passwords = [] if password_option == "Single Password": password = st.text_input("Enter password:", type="password") if password: passwords = [password] elif password_option == "Password File": if input_method == "Upload Files": password_file = st.file_uploader("Upload password file", type=["txt"]) if password_file: content = password_file.getvalue().decode() passwords = [line.strip() for line in content.splitlines() if line.strip()] st.info(f"Loaded {len(passwords)} passwords") else: # Select Directory password_path = st.text_input("Password File Path", help="Enter the full path to the text file containing passwords", value=st.session_state.get('password_path', '')) password_browse = st.button("Browse Password File") if password_browse: path = get_file_path("Select Password File", [("Text Files", "*.txt")]) if path: st.session_state['password_path'] = path st.experimental_rerun() if password_path and os.path.exists(password_path): with open(password_path, 'r', encoding='utf-8') as pf: passwords = [line.strip() for line in pf if line.strip()] st.info(f"Loaded {len(passwords)} passwords from file") # Process button and logic if input_method == "Upload Files" and uploaded_files and st.button("Process Files", type="primary"): progress_bar = st.progress(0) status_text = st.empty() processed_files = {} for idx, uploaded_file in enumerate(uploaded_files): try: with st.expander(f"Processing {uploaded_file.name}", expanded=True): st.write(f"📝 Processing {uploaded_file.name}...") temp_input_path = save_uploaded_file(uploaded_file) temp_output_path = f"{temp_input_path}_processed" if file_type == "Excel": copy_excel_file(temp_input_path, temp_output_path, passwords) else: # Word remove_all_protection_tags(temp_input_path, temp_output_path) with open(temp_output_path, "rb") as f: processed_file = f.read() processed_files[f"processed_{uploaded_file.name}"] = processed_file mime_type = ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" if file_type == "Excel" else "application/vnd.openxmlformats-officedocument.wordprocessingml.document") st.download_button( label=f"⬇️ Download processed file", data=processed_file, file_name=f"processed_{uploaded_file.name}", mime=mime_type ) os.unlink(temp_input_path) os.unlink(temp_output_path) st.success("✅ Processing complete!") progress = (idx + 1) / len(uploaded_files) progress_bar.progress(progress) status_text.text(f"Processed {idx + 1} of {len(uploaded_files)} files") except Exception as e: st.error(f"❌ Error processing {uploaded_file.name}: {str(e)}") if len(processed_files) > 1: zip_buffer = create_zip_file(processed_files) st.download_button( label="⬇️ Download all files as ZIP", data=zip_buffer.getvalue(), file_name="processed_files.zip", mime="application/zip", ) elif input_method == "Select Directory" and source_dir and dest_dir and st.button("Process Directory", type="primary"): if not os.path.exists(source_dir): st.error(f"Source directory does not exist: {source_dir}") elif not os.path.exists(os.path.dirname(dest_dir)): st.error(f"Parent of destination directory does not exist: {os.path.dirname(dest_dir)}") else: os.makedirs(dest_dir, exist_ok=True) # Get all files recursively if file_type == "Excel": files = glob.glob(os.path.join(source_dir, "**/*.xlsx"), recursive=True) files.extend(glob.glob(os.path.join(source_dir, "**/*.xlsm"), recursive=True)) else: # Word files = glob.glob(os.path.join(source_dir, "**/*.docx"), recursive=True) files.extend(glob.glob(os.path.join(source_dir, "**/*.docm"), recursive=True)) if not files: st.warning(f"No {file_type} files found in the source directory") else: progress_bar = st.progress(0) status_text = st.empty() for idx, source_path in enumerate(files): try: relative_path = os.path.relpath(source_path, source_dir) dest_path = os.path.join(dest_dir, relative_path) with st.expander(f"Processing {relative_path}", expanded=True): st.write(f"📝 Processing {relative_path}...") os.makedirs(os.path.dirname(dest_path), exist_ok=True) if file_type == "Excel": copy_excel_file(source_path, dest_path, passwords) else: # Word remove_all_protection_tags(source_path, dest_path) st.success("✅ Processing complete!") progress = (idx + 1) / len(files) progress_bar.progress(progress) status_text.text(f"Processed {idx + 1} of {len(files)} files") except Exception as e: st.error(f"❌ Error processing {relative_path}: {str(e)}") st.success(f"✨ All files processed and saved to: {dest_dir}") # Footer st.sidebar.markdown("---") st.sidebar.markdown("### Instructions") st.sidebar.markdown(""" 1. Select file type (Excel or Word) 2. Choose input method: - Upload Files: Process files via web upload - Select Directory: Process files from local directories 3. For Excel files, set password options if needed 4. Click Process button 5. Monitor progress and download processed files """) st.sidebar.markdown("---") st.sidebar.markdown("### About") st.sidebar.markdown(""" This tool helps you remove protection from: - Excel workbooks (.xlsx, .xlsm) - Word documents (.docx, .docm) No files are stored on the server - all processing happens in your browser! """)