import sys import os import json import threading import shutil from pathlib import Path from datetime import datetime from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QFileDialog, QComboBox, QProgressBar, QTextEdit, QTabWidget, QGroupBox, QGridLayout, QMessageBox, QListWidget, QListWidgetItem, QCheckBox, QSpinBox, QLineEdit, QSplitter, QFrame, QToolButton, QSizePolicy ) from PyQt6.QtCore import Qt, QThread, pyqtSignal, QSize, QPropertyAnimation, QEasingCurve, QRect from PyQt6.QtGui import QFont, QIcon, QDragEnterEvent, QDropEvent, QPalette, QColor, QAction, QBrush, QPen, QPainter, \ QPixmap # Fix FFmpeg before any imports import warnings warnings.filterwarnings("ignore", message="Couldn't find ffmpeg or avconv") # Setup FFmpeg ffmpeg_path = None for path in [ shutil.which('ffmpeg'), shutil.which('ffmpeg.exe'), r"C:\ffmpeg\bin\ffmpeg.exe", r"C:\Program Files\ffmpeg\bin\ffmpeg.exe" ]: if path and os.path.exists(path): ffmpeg_path = path os.environ["FFMPEG_BINARY"] = ffmpeg_path break # Import converters from converters.document_converter import DocumentConverter from converters.image_converter import ImageConverter from converters.video_converter import VideoConverter from converters.audio_converter import AudioConverter from ai.ai_assistant import AIAssistant class ConversionThread(QThread): progress_update = pyqtSignal(int) status_update = pyqtSignal(str) conversion_complete = pyqtSignal(bool, str) def __init__(self, converter, input_path, output_path, options=None): super().__init__() self.converter = converter self.input_path = input_path self.output_path = output_path self.options = options or {} def run(self): try: self.status_update.emit(f"Converting: {os.path.basename(self.input_path)}") success = self.converter.convert( self.input_path, self.output_path, self.options, self.progress_update ) if success: self.conversion_complete.emit(True, f"✅ Successfully converted: {os.path.basename(self.input_path)}") else: self.conversion_complete.emit(False, f"❌ Failed to convert: {os.path.basename(self.input_path)}") except Exception as e: self.conversion_complete.emit(False, f"❌ Error: {str(e)}") class FileConverterApp(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("AI File Converter Pro") self.setGeometry(100, 100, 1400, 900) # ===== ADD LOGO SUPPORT ===== self.setup_logo() # ============================ # Sidebar state self.sidebar_visible = True # Set modern style self.setStyleSheet(""" QMainWindow { background-color: #1e1e2e; } QGroupBox { font-weight: bold; border: 2px solid #313244; border-radius: 8px; margin-top: 10px; padding-top: 10px; color: #cdd6f4; font-size: 13px; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px 0 5px; color: #89b4fa; } QPushButton { background-color: #89b4fa; border: none; padding: 8px; border-radius: 6px; font-weight: bold; color: #1e1e2e; } QPushButton:hover { background-color: #b4befe; } QPushButton:pressed { background-color: #6c7086; } QToolButton { background-color: #313244; border: 1px solid #45475a; border-radius: 6px; padding: 8px; color: #cdd6f4; } QToolButton:hover { background-color: #45475a; } QListWidget, QTextEdit, QLineEdit, QComboBox { background-color: #313244; border: 1px solid #45475a; border-radius: 6px; padding: 5px; color: #cdd6f4; } QProgressBar { border: 2px solid #45475a; border-radius: 6px; text-align: center; color: #cdd6f4; } QProgressBar::chunk { background-color: #89b4fa; border-radius: 4px; } QTabWidget::pane { background-color: #1e1e2e; border: 1px solid #313244; border-radius: 6px; } QTabBar::tab { background-color: #313244; padding: 8px; margin: 2px; border-radius: 4px; color: #cdd6f4; } QTabBar::tab:selected { background-color: #89b4fa; color: #1e1e2e; } QLabel { color: #cdd6f4; } QCheckBox { color: #cdd6f4; } QSplitter::handle { background-color: #313244; margin: 2px; } QSplitter::handle:hover { background-color: #89b4fa; } """) # Initialize converters and other components... self.doc_converter = DocumentConverter() self.img_converter = ImageConverter() self.video_converter = VideoConverter() self.audio_converter = AudioConverter() self.ai_assistant = AIAssistant() # Conversion queue self.conversion_queue = [] self.current_conversion = None self.active_threads = [] self.converted_count = 0 self.failed_count = 0 self.setup_ui() self.setup_drag_drop() self.setup_menu_bar() def setup_logo(self): """Setup window icon and UI logo""" # Try multiple locations for the logo file logo_paths = [ Path(__file__).parent / "logo.png", Path(__file__).parent / "logo.ico", Path(__file__).parent / "assets" / "logo.png", Path(__file__).parent / "assets" / "logo.ico", # For PyInstaller bundled app Path(sys.executable).parent / "logo.png", Path(sys.executable).parent / "logo.ico", Path(sys.executable).parent / "assets" / "logo.png", ] icon_loaded = False for logo_path in logo_paths: if logo_path.exists(): # Set window icon (appears in title bar and taskbar) self.setWindowIcon(QIcon(str(logo_path))) icon_loaded = True print(f"✅ Logo loaded from: {logo_path}") break if not icon_loaded: # Create default icon programmatically self.setWindowIcon(self.create_default_icon()) print("⚠️ Using default generated icon. Place 'logo.png' in the application directory for custom logo.") def create_default_icon(self): """Create a default icon programmatically""" pixmap = QPixmap(256, 256) pixmap.fill(Qt.GlobalColor.transparent) painter = QPainter(pixmap) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # Draw circular background painter.setBrush(QBrush(QColor(137, 180, 250))) # #89b4fa painter.setPen(Qt.PenStyle.NoPen) painter.drawEllipse(20, 20, 216, 216) # Draw inner circle painter.setBrush(QBrush(QColor(30, 30, 46))) # #1e1e2e painter.drawEllipse(40, 40, 176, 176) # Draw "AI" text painter.setPen(QPen(QColor(137, 180, 250), 2)) font = QFont("Arial", 80, QFont.Weight.Bold) painter.setFont(font) painter.drawText(QRect(40, 70, 176, 100), Qt.AlignmentFlag.AlignCenter, "AI") # Draw conversion arrows painter.setPen(QPen(QColor(166, 227, 161), 5)) # #a6e3a1 painter.setBrush(Qt.BrushStyle.NoBrush) # Right arrow painter.drawLine(180, 180, 210, 195) painter.drawLine(210, 195, 180, 210) # Left arrow painter.drawLine(76, 180, 46, 195) painter.drawLine(46, 195, 76, 210) painter.end() return QIcon(pixmap) def create_ui_logo(self, size=48): """Create a logo widget for the UI""" logo_label = QLabel() # Try to load from file first logo_path = Path(__file__).parent / "logo.png" if not logo_path.exists() and hasattr(sys, 'frozen'): # If running as bundled executable logo_path = Path(sys.executable).parent / "logo.png" if logo_path.exists(): pixmap = QPixmap(str(logo_path)) scaled_pixmap = pixmap.scaled(size, size, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) logo_label.setPixmap(scaled_pixmap) else: # Create programmatic logo pixmap = QPixmap(size, size) pixmap.fill(Qt.GlobalColor.transparent) painter = QPainter(pixmap) painter.setRenderHint(QPainter.RenderHint.Antialiasing) # Draw circle painter.setBrush(QBrush(QColor(137, 180, 250))) painter.setPen(Qt.PenStyle.NoPen) painter.drawEllipse(4, 4, size - 8, size - 8) # Draw inner circle painter.setBrush(QBrush(QColor(30, 30, 46))) painter.drawEllipse(8, 8, size - 16, size - 16) # Draw text painter.setPen(QPen(QColor(137, 180, 250), 1)) font = QFont("Arial", size // 3, QFont.Weight.Bold) painter.setFont(font) painter.drawText(QRect(8, 8, size - 16, size - 16), Qt.AlignmentFlag.AlignCenter, "AI") painter.end() logo_label.setPixmap(pixmap) logo_label.setFixedSize(size, size) logo_label.setToolTip("AI File Converter Pro") return logo_label def setup_ui(self): central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QHBoxLayout(central_widget) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) # Create splitter for resizable panels self.main_splitter = QSplitter(Qt.Orientation.Horizontal) # Left panel - Main content (WITH LOGO IN TOP-LEFT CORNER) self.left_panel = self.create_left_panel_with_logo() self.main_splitter.addWidget(self.left_panel) # Right panel - AI Sidebar (collapsible) self.sidebar_widget = self.create_sidebar() self.main_splitter.addWidget(self.sidebar_widget) # Set initial sizes (70% left, 30% right) self.main_splitter.setSizes([980, 420]) main_layout.addWidget(self.main_splitter) def setup_menu_bar(self): """Setup menu bar with toggle option""" menubar = self.menuBar() menubar.setStyleSheet(""" QMenuBar { background-color: #1e1e2e; color: #cdd6f4; border-bottom: 1px solid #313244; } QMenuBar::item:selected { background-color: #313244; } QMenu { background-color: #1e1e2e; color: #cdd6f4; border: 1px solid #313244; } QMenu::item:selected { background-color: #89b4fa; color: #1e1e2e; } """) # File menu file_menu = menubar.addMenu("📁 File") exit_action = QAction("🚪 Exit", self) exit_action.setShortcut("Ctrl+Q") exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) # View menu view_menu = menubar.addMenu("👁️ View") toggle_action = QAction("🤖 Toggle AI Assistant", self) toggle_action.setShortcut("Ctrl+Shift+A") toggle_action.triggered.connect(self.toggle_sidebar) view_menu.addAction(toggle_action) view_menu.addSeparator() reset_view_action = QAction("🔄 Reset Layout", self) reset_view_action.setShortcut("Ctrl+Shift+R") reset_view_action.triggered.connect(self.reset_layout) view_menu.addAction(reset_view_action) # Help menu help_menu = menubar.addMenu("❓ Help") about_action = QAction("📖 About", self) about_action.setShortcut("F1") about_action.triggered.connect(self.show_about) help_menu.addAction(about_action) def show_about(self): """Show about dialog with logo""" about_text = """

🎯 AI File Converter Pro

Version 2.0

Advanced file conversion tool with AI assistance


Features:

• Convert documents, images, videos, and audio

• AI-powered format recommendations

• Natural language commands

• Batch processing support

• Quality optimization


© 2024 AI File Converter Pro

All rights reserved

""" about_dialog = QMessageBox(self) about_dialog.setWindowTitle("About AI File Converter Pro") about_dialog.setText(about_text) # Add logo to about dialog logo_path = Path(__file__).parent / "logo.png" if not logo_path.exists() and hasattr(sys, 'frozen'): logo_path = Path(sys.executable).parent / "logo.png" if logo_path.exists(): logo_pixmap = QPixmap(str(logo_path)).scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio, Qt.TransformationMode.SmoothTransformation) about_dialog.setIconPixmap(logo_pixmap) about_dialog.exec() def create_left_panel_with_logo(self): """Create the main left panel with logo in top-left corner""" left_panel = QWidget() left_layout = QVBoxLayout(left_panel) left_layout.setSpacing(10) left_layout.setContentsMargins(10, 10, 10, 10) # ===== ADD LOGO HEADER SECTION ===== header_widget = QWidget() header_layout = QHBoxLayout(header_widget) header_layout.setContentsMargins(0, 0, 0, 10) # Add logo to top-left corner logo_widget = self.create_ui_logo(48) header_layout.addWidget(logo_widget) # Add title next to logo title_label = QLabel("AI File Converter Pro") title_label.setStyleSheet(""" font-size: 20px; font-weight: bold; color: #89b4fa; padding: 5px; """) header_layout.addWidget(title_label) header_layout.addStretch() # Optional: Add version label version_label = QLabel("v2.0") version_label.setStyleSheet("color: #6c7086; font-size: 12px;") header_layout.addWidget(version_label) left_layout.addWidget(header_widget) # ================================== # File selection area file_group = QGroupBox("📁 Files to Convert") file_layout = QVBoxLayout() self.file_list = QListWidget() self.file_list.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection) self.file_list.setMinimumHeight(200) file_layout.addWidget(self.file_list) btn_layout = QHBoxLayout() add_btn = QPushButton("➕ Add Files") add_btn.clicked.connect(self.add_files) add_folder_btn = QPushButton("📂 Add Folder") add_folder_btn.clicked.connect(self.add_folder) remove_btn = QPushButton("❌ Remove Selected") remove_btn.clicked.connect(self.remove_selected) clear_btn = QPushButton("🗑️ Clear All") clear_btn.clicked.connect(self.clear_files) btn_layout.addWidget(add_btn) btn_layout.addWidget(add_folder_btn) btn_layout.addWidget(remove_btn) btn_layout.addWidget(clear_btn) file_layout.addLayout(btn_layout) file_group.setLayout(file_layout) left_layout.addWidget(file_group) # Converter settings converter_group = QGroupBox("⚙️ Converter Settings") converter_layout = QGridLayout() converter_layout.setSpacing(10) converter_layout.addWidget(QLabel("File Type:"), 0, 0) self.file_type_combo = QComboBox() self.file_type_combo.addItems(["Document", "Image", "Video", "Audio"]) self.file_type_combo.currentTextChanged.connect(self.on_file_type_changed) converter_layout.addWidget(self.file_type_combo, 0, 1) converter_layout.addWidget(QLabel("Output Format:"), 1, 0) self.output_format_combo = QComboBox() converter_layout.addWidget(self.output_format_combo, 1, 1) converter_layout.addWidget(QLabel("Quality:"), 2, 0) self.quality_combo = QComboBox() self.quality_combo.addItems(["High", "Medium", "Low"]) converter_layout.addWidget(self.quality_combo, 2, 1) converter_layout.addWidget(QLabel("Output Folder:"), 3, 0) self.output_folder = QLineEdit() self.output_folder.setText(str(Path.home() / "Downloads" / "Converted")) converter_layout.addWidget(self.output_folder, 3, 1) browse_btn = QPushButton("📁 Browse") browse_btn.clicked.connect(self.browse_output_folder) converter_layout.addWidget(browse_btn, 3, 2) # Create output folder if not exists Path(self.output_folder.text()).mkdir(parents=True, exist_ok=True) self.ai_checkbox = QCheckBox("🤖 Enable AI Enhancement") self.ai_checkbox.setChecked(False) converter_layout.addWidget(self.ai_checkbox, 4, 0, 1, 2) converter_group.setLayout(converter_layout) left_layout.addWidget(converter_group) # Progress section progress_group = QGroupBox("📊 Conversion Progress") progress_layout = QVBoxLayout() self.progress_bar = QProgressBar() progress_layout.addWidget(self.progress_bar) self.status_label = QLabel("✅ Ready to convert") self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter) progress_layout.addWidget(self.status_label) self.stats_label = QLabel("📈 Converted: 0 | ❌ Failed: 0") self.stats_label.setAlignment(Qt.AlignmentFlag.AlignCenter) progress_layout.addWidget(self.stats_label) progress_group.setLayout(progress_layout) left_layout.addWidget(progress_group) # Convert button self.convert_btn = QPushButton("🚀 Start Conversion") self.convert_btn.setMinimumHeight(50) self.convert_btn.setStyleSheet(""" QPushButton { background-color: #a6e3a1; font-size: 16px; font-weight: bold; } QPushButton:hover { background-color: #94e2d5; } """) self.convert_btn.clicked.connect(self.start_conversion) left_layout.addWidget(self.convert_btn) return left_panel def create_sidebar(self): """Create the collapsible AI sidebar""" sidebar_container = QWidget() sidebar_layout = QVBoxLayout(sidebar_container) sidebar_layout.setContentsMargins(0, 0, 0, 0) sidebar_layout.setSpacing(0) # Header with toggle button header = QWidget() header.setStyleSheet(""" QWidget { background-color: #313244; border-bottom: 1px solid #45475a; } """) header_layout = QHBoxLayout(header) header_layout.setContentsMargins(10, 10, 10, 10) title_label = QLabel("🤖 AI Assistant") title_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #89b4fa;") header_layout.addWidget(title_label) header_layout.addStretch() # Close button self.close_sidebar_btn = QToolButton() self.close_sidebar_btn.setText("◀") self.close_sidebar_btn.setToolTip("Close Sidebar (Ctrl+Shift+A)") self.close_sidebar_btn.setStyleSheet(""" QToolButton { background-color: #45475a; border: none; font-size: 14px; padding: 5px; } QToolButton:hover { background-color: #6c7086; } """) self.close_sidebar_btn.clicked.connect(self.toggle_sidebar) header_layout.addWidget(self.close_sidebar_btn) sidebar_layout.addWidget(header) # Tab widget for AI and Log right_panel = QTabWidget() right_panel.setStyleSheet(""" QTabWidget::pane { border: none; } """) # AI Assistant tab ai_widget = QWidget() ai_layout = QVBoxLayout(ai_widget) ai_layout.setSpacing(10) # Suggestions panel suggestions_group = QGroupBox("💡 Quick Commands") suggestions_layout = QGridLayout() suggestions_group.setMaximumHeight(220) suggestions = [ ("🖼️ To PNG", "convert this to png"), ("📄 To PDF", "convert to pdf"), ("🎵 To MP3", "make it mp3"), ("🎬 Web format", "best format for web"), ("📊 Analyze", "analyze this file"), ("💾 Compress", "how to compress video"), ("📧 Email size", "best format for email"), ("🖨️ Print", "best format for printing"), ] row, col = 0, 0 for text, cmd in suggestions: btn = QPushButton(text) btn.setMaximumHeight(30) btn.clicked.connect(lambda checked, c=cmd: self.ai_input.setText(c)) suggestions_layout.addWidget(btn, row, col) col += 1 if col >= 2: col = 0 row += 1 suggestions_group.setLayout(suggestions_layout) ai_layout.addWidget(suggestions_group) # Input area self.ai_input = QTextEdit() self.ai_input.setPlaceholderText("""💬 Ask me anything about file conversion... Examples: • "Convert this to PNG" • "What's the best format for web images?" • "How to compress video for email?" • "Analyze this file" • "Make it smaller without losing quality" """) self.ai_input.setMaximumHeight(100) ai_layout.addWidget(self.ai_input) # Buttons ai_buttons_layout = QHBoxLayout() ai_send_btn = QPushButton("💭 Ask AI") ai_send_btn.clicked.connect(self.ask_ai_assistant) ai_clear_btn = QPushButton("🗑️ Clear") ai_clear_btn.clicked.connect(lambda: self.ai_input.clear()) ai_buttons_layout.addWidget(ai_send_btn) ai_buttons_layout.addWidget(ai_clear_btn) ai_layout.addLayout(ai_buttons_layout) # Response area self.ai_response = QTextEdit() self.ai_response.setReadOnly(True) self.ai_response.setPlaceholderText("AI response will appear here...") self.ai_response.setMinimumHeight(200) ai_layout.addWidget(self.ai_response) right_panel.addTab(ai_widget, "💬 Assistant") # Log tab log_widget = QWidget() log_layout = QVBoxLayout(log_widget) self.log_text = QTextEdit() self.log_text.setReadOnly(True) log_layout.addWidget(self.log_text) log_btn_layout = QHBoxLayout() clear_log_btn = QPushButton("🗑️ Clear Log") clear_log_btn.clicked.connect(lambda: self.log_text.clear()) log_btn_layout.addStretch() log_btn_layout.addWidget(clear_log_btn) log_layout.addLayout(log_btn_layout) right_panel.addTab(log_widget, "📝 Log") sidebar_layout.addWidget(right_panel) return sidebar_container def toggle_sidebar(self): """Toggle the AI sidebar visibility""" self.sidebar_visible = not self.sidebar_visible if self.sidebar_visible: self.sidebar_widget.setVisible(True) self.close_sidebar_btn.setText("◀") self.main_splitter.setSizes([980, 420]) else: self.sidebar_widget.setVisible(False) self.close_sidebar_btn.setText("▶") self.main_splitter.setSizes([1400, 0]) # Update tooltip self.close_sidebar_btn.setToolTip( "Open Sidebar (Ctrl+Shift+A)" if not self.sidebar_visible else "Close Sidebar (Ctrl+Shift+A)") def reset_layout(self): """Reset layout to default""" self.main_splitter.setSizes([980, 420]) if not self.sidebar_visible: self.toggle_sidebar() self.log_message("🔄 Layout reset to default") def setup_drag_drop(self): self.setAcceptDrops(True) def dragEnterEvent(self, event: QDragEnterEvent): if event.mimeData().hasUrls(): event.acceptProposedAction() def dropEvent(self, event: QDropEvent): for url in event.mimeData().urls(): file_path = url.toLocalFile() if os.path.isfile(file_path): self.add_file_to_list(file_path) def add_files(self): files, _ = QFileDialog.getOpenFileNames( self, "Select Files to Convert", "", "All Files (*.*)" ) for file in files: self.add_file_to_list(file) def add_folder(self): folder = QFileDialog.getExistingDirectory(self, "Select Folder") if folder: for root, dirs, files in os.walk(folder): for file in files: self.add_file_to_list(os.path.join(root, file)) def remove_selected(self): for item in self.file_list.selectedItems(): self.file_list.takeItem(self.file_list.row(item)) def add_file_to_list(self, file_path): # Check if already in list for i in range(self.file_list.count()): if self.file_list.item(i).data(Qt.ItemDataRole.UserRole) == file_path: return item = QListWidgetItem(f"📄 {os.path.basename(file_path)}") item.setToolTip(file_path) item.setData(Qt.ItemDataRole.UserRole, file_path) self.file_list.addItem(item) def clear_files(self): self.file_list.clear() def on_file_type_changed(self, file_type): self.output_format_combo.clear() formats = { "Document": ["pdf", "docx", "txt", "html", "md"], "Image": ["png", "jpg", "webp", "bmp", "tiff"], "Video": ["mp4", "avi", "mkv", "mov", "webm", "gif"], "Audio": ["mp3", "wav", "ogg", "m4a", "flac"] } self.output_format_combo.addItems(formats.get(file_type, [])) def browse_output_folder(self): folder = QFileDialog.getExistingDirectory(self, "Select Output Folder") if folder: self.output_folder.setText(folder) Path(folder).mkdir(parents=True, exist_ok=True) def get_converter(self): file_type = self.file_type_combo.currentText() converters = { "Document": self.doc_converter, "Image": self.img_converter, "Video": self.video_converter, "Audio": self.audio_converter } return converters.get(file_type) def start_conversion(self): if self.file_list.count() == 0: QMessageBox.warning(self, "Warning", "Please add files to convert") return # Reset stats self.converted_count = 0 self.failed_count = 0 self.update_stats() # Build queue self.conversion_queue = [] selected_items = self.file_list.selectedItems() if selected_items: for item in selected_items: self.conversion_queue.append(item.data(Qt.ItemDataRole.UserRole)) else: for i in range(self.file_list.count()): self.conversion_queue.append(self.file_list.item(i).data(Qt.ItemDataRole.UserRole)) self.convert_btn.setEnabled(False) self.process_next_conversion() def process_next_conversion(self): if not self.conversion_queue: self.log_message("🎉 All conversions completed!") self.status_label.setText("✅ All conversions completed!") self.progress_bar.setValue(100) self.convert_btn.setEnabled(True) QMessageBox.information(self, "Success", f"Conversion complete!\n✅ Success: {self.converted_count}\n❌ Failed: {self.failed_count}") return input_path = self.conversion_queue.pop(0) self.current_conversion = input_path # Get output path output_format = self.output_format_combo.currentText() output_folder = self.output_folder.text() base_name = Path(input_path).stem output_path = Path(output_folder) / f"{base_name}.{output_format}" # Create output directory if needed Path(output_folder).mkdir(parents=True, exist_ok=True) # Quality settings quality_map = { "High": {"video": "18", "audio": "320k", "image": 95}, "Medium": {"video": "23", "audio": "192k", "image": 85}, "Low": {"video": "28", "audio": "128k", "image": 70} } quality = self.quality_combo.currentText() # Prepare options options = { "ai_enhancement": self.ai_checkbox.isChecked(), "quality": quality, "quality_settings": quality_map[quality] } # Start conversion thread converter = self.get_converter() if converter: self.conversion_thread = ConversionThread( converter, input_path, str(output_path), options ) self.conversion_thread.progress_update.connect(self.update_progress) self.conversion_thread.status_update.connect(self.update_status) self.conversion_thread.conversion_complete.connect(self.on_conversion_complete) self.conversion_thread.start() self.active_threads.append(self.conversion_thread) else: self.log_message(f"❌ Invalid converter for: {input_path}") self.process_next_conversion() def update_progress(self, value): self.progress_bar.setValue(value) def update_status(self, status): self.status_label.setText(status) self.log_message(status) def on_conversion_complete(self, success, message): self.log_message(message) if success: self.converted_count += 1 else: self.failed_count += 1 self.update_stats() self.process_next_conversion() def update_stats(self): self.stats_label.setText(f"📈 Converted: {self.converted_count} | ❌ Failed: {self.failed_count}") def log_message(self, message): timestamp = datetime.now().strftime("%H:%M:%S") self.log_text.append(f"[{timestamp}] {message}") def ask_ai_assistant(self): """Process AI query and potentially auto-configure settings""" question = self.ai_input.toPlainText() if not question.strip(): return self.ai_response.setPlainText("🤔 AI Assistant is thinking...") # Get AI response response = self.ai_assistant.ask(question, self.get_conversion_context()) # Check if AI suggested a format change suggested_format = self.extract_format_from_response(response) if suggested_format: # Auto-update the output format combo box index = self.output_format_combo.findText(suggested_format.lower()) if index >= 0: self.output_format_combo.setCurrentIndex(index) response += f"\n\n✅ **Auto-configured**: Output format changed to {suggested_format.upper()}" # Check if AI suggested a file type change suggested_type = self.extract_file_type_from_response(response) if suggested_type: index = self.file_type_combo.findText(suggested_type.title()) if index >= 0: self.file_type_combo.setCurrentIndex(index) response += f"\n\n✅ **Auto-configured**: File type changed to {suggested_type.title()}" self.ai_response.setPlainText(response) def extract_format_from_response(self, response: str) -> str: """Extract suggested format from AI response""" formats = ['pdf', 'docx', 'txt', 'html', 'md', 'png', 'jpg', 'jpeg', 'webp', 'bmp', 'tiff', 'mp4', 'avi', 'mkv', 'mov', 'webm', 'gif', 'mp3', 'wav', 'ogg', 'm4a', 'flac'] for fmt in formats: if f" {fmt.upper()}" in response.upper() or f"to {fmt}" in response.lower(): return fmt return "" def extract_file_type_from_response(self, response: str) -> str: """Extract suggested file type from AI response""" types = ['document', 'image', 'video', 'audio'] for t in types: if t in response.lower(): return t return "" def get_conversion_context(self): return { "file_type": self.file_type_combo.currentText(), "output_format": self.output_format_combo.currentText(), "file_count": self.file_list.count(), "ai_enhancement": self.ai_checkbox.isChecked(), "quality": self.quality_combo.currentText() } if __name__ == "__main__": app = QApplication(sys.argv) window = FileConverterApp() window.show() sys.exit(app.exec())