embedingHF commited on
Commit
8470fc3
ยท
verified ยท
1 Parent(s): 9c7615a

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,35 +1,35 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
CMD.bash ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Install required packages
2
+ pip install -r requirements.txt
3
+
4
+ # For video conversion, install FFmpeg
5
+ # Ubuntu/Debian:
6
+ sudo apt-get install ffmpeg
7
+
8
+ # macOS:
9
+ brew install ffmpeg
10
+
11
+ # Windows: Download from ffmpeg.org
12
+ winget install Gyan-FFmpeg
13
+ # Run the application
14
+ python main.py
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bahadur Ali
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: AI File Converter Pro
3
+ emoji: ๐Ÿ”„
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: python
7
+ sdk_version: 3.10
8
+ app_file: main.py
9
+ pinned: false
10
+ license: mit
11
+ tags:
12
+ - file-converter
13
+ - ai
14
+ - pyqt6
15
+ - document-converter
16
+ - image-converter
17
+ - video-converter
18
+ - audio-converter
19
+ - ocr
20
+ ---
21
+
22
+ # AI File Converter Pro
23
+
24
+ An intelligent, AI-powered file converter that supports documents, images, videos, and audio files with smart enhancement capabilities.
25
+
26
+ ## ๐Ÿš€ Features
27
+
28
+ - **Multi-Format Support**: Convert between 20+ file formats
29
+ - Documents: PDF, DOCX, TXT, HTML, MD
30
+ - Images: PNG, JPG, WebP, BMP, TIFF
31
+ - Videos: MP4, AVI, MKV, MOV, WebM
32
+ - Audio: MP3, WAV, OGG, M4A, FLAC
33
+
34
+ - **AI-Powered Features**
35
+ - Smart format recommendations
36
+ - Automatic quality optimization
37
+ - Image enhancement (contrast, sharpness)
38
+ - Audio normalization
39
+ - OCR text extraction from images/PDFs
40
+
41
+ ![Banner](https://i.ytimg.com/vi/3_U5T2Qg5_o/hq720.jpg?sqp=-oaymwEhCK4FEIIDSFryq4qpAxMIARUAAAAAGAElAADIQj0AgKJD&rs=AOn4CLA_ciOfAAWCyJTzbW4iKMSAlqFkBA.png)
42
+
43
+ - **User-Friendly Interface**
44
+ - Drag & drop support
45
+ - Batch processing
46
+ - Real-time progress tracking
47
+ - Built-in AI assistant for help
48
+
49
+ ## ๐Ÿ“‹ Requirements
50
+
51
+ ```bash
52
+ Python 3.8+
53
+ FFmpeg (for video/audio conversion)
54
+ Tesseract OCR (optional, for text extraction)
ai/__pycache__/ai_assistant.cpython-312.pyc ADDED
Binary file (15.1 kB). View file
 
ai/ai_assistant.py ADDED
@@ -0,0 +1,378 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import os
3
+ import subprocess
4
+ from pathlib import Path
5
+ from typing import Dict, Any, Tuple, Optional
6
+
7
+
8
+ class AIAssistant:
9
+ def __init__(self):
10
+ self.knowledge_base = self.load_knowledge_base()
11
+
12
+ def load_knowledge_base(self):
13
+ return {
14
+ "document": {
15
+ "pdf": {
16
+ "best_for": "Official documents, forms, print-ready files",
17
+ "suggested_formats": {
18
+ "word": "For editing documents",
19
+ "txt": "For text extraction",
20
+ "html": "For web display"
21
+ }
22
+ },
23
+ "word": {
24
+ "best_for": "Editable documents, reports, letters",
25
+ "suggested_formats": {
26
+ "pdf": "For sharing without edits",
27
+ "txt": "For plain text",
28
+ "html": "For web publishing"
29
+ }
30
+ },
31
+ "txt": {
32
+ "best_for": "Simple text, code, notes",
33
+ "suggested_formats": {
34
+ "pdf": "For professional sharing",
35
+ "docx": "For formatted documents",
36
+ "md": "For documentation"
37
+ }
38
+ }
39
+ },
40
+ "image": {
41
+ "jpg": {
42
+ "best_for": "Photographs, web images",
43
+ "suggested_formats": {
44
+ "png": "For images with transparency",
45
+ "webp": "For better web compression",
46
+ "bmp": "For uncompressed quality"
47
+ }
48
+ },
49
+ "png": {
50
+ "best_for": "Graphics, logos, screenshots with transparency",
51
+ "suggested_formats": {
52
+ "jpg": "For smaller file size (photos)",
53
+ "webp": "For web optimization",
54
+ "ico": "For icons"
55
+ }
56
+ },
57
+ "webp": {
58
+ "best_for": "Web images, modern browsers",
59
+ "suggested_formats": {
60
+ "png": "For editing",
61
+ "jpg": "For compatibility"
62
+ }
63
+ }
64
+ },
65
+ "video": {
66
+ "mp4": {
67
+ "best_for": "Universal compatibility, web, social media",
68
+ "suggested_formats": {
69
+ "gif": "For short animations",
70
+ "avi": "For editing",
71
+ "mkv": "For high quality storage"
72
+ }
73
+ },
74
+ "avi": {
75
+ "best_for": "Editing, Windows compatibility",
76
+ "suggested_formats": {
77
+ "mp4": "For web sharing",
78
+ "mkv": "For compression"
79
+ }
80
+ }
81
+ },
82
+ "audio": {
83
+ "mp3": {
84
+ "best_for": "Music, podcasts, universal playback",
85
+ "suggested_formats": {
86
+ "wav": "For editing",
87
+ "flac": "For lossless quality",
88
+ "ogg": "For open source projects"
89
+ }
90
+ },
91
+ "wav": {
92
+ "best_for": "Professional audio, editing",
93
+ "suggested_formats": {
94
+ "mp3": "For smaller size",
95
+ "flac": "For lossless compression"
96
+ }
97
+ }
98
+ }
99
+ }
100
+
101
+ def ask(self, question: str, context: Dict[str, Any]) -> str:
102
+ """Process AI queries and provide intelligent responses"""
103
+ question_lower = question.lower()
104
+
105
+ # Check for conversion commands
106
+ if self.is_conversion_command(question_lower):
107
+ return self.execute_conversion_command(question, context)
108
+
109
+ # Check for format suggestions
110
+ elif "suggest" in question_lower or "recommend" in question_lower or "best format" in question_lower:
111
+ return self.suggest_format(question_lower, context)
112
+
113
+ # Check for file analysis
114
+ elif "what is this" in question_lower or "analyze" in question_lower:
115
+ return self.analyze_file(context)
116
+
117
+ # Check for general help
118
+ elif "help" in question_lower or "how to" in question_lower:
119
+ return self.get_help(question_lower, context)
120
+
121
+ # Default response
122
+ else:
123
+ return self.get_general_response(question_lower, context)
124
+
125
+ def is_conversion_command(self, text: str) -> bool:
126
+ """Check if user wants to convert a file"""
127
+ commands = ['convert', 'change', 'transform', 'turn into', 'make it']
128
+ return any(cmd in text for cmd in commands)
129
+
130
+ def parse_conversion_request(self, text: str, context: Dict[str, Any]) -> Tuple[Optional[str], Optional[str]]:
131
+ """Parse natural language conversion request"""
132
+ # Pattern: "convert this to pdf" or "change this file to mp3"
133
+ patterns = [
134
+ r'(?:convert|change|transform|turn)\s+(?:this|it|the\s+file)\s+(?:to|into)\s+(\w+)',
135
+ r'to\s+(\w+)\s+format',
136
+ r'convert\s+to\s+(\w+)',
137
+ ]
138
+
139
+ for pattern in patterns:
140
+ match = re.search(pattern, text.lower())
141
+ if match:
142
+ target_format = match.group(1)
143
+ return target_format, None
144
+
145
+ return None, None
146
+
147
+ def execute_conversion_command(self, question: str, context: Dict[str, Any]) -> str:
148
+ """Execute a conversion command"""
149
+ target_format, _ = self.parse_conversion_request(question, context)
150
+
151
+ if not target_format:
152
+ return self.get_conversion_help()
153
+
154
+ # Get current context
155
+ current_type = context.get("file_type", "").lower()
156
+ current_format = context.get("output_format", "").lower()
157
+
158
+ if not current_type or not current_format:
159
+ return "โš ๏ธ Please select a file type and output format first in the settings panel."
160
+
161
+ response = f"๐ŸŽฏ **Conversion Command Received**\n\n"
162
+ response += f"Converting from **{current_format.upper()}** to **{target_format.upper()}**...\n\n"
163
+
164
+ # Check if format is supported
165
+ supported_formats = {
166
+ "document": ["pdf", "docx", "txt", "html", "md"],
167
+ "image": ["jpg", "png", "webp", "bmp", "tiff"],
168
+ "video": ["mp4", "avi", "mkv", "mov", "webm", "gif"],
169
+ "audio": ["mp3", "wav", "ogg", "m4a", "flac"]
170
+ }
171
+
172
+ if current_type in supported_formats and target_format in supported_formats[current_type]:
173
+ response += f"โœ… **{target_format.upper()}** is supported!\n\n"
174
+ response += f"๐Ÿ’ก **Tip**: Make sure you've added files to convert, then click 'Start Conversion'\n\n"
175
+
176
+ # Add format-specific advice
177
+ advice = self.get_format_advice(current_type, target_format)
178
+ if advice:
179
+ response += f"๐Ÿ“Œ **Advice**: {advice}\n\n"
180
+ else:
181
+ response += f"โŒ **{target_format.upper()}** is not supported for {current_type} files.\n\n"
182
+ response += f"Supported formats: {', '.join(supported_formats.get(current_type, []))}\n\n"
183
+
184
+ response += "๐Ÿ”„ **Next Steps**:\n"
185
+ response += "1. Select the target format from the dropdown\n"
186
+ response += "2. Add files to convert\n"
187
+ response += "3. Click 'Start Conversion'"
188
+
189
+ return response
190
+
191
+ def suggest_format(self, question: str, context: Dict[str, Any]) -> str:
192
+ """Suggest best format based on use case"""
193
+ current_type = context.get("file_type", "").lower()
194
+
195
+ # Determine use case
196
+ use_case = ""
197
+ if "web" in question or "website" in question:
198
+ use_case = "web"
199
+ elif "email" in question:
200
+ use_case = "email"
201
+ elif "print" in question:
202
+ use_case = "print"
203
+ elif "edit" in question or "editing" in question:
204
+ use_case = "edit"
205
+ elif "compress" in question or "small" in question:
206
+ use_case = "compress"
207
+ elif "quality" in question or "lossless" in question:
208
+ use_case = "quality"
209
+
210
+ # Format suggestions by use case and type
211
+ suggestions = {
212
+ "document": {
213
+ "web": "HTML or PDF (for consistent formatting)",
214
+ "email": "PDF (preserves formatting) or TXT (small size)",
215
+ "print": "PDF (best for printing)",
216
+ "edit": "DOCX (Microsoft Word) or ODT (OpenOffice)",
217
+ "compress": "TXT (smallest) or compressed PDF",
218
+ "quality": "PDF (preserves all formatting)"
219
+ },
220
+ "image": {
221
+ "web": "WebP (best compression) or JPEG (good quality)",
222
+ "email": "JPEG (small size) or PNG (if transparency needed)",
223
+ "print": "TIFF (high quality) or PNG",
224
+ "edit": "PNG (lossless) or PSD",
225
+ "compress": "JPEG (80% quality) or WebP",
226
+ "quality": "PNG or TIFF (lossless)"
227
+ },
228
+ "video": {
229
+ "web": "MP4 with H.264 codec (best compatibility)",
230
+ "email": "GIF (short clips) or small MP4",
231
+ "print": "Not applicable for video",
232
+ "edit": "AVI or MOV (uncompressed)",
233
+ "compress": "MP4 with H.265 codec (smallest)",
234
+ "quality": "MKV or MOV (ProRes)"
235
+ },
236
+ "audio": {
237
+ "web": "MP3 (128-192kbps) or OGG",
238
+ "email": "MP3 (low bitrate)",
239
+ "print": "Not applicable for audio",
240
+ "edit": "WAV or FLAC (lossless)",
241
+ "compress": "MP3 or AAC",
242
+ "quality": "FLAC or WAV (lossless)"
243
+ }
244
+ }
245
+
246
+ if current_type in suggestions:
247
+ if use_case in suggestions[current_type]:
248
+ recommended = suggestions[current_type][use_case]
249
+ response = f"๐Ÿ“‹ **Format Recommendation**\n\n"
250
+ response += f"Based on your request to **{use_case}** a {current_type}:\n"
251
+ response += f"โœ… Recommended format: **{recommended}**\n\n"
252
+
253
+ # Add additional tips
254
+ tips = {
255
+ "web": f"๐Ÿ’ก For web, also consider optimizing file size and using responsive formats.",
256
+ "email": f"๐Ÿ’ก Keep file size under 10-20MB for email attachments.",
257
+ "print": f"๐Ÿ’ก Use at least 300 DPI for print quality.",
258
+ "edit": f"๐Ÿ’ก Lossless formats preserve quality for multiple edits.",
259
+ "compress": f"๐Ÿ’ก Balance between quality and file size.",
260
+ "quality": f"๐Ÿ’ก Lossless formats preserve original quality."
261
+ }
262
+ if use_case in tips:
263
+ response += f"{tips[use_case]}\n\n"
264
+
265
+ response += f"๐Ÿ”„ To convert: Select **{recommended.split()[0]}** from the Output Format dropdown."
266
+ return response
267
+
268
+ # Default suggestions
269
+ default_suggestions = {
270
+ "document": "PDF for sharing, DOCX for editing, TXT for simplicity",
271
+ "image": "JPEG for photos, PNG for graphics, WebP for web",
272
+ "video": "MP4 for compatibility, AVI for editing, MKV for storage",
273
+ "audio": "MP3 for general use, WAV for editing, FLAC for quality"
274
+ }
275
+
276
+ response = f"๐Ÿ’ก **Format Suggestions for {current_type.title()}s:**\n\n"
277
+ response += f"{default_suggestions.get(current_type, 'Choose based on your needs')}\n\n"
278
+ response += f"Tell me more about your use case (web, print, edit, compress) for better recommendations!"
279
+
280
+ return response
281
+
282
+ def analyze_file(self, context: Dict[str, Any]) -> str:
283
+ """Analyze file and provide insights"""
284
+ current_type = context.get("file_type", "").lower()
285
+ current_format = context.get("output_format", "").upper()
286
+
287
+ response = f"๐Ÿ” **File Analysis**\n\n"
288
+ response += f"๐Ÿ“„ Type: {current_type.title()}\n"
289
+ response += f"๐ŸŽฏ Current format: {current_format}\n"
290
+
291
+ if current_type in self.knowledge_base and current_format.lower() in self.knowledge_base[current_type]:
292
+ info = self.knowledge_base[current_type][current_format.lower()]
293
+ response += f"๐Ÿ“Œ Best for: {info['best_for']}\n\n"
294
+
295
+ if "suggested_formats" in info:
296
+ response += f"๐Ÿ”„ **Suggested conversions:**\n"
297
+ for fmt, reason in info['suggested_formats'].items():
298
+ response += f" โ€ข {fmt.upper()} - {reason}\n"
299
+ else:
300
+ response += f"\n๐Ÿ’ก Try converting to a different format for better results!\n"
301
+
302
+ return response
303
+
304
+ def get_help(self, question: str, context: Dict[str, Any]) -> str:
305
+ """Get help on specific topics"""
306
+ response = "๐Ÿค– **How I Can Help You**\n\n"
307
+ response += "I can assist with:\n"
308
+ response += "โœ… **Convert files** - Say: 'convert this to mp4' or 'make it PDF'\n"
309
+ response += "โœ… **Suggest formats** - Ask: 'what's the best format for web?'\n"
310
+ response += "โœ… **Analyze files** - Ask: 'analyze this file' or 'what is this good for?'\n"
311
+ response += "โœ… **Optimize settings** - Ask: 'how to compress video?'\n\n"
312
+ response += "๐Ÿ’ฌ **Examples:**\n"
313
+ response += "โ€ข 'Convert this to PNG'\n"
314
+ response += "โ€ข 'Best format for email attachment'\n"
315
+ response += "โ€ข 'How to reduce file size?'\n"
316
+ response += "โ€ข 'What format should I use for web?'"
317
+
318
+ return response
319
+
320
+ def get_general_response(self, question: str, context: Dict[str, Any]) -> str:
321
+ """General response when no specific command is detected"""
322
+ response = "๐Ÿค– **AI Assistant Ready**\n\n"
323
+
324
+ # Try to understand intent
325
+ if "thank" in question:
326
+ return "You're welcome! ๐Ÿ˜Š Let me know if you need help with any conversions!"
327
+
328
+ if "hello" in question or "hi" in question:
329
+ return "Hello! ๐Ÿ‘‹ I'm your AI conversion assistant. How can I help you today?\n\nTry asking me to 'convert this to PDF' or 'suggest best format for web'!"
330
+
331
+ response += "I can help you:\n"
332
+ response += "โ€ข **Convert files** using natural language\n"
333
+ response += "โ€ข **Recommend formats** based on your needs\n"
334
+ response += "โ€ข **Analyze files** and suggest improvements\n"
335
+ response += "โ€ข **Optimize** quality vs file size\n\n"
336
+ response += "๐Ÿ’ก **Quick commands:**\n"
337
+ response += " โ†’ 'Convert this to MP3'\n"
338
+ response += " โ†’ 'Best format for web'\n"
339
+ response += " โ†’ 'How to compress video?'\n"
340
+ response += " โ†’ 'Analyze this file'"
341
+
342
+ return response
343
+
344
+ def get_format_advice(self, file_type: str, target_format: str) -> str:
345
+ """Get specific advice for format conversion"""
346
+ advice = {
347
+ ("image", "jpg"): "Good for photos. Use quality 85-90 for best balance.",
348
+ ("image", "png"): "Best for graphics with transparency. Larger file size.",
349
+ ("image", "webp"): "Modern format with great compression. Supported by all modern browsers.",
350
+ ("video", "mp4"): "Most compatible. H.264 codec is universal.",
351
+ ("video", "gif"): "Good for short animations. Keep duration under 5 seconds.",
352
+ ("audio", "mp3"): "Universal format. 192kbps is good for most uses.",
353
+ ("audio", "flac"): "Lossless format. Perfect for archiving.",
354
+ ("document", "pdf"): "Preserves formatting across all devices.",
355
+ ("document", "txt"): "Plain text - very small but loses all formatting."
356
+ }
357
+
358
+ return advice.get((file_type, target_format), "")
359
+
360
+ def get_conversion_help(self) -> str:
361
+ """Help for conversion commands"""
362
+ return """
363
+ ๐ŸŽฏ **Conversion Commands**
364
+
365
+ Try these examples:
366
+ โ€ข "**Convert this to PDF**" - Changes output format to PDF
367
+ โ€ข "**Make it MP3**" - Changes to audio format
368
+ โ€ข "**Transform to PNG**" - Changes image format
369
+ โ€ข "**Change this to AVI**" - Changes video format
370
+
371
+ **How to use:**
372
+ 1. Select the file type (Document/Image/Video/Audio)
373
+ 2. Say what format you want
374
+ 3. Click "Ask AI" or press Enter
375
+ 4. Then click "Start Conversion"
376
+
377
+ ๐Ÿ’ก **Tip:** Be specific about the format you want!
378
+ """
converters/__pycache__/audio_converter.cpython-312.pyc ADDED
Binary file (3.96 kB). View file
 
converters/__pycache__/document_converter.cpython-312.pyc ADDED
Binary file (17.6 kB). View file
 
converters/__pycache__/image_converter.cpython-312.pyc ADDED
Binary file (4.23 kB). View file
 
converters/__pycache__/video_converter.cpython-312.pyc ADDED
Binary file (4.63 kB). View file
 
converters/audio_converter.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Callable, Dict, Any
4
+ import traceback
5
+
6
+ # Setup ffmpeg for pydub
7
+ import shutil
8
+
9
+ ffmpeg_path = shutil.which('ffmpeg') or shutil.which('ffmpeg.exe')
10
+ if ffmpeg_path:
11
+ os.environ["FFMPEG_BINARY"] = ffmpeg_path
12
+
13
+ try:
14
+ from pydub import AudioSegment
15
+
16
+ PYDUB_AVAILABLE = True
17
+ except ImportError:
18
+ PYDUB_AVAILABLE = False
19
+ print("pydub not installed. Audio conversion will not work.")
20
+
21
+
22
+ class AudioConverter:
23
+ def __init__(self):
24
+ pass
25
+
26
+ def convert(self, input_path: str, output_path: str,
27
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
28
+ """Convert audio files"""
29
+ try:
30
+ if not PYDUB_AVAILABLE:
31
+ print("pydub library not available")
32
+ return False
33
+
34
+ self._update_progress(progress_callback, 10)
35
+
36
+ # Check if input file exists
37
+ if not os.path.exists(input_path):
38
+ print(f"Input file not found: {input_path}")
39
+ return False
40
+
41
+ # Load audio
42
+ audio = AudioSegment.from_file(input_path)
43
+
44
+ self._update_progress(progress_callback, 40)
45
+
46
+ # Apply AI enhancement if requested
47
+ if options.get("ai_enhancement", False):
48
+ audio = self.enhance_audio(audio)
49
+
50
+ self._update_progress(progress_callback, 60)
51
+
52
+ # Get quality settings
53
+ quality_settings = options.get("quality_settings", {})
54
+ bitrate = quality_settings.get("audio", "192k")
55
+
56
+ # Create output directory
57
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
58
+
59
+ # Export to desired format
60
+ output_ext = Path(output_path).suffix.lower()
61
+
62
+ export_params = {
63
+ '.mp3': {'format': 'mp3', 'bitrate': bitrate},
64
+ '.wav': {'format': 'wav'},
65
+ '.ogg': {'format': 'ogg', 'bitrate': bitrate},
66
+ '.m4a': {'format': 'mp4', 'bitrate': bitrate},
67
+ '.flac': {'format': 'flac'}
68
+ }
69
+
70
+ if output_ext in export_params:
71
+ audio.export(output_path, **export_params[output_ext])
72
+ else:
73
+ audio.export(output_path)
74
+
75
+ self._update_progress(progress_callback, 100)
76
+
77
+ print(f"โœ“ Successfully converted: {os.path.basename(input_path)} โ†’ {output_ext}")
78
+ return True
79
+
80
+ except Exception as e:
81
+ print(f"Audio conversion error for {input_path}: {str(e)}")
82
+ traceback.print_exc()
83
+ return False
84
+
85
+ def _update_progress(self, callback, value):
86
+ """Safely update progress"""
87
+ if callback is not None:
88
+ try:
89
+ callback(value)
90
+ except Exception:
91
+ pass
92
+
93
+ def enhance_audio(self, audio):
94
+ """Basic audio enhancement"""
95
+ try:
96
+ audio = audio.normalize()
97
+ return audio
98
+ except:
99
+ return audio
converters/document_converter.py ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Callable, Dict, Any
4
+ import traceback
5
+
6
+ # Optional imports with error handling
7
+ try:
8
+ from docx import Document
9
+
10
+ DOCX_AVAILABLE = True
11
+ except ImportError:
12
+ DOCX_AVAILABLE = False
13
+ print("โš  python-docx not installed. DOCX conversion will not work.")
14
+
15
+ try:
16
+ import fitz # PyMuPDF
17
+
18
+ FITZ_AVAILABLE = True
19
+ except ImportError:
20
+ FITZ_AVAILABLE = False
21
+ print("โš  PyMuPDF not installed. PDF conversion will not work.")
22
+
23
+ try:
24
+ import markdown
25
+
26
+ MARKDOWN_AVAILABLE = True
27
+ except ImportError:
28
+ MARKDOWN_AVAILABLE = False
29
+ print("โš  markdown not installed. MD conversion will not work.")
30
+
31
+ try:
32
+ from bs4 import BeautifulSoup
33
+
34
+ BS4_AVAILABLE = True
35
+ except ImportError:
36
+ BS4_AVAILABLE = False
37
+ print("โš  beautifulsoup4 not installed. HTML conversion will not work.")
38
+
39
+
40
+ class DocumentConverter:
41
+ def __init__(self):
42
+ pass
43
+
44
+ def convert(self, input_path: str, output_path: str,
45
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
46
+ """Convert document files"""
47
+ input_ext = Path(input_path).suffix.lower()
48
+
49
+ try:
50
+ self._update_progress(progress_callback, 10)
51
+
52
+ # Check if input file exists
53
+ if not os.path.exists(input_path):
54
+ print(f"Input file not found: {input_path}")
55
+ return False
56
+
57
+ # Create output directory if needed
58
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
59
+
60
+ result = False
61
+
62
+ # PDF conversion
63
+ if input_ext == '.pdf':
64
+ if not FITZ_AVAILABLE:
65
+ print("PyMuPDF not available for PDF conversion")
66
+ return False
67
+ result = self.convert_pdf(input_path, output_path, options, progress_callback)
68
+
69
+ # DOCX conversion
70
+ elif input_ext in ['.docx', '.doc']:
71
+ if not DOCX_AVAILABLE:
72
+ print("python-docx not available for DOCX conversion")
73
+ return False
74
+ result = self.convert_docx(input_path, output_path, options, progress_callback)
75
+
76
+ # TXT conversion
77
+ elif input_ext == '.txt':
78
+ result = self.convert_txt(input_path, output_path, options, progress_callback)
79
+
80
+ # Markdown conversion
81
+ elif input_ext == '.md':
82
+ if not MARKDOWN_AVAILABLE:
83
+ print("markdown library not available")
84
+ return False
85
+ result = self.convert_markdown(input_path, output_path, options, progress_callback)
86
+
87
+ # HTML conversion
88
+ elif input_ext == '.html':
89
+ result = self.convert_html(input_path, output_path, options, progress_callback)
90
+
91
+ else:
92
+ result = self.convert_generic(input_path, output_path, options, progress_callback)
93
+
94
+ if result:
95
+ output_ext = Path(output_path).suffix.lower()
96
+ print(f"โœ“ Successfully converted: {os.path.basename(input_path)} โ†’ {output_ext}")
97
+
98
+ return result
99
+
100
+ except Exception as e:
101
+ print(f"Document conversion error for {input_path}: {str(e)}")
102
+ traceback.print_exc()
103
+ return False
104
+
105
+ def _update_progress(self, callback, value):
106
+ """Safely update progress"""
107
+ if callback is not None:
108
+ try:
109
+ callback(value)
110
+ except Exception:
111
+ pass
112
+
113
+ def convert_pdf(self, input_path: str, output_path: str,
114
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
115
+ """Convert PDF to other formats"""
116
+ try:
117
+ doc = fitz.open(input_path)
118
+ total_pages = len(doc)
119
+
120
+ self._update_progress(progress_callback, 20)
121
+
122
+ if output_path.endswith('.txt'):
123
+ text = ""
124
+ for page_num in range(total_pages):
125
+ page = doc[page_num]
126
+ text += page.get_text()
127
+ progress_pct = 20 + (page_num + 1) * 60 // total_pages
128
+ self._update_progress(progress_callback, progress_pct)
129
+
130
+ with open(output_path, 'w', encoding='utf-8') as f:
131
+ f.write(text)
132
+
133
+ elif output_path.endswith('.docx'):
134
+ docx_doc = Document()
135
+ for page_num in range(total_pages):
136
+ page = doc[page_num]
137
+ text = page.get_text()
138
+ docx_doc.add_paragraph(text)
139
+ progress_pct = 20 + (page_num + 1) * 60 // total_pages
140
+ self._update_progress(progress_callback, progress_pct)
141
+
142
+ docx_doc.save(output_path)
143
+
144
+ elif output_path.endswith('.html'):
145
+ html_content = """<!DOCTYPE html>
146
+ <html>
147
+ <head>
148
+ <meta charset="UTF-8">
149
+ <title>PDF Content</title>
150
+ <style>
151
+ body { font-family: Arial, sans-serif; margin: 40px; }
152
+ .page { margin-bottom: 30px; page-break-after: always; }
153
+ .page-number { color: #666; font-size: 12px; margin-bottom: 10px; }
154
+ pre { white-space: pre-wrap; word-wrap: break-word; }
155
+ </style>
156
+ </head>
157
+ <body>
158
+ """
159
+ for page_num in range(total_pages):
160
+ page = doc[page_num]
161
+ text = page.get_text()
162
+ html_content += f"""
163
+ <div class="page">
164
+ <div class="page-number">Page {page_num + 1}</div>
165
+ <pre>{text}</pre>
166
+ </div>
167
+ """
168
+ progress_pct = 20 + (page_num + 1) * 60 // total_pages
169
+ self._update_progress(progress_callback, progress_pct)
170
+
171
+ html_content += "</body></html>"
172
+
173
+ with open(output_path, 'w', encoding='utf-8') as f:
174
+ f.write(html_content)
175
+
176
+ doc.close()
177
+ self._update_progress(progress_callback, 100)
178
+ return True
179
+
180
+ except Exception as e:
181
+ print(f"PDF conversion error: {e}")
182
+ return False
183
+
184
+ def convert_docx(self, input_path: str, output_path: str,
185
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
186
+ """Convert DOCX to other formats"""
187
+ try:
188
+ doc = Document(input_path)
189
+ self._update_progress(progress_callback, 30)
190
+
191
+ if output_path.endswith('.txt'):
192
+ text = "\n".join([para.text for para in doc.paragraphs])
193
+ with open(output_path, 'w', encoding='utf-8') as f:
194
+ f.write(text)
195
+
196
+ elif output_path.endswith('.html'):
197
+ html_content = """<!DOCTYPE html>
198
+ <html>
199
+ <head><meta charset="UTF-8"><title>Document Content</title></head>
200
+ <body>
201
+ """
202
+ for para in doc.paragraphs:
203
+ if para.text.strip():
204
+ html_content += f"<p>{para.text}</p>"
205
+ html_content += "</body></html>"
206
+
207
+ with open(output_path, 'w', encoding='utf-8') as f:
208
+ f.write(html_content)
209
+
210
+ elif output_path.endswith('.md'):
211
+ markdown_content = "\n\n".join([para.text for para in doc.paragraphs if para.text.strip()])
212
+ with open(output_path, 'w', encoding='utf-8') as f:
213
+ f.write(markdown_content)
214
+
215
+ elif output_path.endswith('.pdf'):
216
+ # Simple PDF conversion using text extraction
217
+ text = "\n".join([para.text for para in doc.paragraphs])
218
+ with open(output_path.replace('.pdf', '.txt'), 'w', encoding='utf-8') as f:
219
+ f.write(text)
220
+ print("Note: DOCX to PDF requires additional libraries. Saved as TXT instead.")
221
+
222
+ self._update_progress(progress_callback, 100)
223
+ return True
224
+
225
+ except Exception as e:
226
+ print(f"DOCX conversion error: {e}")
227
+ return False
228
+
229
+ def convert_txt(self, input_path: str, output_path: str,
230
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
231
+ """Convert TXT to other formats"""
232
+ try:
233
+ with open(input_path, 'r', encoding='utf-8') as f:
234
+ content = f.read()
235
+
236
+ self._update_progress(progress_callback, 40)
237
+
238
+ if output_path.endswith('.md'):
239
+ with open(output_path, 'w', encoding='utf-8') as f:
240
+ f.write(content)
241
+
242
+ elif output_path.endswith('.html'):
243
+ html_content = f"""<!DOCTYPE html>
244
+ <html>
245
+ <head><meta charset="UTF-8"><title>Text Document</title></head>
246
+ <body>
247
+ <pre>{content}</pre>
248
+ </body></html>"""
249
+ with open(output_path, 'w', encoding='utf-8') as f:
250
+ f.write(html_content)
251
+
252
+ elif output_path.endswith('.docx'):
253
+ if DOCX_AVAILABLE:
254
+ doc = Document()
255
+ doc.add_paragraph(content)
256
+ doc.save(output_path)
257
+ else:
258
+ with open(output_path.replace('.docx', '.txt'), 'w', encoding='utf-8') as f:
259
+ f.write(content)
260
+ print("Note: python-docx not installed. Saved as TXT instead.")
261
+
262
+ self._update_progress(progress_callback, 100)
263
+ return True
264
+
265
+ except Exception as e:
266
+ print(f"TXT conversion error: {e}")
267
+ return False
268
+
269
+ def convert_markdown(self, input_path: str, output_path: str,
270
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
271
+ """Convert Markdown to other formats"""
272
+ try:
273
+ with open(input_path, 'r', encoding='utf-8') as f:
274
+ content = f.read()
275
+
276
+ self._update_progress(progress_callback, 40)
277
+
278
+ if output_path.endswith('.html'):
279
+ html_content = markdown.markdown(content)
280
+ full_html = f"""<!DOCTYPE html>
281
+ <html>
282
+ <head><meta charset="UTF-8"><title>Markdown Document</title></head>
283
+ <body>
284
+ {html_content}
285
+ </body></html>"""
286
+ with open(output_path, 'w', encoding='utf-8') as f:
287
+ f.write(full_html)
288
+
289
+ elif output_path.endswith('.docx'):
290
+ if DOCX_AVAILABLE and BS4_AVAILABLE:
291
+ html = markdown.markdown(content)
292
+ soup = BeautifulSoup(html, 'html.parser')
293
+ doc = Document()
294
+ for para in soup.find_all('p'):
295
+ if para.get_text().strip():
296
+ doc.add_paragraph(para.get_text())
297
+ doc.save(output_path)
298
+ else:
299
+ with open(output_path.replace('.docx', '.txt'), 'w', encoding='utf-8') as f:
300
+ f.write(content)
301
+ print("Note: Required libraries not installed. Saved as TXT instead.")
302
+
303
+ self._update_progress(progress_callback, 100)
304
+ return True
305
+
306
+ except Exception as e:
307
+ print(f"Markdown conversion error: {e}")
308
+ return False
309
+
310
+ def convert_html(self, input_path: str, output_path: str,
311
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
312
+ """Convert HTML to other formats"""
313
+ try:
314
+ with open(input_path, 'r', encoding='utf-8') as f:
315
+ content = f.read()
316
+
317
+ self._update_progress(progress_callback, 40)
318
+
319
+ if BS4_AVAILABLE:
320
+ soup = BeautifulSoup(content, 'html.parser')
321
+ text = soup.get_text()
322
+ else:
323
+ # Simple text extraction
324
+ import re
325
+ text = re.sub(r'<[^>]+>', ' ', content)
326
+ text = re.sub(r'\s+', ' ', text).strip()
327
+
328
+ if output_path.endswith('.txt'):
329
+ with open(output_path, 'w', encoding='utf-8') as f:
330
+ f.write(text)
331
+
332
+ elif output_path.endswith('.md'):
333
+ with open(output_path, 'w', encoding='utf-8') as f:
334
+ f.write(f"# Converted from HTML\n\n{text}")
335
+
336
+ elif output_path.endswith('.docx'):
337
+ if DOCX_AVAILABLE:
338
+ doc = Document()
339
+ doc.add_paragraph(text)
340
+ doc.save(output_path)
341
+ else:
342
+ with open(output_path.replace('.docx', '.txt'), 'w', encoding='utf-8') as f:
343
+ f.write(text)
344
+
345
+ self._update_progress(progress_callback, 100)
346
+ return True
347
+
348
+ except Exception as e:
349
+ print(f"HTML conversion error: {e}")
350
+ return False
351
+
352
+ def convert_generic(self, input_path: str, output_path: str,
353
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
354
+ """Generic text file conversion"""
355
+ try:
356
+ # Try to read as text
357
+ encodings = ['utf-8', 'latin-1', 'cp1252']
358
+ content = None
359
+
360
+ for encoding in encodings:
361
+ try:
362
+ with open(input_path, 'r', encoding=encoding) as f:
363
+ content = f.read()
364
+ break
365
+ except UnicodeDecodeError:
366
+ continue
367
+
368
+ if content is None:
369
+ # If can't read as text, just copy binary
370
+ with open(input_path, 'rb') as src:
371
+ with open(output_path, 'wb') as dst:
372
+ dst.write(src.read())
373
+ else:
374
+ with open(output_path, 'w', encoding='utf-8') as f:
375
+ f.write(content)
376
+
377
+ self._update_progress(progress_callback, 100)
378
+ return True
379
+
380
+ except Exception as e:
381
+ print(f"Generic conversion error: {e}")
382
+ return False
converters/image_converter.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PIL import Image
2
+ from pathlib import Path
3
+ from typing import Callable, Dict, Any
4
+ import traceback
5
+ import os
6
+
7
+
8
+ class ImageConverter:
9
+ def __init__(self):
10
+ pass
11
+
12
+ def convert(self, input_path: str, output_path: str,
13
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
14
+ """Convert image files"""
15
+ try:
16
+ # Update progress safely
17
+ self._update_progress(progress_callback, 10)
18
+
19
+ # Check if input file exists
20
+ if not os.path.exists(input_path):
21
+ print(f"Input file not found: {input_path}")
22
+ return False
23
+
24
+ # Open image
25
+ img = Image.open(input_path)
26
+
27
+ self._update_progress(progress_callback, 30)
28
+
29
+ # Apply AI enhancement if requested
30
+ if options.get("ai_enhancement", False):
31
+ img = self.enhance_image(img)
32
+
33
+ self._update_progress(progress_callback, 50)
34
+
35
+ # Get quality settings
36
+ quality_settings = options.get("quality_settings", {})
37
+ quality = quality_settings.get("image", 85)
38
+
39
+ # Create output directory if needed
40
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
41
+
42
+ # Convert and save based on output format
43
+ output_ext = Path(output_path).suffix.lower()
44
+
45
+ self._update_progress(progress_callback, 70)
46
+
47
+ # Handle different formats
48
+ if output_ext in ['.jpg', '.jpeg']:
49
+ # Convert RGBA to RGB for JPEG
50
+ if img.mode in ('RGBA', 'P'):
51
+ img = img.convert('RGB')
52
+ img.save(output_path, 'JPEG', quality=quality, optimize=True)
53
+
54
+ elif output_ext == '.png':
55
+ # PNG uses compression level (0-9)
56
+ compression = 6 if quality < 90 else 3
57
+ img.save(output_path, 'PNG', compress_level=compression, optimize=True)
58
+
59
+ elif output_ext == '.webp':
60
+ img.save(output_path, 'WEBP', quality=quality, method=6)
61
+
62
+ elif output_ext == '.bmp':
63
+ img.save(output_path, 'BMP')
64
+
65
+ elif output_ext == '.tiff':
66
+ img.save(output_path, 'TIFF', compression='tiff_lzw')
67
+
68
+ else:
69
+ img.save(output_path)
70
+
71
+ self._update_progress(progress_callback, 100)
72
+
73
+ print(f"โœ“ Successfully converted: {os.path.basename(input_path)} โ†’ {output_ext}")
74
+ return True
75
+
76
+ except Exception as e:
77
+ print(f"Image conversion error for {input_path}: {str(e)}")
78
+ traceback.print_exc()
79
+ return False
80
+
81
+ def _update_progress(self, callback, value):
82
+ """Safely update progress"""
83
+ if callback is not None:
84
+ try:
85
+ callback(value)
86
+ except Exception as e:
87
+ # If callback fails (e.g., Qt signal issue), just continue
88
+ pass
89
+
90
+ def enhance_image(self, img):
91
+ """Basic image enhancement"""
92
+ try:
93
+ from PIL import ImageEnhance, ImageFilter
94
+
95
+ # Enhance contrast slightly
96
+ enhancer = ImageEnhance.Contrast(img)
97
+ img = enhancer.enhance(1.1)
98
+
99
+ # Enhance sharpness
100
+ enhancer = ImageEnhance.Sharpness(img)
101
+ img = enhancer.enhance(1.05)
102
+
103
+ return img
104
+ except:
105
+ return img
converters/video_converter.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import subprocess
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Callable, Dict, Any
5
+ import shutil
6
+ import traceback
7
+
8
+ # Find ffmpeg
9
+ ffmpeg_path = shutil.which('ffmpeg') or shutil.which('ffmpeg.exe')
10
+ if not ffmpeg_path:
11
+ common_paths = [
12
+ r"C:\ffmpeg\bin\ffmpeg.exe",
13
+ r"C:\Program Files\ffmpeg\bin\ffmpeg.exe",
14
+ ]
15
+ for path in common_paths:
16
+ if os.path.exists(path):
17
+ ffmpeg_path = path
18
+ break
19
+
20
+
21
+ class VideoConverter:
22
+ def __init__(self):
23
+ self.ffmpeg_available = self._check_ffmpeg()
24
+
25
+ def _check_ffmpeg(self):
26
+ """Check if ffmpeg is installed"""
27
+ if not ffmpeg_path or not os.path.exists(ffmpeg_path):
28
+ print("โš  FFmpeg not found. Video conversion will not work.")
29
+ return False
30
+ return True
31
+
32
+ def convert(self, input_path: str, output_path: str,
33
+ options: Dict[str, Any], progress_callback: Callable = None) -> bool:
34
+ """Convert video files using ffmpeg"""
35
+ try:
36
+ if not self.ffmpeg_available:
37
+ print("FFmpeg not available for video conversion")
38
+ return False
39
+
40
+ self._update_progress(progress_callback, 10)
41
+
42
+ # Check input file
43
+ if not os.path.exists(input_path):
44
+ print(f"Input file not found: {input_path}")
45
+ return False
46
+
47
+ # Build ffmpeg command
48
+ cmd = [ffmpeg_path, '-i', input_path, '-y']
49
+
50
+ # Add quality settings
51
+ quality_settings = options.get("quality_settings", {})
52
+ crf_value = quality_settings.get("video", "23")
53
+
54
+ # Output format specific settings
55
+ output_ext = Path(output_path).suffix.lower()
56
+
57
+ format_settings = {
58
+ '.mp4': ['-c:v', 'libx264', '-preset', 'medium', '-crf', crf_value, '-c:a', 'aac', '-b:a', '128k'],
59
+ '.avi': ['-c:v', 'libxvid', '-q:v', '4', '-c:a', 'mp3', '-b:a', '192k'],
60
+ '.mkv': ['-c:v', 'libx264', '-crf', crf_value, '-c:a', 'aac'],
61
+ '.mov': ['-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-c:a', 'aac'],
62
+ '.webm': ['-c:v', 'libvpx-vp9', '-crf', crf_value, '-b:v', '0', '-c:a', 'libopus'],
63
+ '.gif': ['-vf', 'fps=10,scale=320:-1:flags=lanczos', '-c:v', 'gif']
64
+ }
65
+
66
+ if output_ext in format_settings:
67
+ cmd.extend(format_settings[output_ext])
68
+
69
+ # Add AI enhancement
70
+ if options.get("ai_enhancement", False):
71
+ cmd.extend(['-vf', 'unsharp=5:5:1.0:5:5:0.0'])
72
+
73
+ # Create output directory
74
+ Path(output_path).parent.mkdir(parents=True, exist_ok=True)
75
+ cmd.append(output_path)
76
+
77
+ self._update_progress(progress_callback, 50)
78
+
79
+ # Run ffmpeg
80
+ result = subprocess.run(cmd, capture_output=True, text=True)
81
+
82
+ self._update_progress(progress_callback, 100)
83
+
84
+ if result.returncode == 0:
85
+ print(f"โœ“ Successfully converted: {os.path.basename(input_path)} โ†’ {output_ext}")
86
+ return True
87
+ else:
88
+ print(f"FFmpeg error: {result.stderr[:200]}")
89
+ return False
90
+
91
+ except Exception as e:
92
+ print(f"Video conversion error for {input_path}: {str(e)}")
93
+ traceback.print_exc()
94
+ return False
95
+
96
+ def _update_progress(self, callback, value):
97
+ """Safely update progress"""
98
+ if callback is not None:
99
+ try:
100
+ callback(value)
101
+ except Exception:
102
+ pass
main.py ADDED
@@ -0,0 +1,792 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ import json
4
+ import threading
5
+ import shutil
6
+ from pathlib import Path
7
+ from datetime import datetime
8
+ from PyQt6.QtWidgets import (
9
+ QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
10
+ QPushButton, QLabel, QFileDialog, QComboBox, QProgressBar,
11
+ QTextEdit, QTabWidget, QGroupBox, QGridLayout, QMessageBox,
12
+ QListWidget, QListWidgetItem, QCheckBox, QSpinBox, QLineEdit,
13
+ QSplitter, QFrame, QToolButton, QSizePolicy
14
+ )
15
+ from PyQt6.QtCore import Qt, QThread, pyqtSignal, QSize, QPropertyAnimation, QEasingCurve, QRect
16
+ from PyQt6.QtGui import QFont, QIcon, QDragEnterEvent, QDropEvent, QPalette, QColor, QAction
17
+
18
+ # Fix FFmpeg before any imports
19
+ import warnings
20
+
21
+ warnings.filterwarnings("ignore", message="Couldn't find ffmpeg or avconv")
22
+
23
+ # Setup FFmpeg
24
+ ffmpeg_path = None
25
+ for path in [
26
+ shutil.which('ffmpeg'),
27
+ shutil.which('ffmpeg.exe'),
28
+ r"C:\ffmpeg\bin\ffmpeg.exe",
29
+ r"C:\Program Files\ffmpeg\bin\ffmpeg.exe"
30
+ ]:
31
+ if path and os.path.exists(path):
32
+ ffmpeg_path = path
33
+ os.environ["FFMPEG_BINARY"] = ffmpeg_path
34
+ break
35
+
36
+ # Import converters
37
+ from converters.document_converter import DocumentConverter
38
+ from converters.image_converter import ImageConverter
39
+ from converters.video_converter import VideoConverter
40
+ from converters.audio_converter import AudioConverter
41
+ from ai.ai_assistant import AIAssistant
42
+
43
+
44
+ class ConversionThread(QThread):
45
+ progress_update = pyqtSignal(int)
46
+ status_update = pyqtSignal(str)
47
+ conversion_complete = pyqtSignal(bool, str)
48
+
49
+ def __init__(self, converter, input_path, output_path, options=None):
50
+ super().__init__()
51
+ self.converter = converter
52
+ self.input_path = input_path
53
+ self.output_path = output_path
54
+ self.options = options or {}
55
+
56
+ def run(self):
57
+ try:
58
+ self.status_update.emit(f"Converting: {os.path.basename(self.input_path)}")
59
+ success = self.converter.convert(
60
+ self.input_path,
61
+ self.output_path,
62
+ self.options,
63
+ self.progress_update
64
+ )
65
+
66
+ if success:
67
+ self.conversion_complete.emit(True, f"โœ… Successfully converted: {os.path.basename(self.input_path)}")
68
+ else:
69
+ self.conversion_complete.emit(False, f"โŒ Failed to convert: {os.path.basename(self.input_path)}")
70
+ except Exception as e:
71
+ self.conversion_complete.emit(False, f"โŒ Error: {str(e)}")
72
+
73
+
74
+ class CollapsibleSidebar(QWidget):
75
+ """Collapsible sidebar widget"""
76
+
77
+ def __init__(self, parent=None):
78
+ super().__init__(parent)
79
+ self.setVisible(True)
80
+ self.animation_duration = 300
81
+
82
+ def toggle(self):
83
+ """Toggle sidebar visibility with animation"""
84
+ self.animated_toggle()
85
+
86
+ def animated_toggle(self):
87
+ """Animated toggle of sidebar"""
88
+ if self.isVisible():
89
+ self.animation = QPropertyAnimation(self, b"maximumWidth")
90
+ self.animation.setDuration(self.animation_duration)
91
+ self.animation.setStartValue(self.width())
92
+ self.animation.setEndValue(0)
93
+ self.animation.setEasingCurve(QEasingCurve.Type.InOutQuad)
94
+ self.animation.finished.connect(lambda: self.setVisible(False))
95
+ self.animation.start()
96
+ else:
97
+ self.setVisible(True)
98
+ self.animation = QPropertyAnimation(self, b"maximumWidth")
99
+ self.animation.setDuration(self.animation_duration)
100
+ self.animation.setStartValue(0)
101
+ self.animation.setEndValue(400) # Fixed width when expanded
102
+ self.animation.setEasingCurve(QEasingCurve.Type.InOutQuad)
103
+ self.animation.start()
104
+
105
+
106
+ class FileConverterApp(QMainWindow):
107
+ def __init__(self):
108
+ super().__init__()
109
+ self.setWindowTitle("๐ŸŽฏ AI File Converter Pro")
110
+ self.setGeometry(100, 100, 1400, 900)
111
+
112
+ # Sidebar state
113
+ self.sidebar_visible = True
114
+
115
+ # Set modern style
116
+ self.setStyleSheet("""
117
+ QMainWindow {
118
+ background-color: #1e1e2e;
119
+ }
120
+ QGroupBox {
121
+ font-weight: bold;
122
+ border: 2px solid #313244;
123
+ border-radius: 8px;
124
+ margin-top: 10px;
125
+ padding-top: 10px;
126
+ color: #cdd6f4;
127
+ font-size: 13px;
128
+ }
129
+ QGroupBox::title {
130
+ subcontrol-origin: margin;
131
+ left: 10px;
132
+ padding: 0 5px 0 5px;
133
+ color: #89b4fa;
134
+ }
135
+ QPushButton {
136
+ background-color: #89b4fa;
137
+ border: none;
138
+ padding: 8px;
139
+ border-radius: 6px;
140
+ font-weight: bold;
141
+ color: #1e1e2e;
142
+ }
143
+ QPushButton:hover {
144
+ background-color: #b4befe;
145
+ }
146
+ QPushButton:pressed {
147
+ background-color: #6c7086;
148
+ }
149
+ QToolButton {
150
+ background-color: #313244;
151
+ border: 1px solid #45475a;
152
+ border-radius: 6px;
153
+ padding: 8px;
154
+ color: #cdd6f4;
155
+ }
156
+ QToolButton:hover {
157
+ background-color: #45475a;
158
+ }
159
+ QListWidget, QTextEdit, QLineEdit, QComboBox {
160
+ background-color: #313244;
161
+ border: 1px solid #45475a;
162
+ border-radius: 6px;
163
+ padding: 5px;
164
+ color: #cdd6f4;
165
+ }
166
+ QProgressBar {
167
+ border: 2px solid #45475a;
168
+ border-radius: 6px;
169
+ text-align: center;
170
+ color: #cdd6f4;
171
+ }
172
+ QProgressBar::chunk {
173
+ background-color: #89b4fa;
174
+ border-radius: 4px;
175
+ }
176
+ QTabWidget::pane {
177
+ background-color: #1e1e2e;
178
+ border: 1px solid #313244;
179
+ border-radius: 6px;
180
+ }
181
+ QTabBar::tab {
182
+ background-color: #313244;
183
+ padding: 8px;
184
+ margin: 2px;
185
+ border-radius: 4px;
186
+ color: #cdd6f4;
187
+ }
188
+ QTabBar::tab:selected {
189
+ background-color: #89b4fa;
190
+ color: #1e1e2e;
191
+ }
192
+ QLabel {
193
+ color: #cdd6f4;
194
+ }
195
+ QCheckBox {
196
+ color: #cdd6f4;
197
+ }
198
+ QSplitter::handle {
199
+ background-color: #313244;
200
+ margin: 2px;
201
+ }
202
+ QSplitter::handle:hover {
203
+ background-color: #89b4fa;
204
+ }
205
+ """)
206
+
207
+ # Initialize converters
208
+ self.doc_converter = DocumentConverter()
209
+ self.img_converter = ImageConverter()
210
+ self.video_converter = VideoConverter()
211
+ self.audio_converter = AudioConverter()
212
+ self.ai_assistant = AIAssistant()
213
+
214
+ # Conversion queue
215
+ self.conversion_queue = []
216
+ self.current_conversion = None
217
+ self.active_threads = []
218
+ self.converted_count = 0
219
+ self.failed_count = 0
220
+
221
+ self.setup_ui()
222
+ self.setup_drag_drop()
223
+ self.setup_menu_bar()
224
+
225
+ def setup_menu_bar(self):
226
+ """Setup menu bar with toggle option"""
227
+ menubar = self.menuBar()
228
+ menubar.setStyleSheet("""
229
+ QMenuBar {
230
+ background-color: #1e1e2e;
231
+ color: #cdd6f4;
232
+ border-bottom: 1px solid #313244;
233
+ }
234
+ QMenuBar::item:selected {
235
+ background-color: #313244;
236
+ }
237
+ QMenu {
238
+ background-color: #1e1e2e;
239
+ color: #cdd6f4;
240
+ border: 1px solid #313244;
241
+ }
242
+ QMenu::item:selected {
243
+ background-color: #89b4fa;
244
+ color: #1e1e2e;
245
+ }
246
+ """)
247
+
248
+ view_menu = menubar.addMenu("๐Ÿ‘๏ธ View")
249
+
250
+ toggle_action = QAction("๐Ÿค– Toggle AI Assistant", self)
251
+ toggle_action.setShortcut("Ctrl+Shift+A")
252
+ toggle_action.triggered.connect(self.toggle_sidebar)
253
+ view_menu.addAction(toggle_action)
254
+
255
+ # Add separator and other options
256
+ view_menu.addSeparator()
257
+
258
+ reset_view_action = QAction("๐Ÿ”„ Reset Layout", self)
259
+ reset_view_action.setShortcut("Ctrl+Shift+R")
260
+ reset_view_action.triggered.connect(self.reset_layout)
261
+ view_menu.addAction(reset_view_action)
262
+
263
+ def setup_ui(self):
264
+ central_widget = QWidget()
265
+ self.setCentralWidget(central_widget)
266
+ main_layout = QHBoxLayout(central_widget)
267
+ main_layout.setContentsMargins(0, 0, 0, 0)
268
+ main_layout.setSpacing(0)
269
+
270
+ # Create splitter for resizable panels
271
+ self.main_splitter = QSplitter(Qt.Orientation.Horizontal)
272
+
273
+ # Left panel - Main content
274
+ self.left_panel = self.create_left_panel()
275
+ self.main_splitter.addWidget(self.left_panel)
276
+
277
+ # Right panel - AI Sidebar (collapsible)
278
+ self.sidebar_widget = self.create_sidebar()
279
+ self.main_splitter.addWidget(self.sidebar_widget)
280
+
281
+ # Set initial sizes (70% left, 30% right)
282
+ self.main_splitter.setSizes([980, 420])
283
+
284
+ main_layout.addWidget(self.main_splitter)
285
+
286
+ def create_left_panel(self):
287
+ """Create the main left panel with file list and controls"""
288
+ left_panel = QWidget()
289
+ left_layout = QVBoxLayout(left_panel)
290
+ left_layout.setSpacing(10)
291
+ left_layout.setContentsMargins(10, 10, 10, 10)
292
+
293
+ # File selection area
294
+ file_group = QGroupBox("๐Ÿ“ Files to Convert")
295
+ file_layout = QVBoxLayout()
296
+
297
+ self.file_list = QListWidget()
298
+ self.file_list.setSelectionMode(QListWidget.SelectionMode.ExtendedSelection)
299
+ self.file_list.setMinimumHeight(200)
300
+ file_layout.addWidget(self.file_list)
301
+
302
+ btn_layout = QHBoxLayout()
303
+ add_btn = QPushButton("โž• Add Files")
304
+ add_btn.clicked.connect(self.add_files)
305
+ add_folder_btn = QPushButton("๐Ÿ“‚ Add Folder")
306
+ add_folder_btn.clicked.connect(self.add_folder)
307
+ remove_btn = QPushButton("โŒ Remove Selected")
308
+ remove_btn.clicked.connect(self.remove_selected)
309
+ clear_btn = QPushButton("๐Ÿ—‘๏ธ Clear All")
310
+ clear_btn.clicked.connect(self.clear_files)
311
+
312
+ btn_layout.addWidget(add_btn)
313
+ btn_layout.addWidget(add_folder_btn)
314
+ btn_layout.addWidget(remove_btn)
315
+ btn_layout.addWidget(clear_btn)
316
+ file_layout.addLayout(btn_layout)
317
+
318
+ file_group.setLayout(file_layout)
319
+ left_layout.addWidget(file_group)
320
+
321
+ # Converter settings
322
+ converter_group = QGroupBox("โš™๏ธ Converter Settings")
323
+ converter_layout = QGridLayout()
324
+ converter_layout.setSpacing(10)
325
+
326
+ converter_layout.addWidget(QLabel("File Type:"), 0, 0)
327
+ self.file_type_combo = QComboBox()
328
+ self.file_type_combo.addItems(["Document", "Image", "Video", "Audio"])
329
+ self.file_type_combo.currentTextChanged.connect(self.on_file_type_changed)
330
+ converter_layout.addWidget(self.file_type_combo, 0, 1)
331
+
332
+ converter_layout.addWidget(QLabel("Output Format:"), 1, 0)
333
+ self.output_format_combo = QComboBox()
334
+ converter_layout.addWidget(self.output_format_combo, 1, 1)
335
+
336
+ converter_layout.addWidget(QLabel("Quality:"), 2, 0)
337
+ self.quality_combo = QComboBox()
338
+ self.quality_combo.addItems(["High", "Medium", "Low"])
339
+ converter_layout.addWidget(self.quality_combo, 2, 1)
340
+
341
+ converter_layout.addWidget(QLabel("Output Folder:"), 3, 0)
342
+ self.output_folder = QLineEdit()
343
+ self.output_folder.setText(str(Path.home() / "Downloads" / "Converted"))
344
+ converter_layout.addWidget(self.output_folder, 3, 1)
345
+
346
+ browse_btn = QPushButton("๐Ÿ“ Browse")
347
+ browse_btn.clicked.connect(self.browse_output_folder)
348
+ converter_layout.addWidget(browse_btn, 3, 2)
349
+
350
+ # Create output folder if not exists
351
+ Path(self.output_folder.text()).mkdir(parents=True, exist_ok=True)
352
+
353
+ self.ai_checkbox = QCheckBox("๐Ÿค– Enable AI Enhancement")
354
+ self.ai_checkbox.setChecked(False)
355
+ converter_layout.addWidget(self.ai_checkbox, 4, 0, 1, 2)
356
+
357
+ converter_group.setLayout(converter_layout)
358
+ left_layout.addWidget(converter_group)
359
+
360
+ # Progress section
361
+ progress_group = QGroupBox("๐Ÿ“Š Conversion Progress")
362
+ progress_layout = QVBoxLayout()
363
+
364
+ self.progress_bar = QProgressBar()
365
+ progress_layout.addWidget(self.progress_bar)
366
+
367
+ self.status_label = QLabel("โœ… Ready to convert")
368
+ self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
369
+ progress_layout.addWidget(self.status_label)
370
+
371
+ self.stats_label = QLabel("๐Ÿ“ˆ Converted: 0 | โŒ Failed: 0")
372
+ self.stats_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
373
+ progress_layout.addWidget(self.stats_label)
374
+
375
+ progress_group.setLayout(progress_layout)
376
+ left_layout.addWidget(progress_group)
377
+
378
+ # Convert button
379
+ self.convert_btn = QPushButton("๐Ÿš€ Start Conversion")
380
+ self.convert_btn.setMinimumHeight(50)
381
+ self.convert_btn.setStyleSheet("""
382
+ QPushButton {
383
+ background-color: #a6e3a1;
384
+ font-size: 16px;
385
+ font-weight: bold;
386
+ }
387
+ QPushButton:hover {
388
+ background-color: #94e2d5;
389
+ }
390
+ """)
391
+ self.convert_btn.clicked.connect(self.start_conversion)
392
+ left_layout.addWidget(self.convert_btn)
393
+
394
+ return left_panel
395
+
396
+ def create_sidebar(self):
397
+ """Create the collapsible AI sidebar"""
398
+ sidebar_container = QWidget()
399
+ sidebar_layout = QVBoxLayout(sidebar_container)
400
+ sidebar_layout.setContentsMargins(0, 0, 0, 0)
401
+ sidebar_layout.setSpacing(0)
402
+
403
+ # Header with toggle button
404
+ header = QWidget()
405
+ header.setStyleSheet("""
406
+ QWidget {
407
+ background-color: #313244;
408
+ border-bottom: 1px solid #45475a;
409
+ }
410
+ """)
411
+ header_layout = QHBoxLayout(header)
412
+ header_layout.setContentsMargins(10, 10, 10, 10)
413
+
414
+ title_label = QLabel("๐Ÿค– AI Assistant")
415
+ title_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #89b4fa;")
416
+ header_layout.addWidget(title_label)
417
+
418
+ header_layout.addStretch()
419
+
420
+ # Close button
421
+ self.close_sidebar_btn = QToolButton()
422
+ self.close_sidebar_btn.setText("โ—€")
423
+ self.close_sidebar_btn.setToolTip("Close Sidebar (Ctrl+Shift+A)")
424
+ self.close_sidebar_btn.setStyleSheet("""
425
+ QToolButton {
426
+ background-color: #45475a;
427
+ border: none;
428
+ font-size: 14px;
429
+ padding: 5px;
430
+ }
431
+ QToolButton:hover {
432
+ background-color: #6c7086;
433
+ }
434
+ """)
435
+ self.close_sidebar_btn.clicked.connect(self.toggle_sidebar)
436
+ header_layout.addWidget(self.close_sidebar_btn)
437
+
438
+ sidebar_layout.addWidget(header)
439
+
440
+ # Tab widget for AI and Log
441
+ right_panel = QTabWidget()
442
+ right_panel.setStyleSheet("""
443
+ QTabWidget::pane {
444
+ border: none;
445
+ }
446
+ """)
447
+
448
+ # AI Assistant tab
449
+ ai_widget = QWidget()
450
+ ai_layout = QVBoxLayout(ai_widget)
451
+ ai_layout.setSpacing(10)
452
+
453
+ # Suggestions panel
454
+ suggestions_group = QGroupBox("๐Ÿ’ก Quick Commands")
455
+ suggestions_layout = QGridLayout()
456
+ suggestions_group.setMaximumHeight(220)
457
+
458
+ suggestions = [
459
+ ("๐Ÿ–ผ๏ธ To PNG", "convert this to png"),
460
+ ("๐Ÿ“„ To PDF", "convert to pdf"),
461
+ ("๐ŸŽต To MP3", "make it mp3"),
462
+ ("๐ŸŽฌ Web format", "best format for web"),
463
+ ("๐Ÿ“Š Analyze", "analyze this file"),
464
+ ("๐Ÿ’พ Compress", "how to compress video"),
465
+ ("๐Ÿ“ง Email size", "best format for email"),
466
+ ("๐Ÿ–จ๏ธ Print", "best format for printing"),
467
+ ]
468
+
469
+ row, col = 0, 0
470
+ for text, cmd in suggestions:
471
+ btn = QPushButton(text)
472
+ btn.setMaximumHeight(30)
473
+ btn.clicked.connect(lambda checked, c=cmd: self.ai_input.setText(c))
474
+ suggestions_layout.addWidget(btn, row, col)
475
+ col += 1
476
+ if col >= 2:
477
+ col = 0
478
+ row += 1
479
+
480
+ suggestions_group.setLayout(suggestions_layout)
481
+ ai_layout.addWidget(suggestions_group)
482
+
483
+ # Input area
484
+ self.ai_input = QTextEdit()
485
+ self.ai_input.setPlaceholderText("""๐Ÿ’ฌ Ask me anything about file conversion...
486
+
487
+ Examples:
488
+ โ€ข "Convert this to PNG"
489
+ โ€ข "What's the best format for web images?"
490
+ โ€ข "How to compress video for email?"
491
+ โ€ข "Analyze this file"
492
+ โ€ข "Make it smaller without losing quality" """)
493
+ self.ai_input.setMaximumHeight(100)
494
+ ai_layout.addWidget(self.ai_input)
495
+
496
+ # Buttons
497
+ ai_buttons_layout = QHBoxLayout()
498
+ ai_send_btn = QPushButton("๐Ÿ’ญ Ask AI")
499
+ ai_send_btn.clicked.connect(self.ask_ai_assistant)
500
+ ai_clear_btn = QPushButton("๐Ÿ—‘๏ธ Clear")
501
+ ai_clear_btn.clicked.connect(lambda: self.ai_input.clear())
502
+ ai_buttons_layout.addWidget(ai_send_btn)
503
+ ai_buttons_layout.addWidget(ai_clear_btn)
504
+ ai_layout.addLayout(ai_buttons_layout)
505
+
506
+ # Response area
507
+ self.ai_response = QTextEdit()
508
+ self.ai_response.setReadOnly(True)
509
+ self.ai_response.setPlaceholderText("AI response will appear here...")
510
+ self.ai_response.setMinimumHeight(200)
511
+ ai_layout.addWidget(self.ai_response)
512
+
513
+ right_panel.addTab(ai_widget, "๐Ÿ’ฌ Assistant")
514
+
515
+ # Log tab
516
+ log_widget = QWidget()
517
+ log_layout = QVBoxLayout(log_widget)
518
+ self.log_text = QTextEdit()
519
+ self.log_text.setReadOnly(True)
520
+ log_layout.addWidget(self.log_text)
521
+
522
+ log_btn_layout = QHBoxLayout()
523
+ clear_log_btn = QPushButton("๐Ÿ—‘๏ธ Clear Log")
524
+ clear_log_btn.clicked.connect(lambda: self.log_text.clear())
525
+ log_btn_layout.addStretch()
526
+ log_btn_layout.addWidget(clear_log_btn)
527
+ log_layout.addLayout(log_btn_layout)
528
+
529
+ right_panel.addTab(log_widget, "๐Ÿ“ Log")
530
+
531
+ sidebar_layout.addWidget(right_panel)
532
+
533
+ return sidebar_container
534
+
535
+ def toggle_sidebar(self):
536
+ """Toggle the AI sidebar visibility"""
537
+ self.sidebar_visible = not self.sidebar_visible
538
+
539
+ if self.sidebar_visible:
540
+ self.sidebar_widget.setVisible(True)
541
+ self.close_sidebar_btn.setText("โ—€")
542
+ self.main_splitter.setSizes([980, 420])
543
+ else:
544
+ self.sidebar_widget.setVisible(False)
545
+ self.close_sidebar_btn.setText("โ–ถ")
546
+ self.main_splitter.setSizes([1400, 0])
547
+
548
+ # Update tooltip
549
+ self.close_sidebar_btn.setToolTip(
550
+ "Open Sidebar (Ctrl+Shift+A)" if not self.sidebar_visible else "Close Sidebar (Ctrl+Shift+A)")
551
+
552
+ def reset_layout(self):
553
+ """Reset layout to default"""
554
+ self.main_splitter.setSizes([980, 420])
555
+ if not self.sidebar_visible:
556
+ self.toggle_sidebar()
557
+ self.log_message("๐Ÿ”„ Layout reset to default")
558
+
559
+ def setup_drag_drop(self):
560
+ self.setAcceptDrops(True)
561
+
562
+ def dragEnterEvent(self, event: QDragEnterEvent):
563
+ if event.mimeData().hasUrls():
564
+ event.acceptProposedAction()
565
+
566
+ def dropEvent(self, event: QDropEvent):
567
+ for url in event.mimeData().urls():
568
+ file_path = url.toLocalFile()
569
+ if os.path.isfile(file_path):
570
+ self.add_file_to_list(file_path)
571
+
572
+ def add_files(self):
573
+ files, _ = QFileDialog.getOpenFileNames(
574
+ self, "Select Files to Convert", "",
575
+ "All Files (*.*)"
576
+ )
577
+ for file in files:
578
+ self.add_file_to_list(file)
579
+
580
+ def add_folder(self):
581
+ folder = QFileDialog.getExistingDirectory(self, "Select Folder")
582
+ if folder:
583
+ for root, dirs, files in os.walk(folder):
584
+ for file in files:
585
+ self.add_file_to_list(os.path.join(root, file))
586
+
587
+ def remove_selected(self):
588
+ for item in self.file_list.selectedItems():
589
+ self.file_list.takeItem(self.file_list.row(item))
590
+
591
+ def add_file_to_list(self, file_path):
592
+ # Check if already in list
593
+ for i in range(self.file_list.count()):
594
+ if self.file_list.item(i).data(Qt.ItemDataRole.UserRole) == file_path:
595
+ return
596
+ item = QListWidgetItem(f"๐Ÿ“„ {os.path.basename(file_path)}")
597
+ item.setToolTip(file_path)
598
+ item.setData(Qt.ItemDataRole.UserRole, file_path)
599
+ self.file_list.addItem(item)
600
+
601
+ def clear_files(self):
602
+ self.file_list.clear()
603
+
604
+ def on_file_type_changed(self, file_type):
605
+ self.output_format_combo.clear()
606
+
607
+ formats = {
608
+ "Document": ["pdf", "docx", "txt", "html", "md"],
609
+ "Image": ["png", "jpg", "webp", "bmp", "tiff"],
610
+ "Video": ["mp4", "avi", "mkv", "mov", "webm", "gif"],
611
+ "Audio": ["mp3", "wav", "ogg", "m4a", "flac"]
612
+ }
613
+ self.output_format_combo.addItems(formats.get(file_type, []))
614
+
615
+ def browse_output_folder(self):
616
+ folder = QFileDialog.getExistingDirectory(self, "Select Output Folder")
617
+ if folder:
618
+ self.output_folder.setText(folder)
619
+ Path(folder).mkdir(parents=True, exist_ok=True)
620
+
621
+ def get_converter(self):
622
+ file_type = self.file_type_combo.currentText()
623
+ converters = {
624
+ "Document": self.doc_converter,
625
+ "Image": self.img_converter,
626
+ "Video": self.video_converter,
627
+ "Audio": self.audio_converter
628
+ }
629
+ return converters.get(file_type)
630
+
631
+ def start_conversion(self):
632
+ if self.file_list.count() == 0:
633
+ QMessageBox.warning(self, "Warning", "Please add files to convert")
634
+ return
635
+
636
+ # Reset stats
637
+ self.converted_count = 0
638
+ self.failed_count = 0
639
+ self.update_stats()
640
+
641
+ # Build queue
642
+ self.conversion_queue = []
643
+ selected_items = self.file_list.selectedItems()
644
+ if selected_items:
645
+ for item in selected_items:
646
+ self.conversion_queue.append(item.data(Qt.ItemDataRole.UserRole))
647
+ else:
648
+ for i in range(self.file_list.count()):
649
+ self.conversion_queue.append(self.file_list.item(i).data(Qt.ItemDataRole.UserRole))
650
+
651
+ self.convert_btn.setEnabled(False)
652
+ self.process_next_conversion()
653
+
654
+ def process_next_conversion(self):
655
+ if not self.conversion_queue:
656
+ self.log_message("๐ŸŽ‰ All conversions completed!")
657
+ self.status_label.setText("โœ… All conversions completed!")
658
+ self.progress_bar.setValue(100)
659
+ self.convert_btn.setEnabled(True)
660
+ QMessageBox.information(self, "Success",
661
+ f"Conversion complete!\nโœ… Success: {self.converted_count}\nโŒ Failed: {self.failed_count}")
662
+ return
663
+
664
+ input_path = self.conversion_queue.pop(0)
665
+ self.current_conversion = input_path
666
+
667
+ # Get output path
668
+ output_format = self.output_format_combo.currentText()
669
+ output_folder = self.output_folder.text()
670
+ base_name = Path(input_path).stem
671
+ output_path = Path(output_folder) / f"{base_name}.{output_format}"
672
+
673
+ # Create output directory if needed
674
+ Path(output_folder).mkdir(parents=True, exist_ok=True)
675
+
676
+ # Quality settings
677
+ quality_map = {
678
+ "High": {"video": "18", "audio": "320k", "image": 95},
679
+ "Medium": {"video": "23", "audio": "192k", "image": 85},
680
+ "Low": {"video": "28", "audio": "128k", "image": 70}
681
+ }
682
+ quality = self.quality_combo.currentText()
683
+
684
+ # Prepare options
685
+ options = {
686
+ "ai_enhancement": self.ai_checkbox.isChecked(),
687
+ "quality": quality,
688
+ "quality_settings": quality_map[quality]
689
+ }
690
+
691
+ # Start conversion thread
692
+ converter = self.get_converter()
693
+ if converter:
694
+ self.conversion_thread = ConversionThread(
695
+ converter, input_path, str(output_path), options
696
+ )
697
+ self.conversion_thread.progress_update.connect(self.update_progress)
698
+ self.conversion_thread.status_update.connect(self.update_status)
699
+ self.conversion_thread.conversion_complete.connect(self.on_conversion_complete)
700
+ self.conversion_thread.start()
701
+ self.active_threads.append(self.conversion_thread)
702
+ else:
703
+ self.log_message(f"โŒ Invalid converter for: {input_path}")
704
+ self.process_next_conversion()
705
+
706
+ def update_progress(self, value):
707
+ self.progress_bar.setValue(value)
708
+
709
+ def update_status(self, status):
710
+ self.status_label.setText(status)
711
+ self.log_message(status)
712
+
713
+ def on_conversion_complete(self, success, message):
714
+ self.log_message(message)
715
+ if success:
716
+ self.converted_count += 1
717
+ else:
718
+ self.failed_count += 1
719
+ self.update_stats()
720
+ self.process_next_conversion()
721
+
722
+ def update_stats(self):
723
+ self.stats_label.setText(f"๐Ÿ“ˆ Converted: {self.converted_count} | โŒ Failed: {self.failed_count}")
724
+
725
+ def log_message(self, message):
726
+ timestamp = datetime.now().strftime("%H:%M:%S")
727
+ self.log_text.append(f"[{timestamp}] {message}")
728
+
729
+ def ask_ai_assistant(self):
730
+ """Process AI query and potentially auto-configure settings"""
731
+ question = self.ai_input.toPlainText()
732
+ if not question.strip():
733
+ return
734
+
735
+ self.ai_response.setPlainText("๐Ÿค” AI Assistant is thinking...")
736
+
737
+ # Get AI response
738
+ response = self.ai_assistant.ask(question, self.get_conversion_context())
739
+
740
+ # Check if AI suggested a format change
741
+ suggested_format = self.extract_format_from_response(response)
742
+ if suggested_format:
743
+ # Auto-update the output format combo box
744
+ index = self.output_format_combo.findText(suggested_format.lower())
745
+ if index >= 0:
746
+ self.output_format_combo.setCurrentIndex(index)
747
+ response += f"\n\nโœ… **Auto-configured**: Output format changed to {suggested_format.upper()}"
748
+
749
+ # Check if AI suggested a file type change
750
+ suggested_type = self.extract_file_type_from_response(response)
751
+ if suggested_type:
752
+ index = self.file_type_combo.findText(suggested_type.title())
753
+ if index >= 0:
754
+ self.file_type_combo.setCurrentIndex(index)
755
+ response += f"\n\nโœ… **Auto-configured**: File type changed to {suggested_type.title()}"
756
+
757
+ self.ai_response.setPlainText(response)
758
+
759
+ def extract_format_from_response(self, response: str) -> str:
760
+ """Extract suggested format from AI response"""
761
+ formats = ['pdf', 'docx', 'txt', 'html', 'md', 'png', 'jpg', 'jpeg',
762
+ 'webp', 'bmp', 'tiff', 'mp4', 'avi', 'mkv', 'mov', 'webm',
763
+ 'gif', 'mp3', 'wav', 'ogg', 'm4a', 'flac']
764
+
765
+ for fmt in formats:
766
+ if f" {fmt.upper()}" in response.upper() or f"to {fmt}" in response.lower():
767
+ return fmt
768
+ return ""
769
+
770
+ def extract_file_type_from_response(self, response: str) -> str:
771
+ """Extract suggested file type from AI response"""
772
+ types = ['document', 'image', 'video', 'audio']
773
+ for t in types:
774
+ if t in response.lower():
775
+ return t
776
+ return ""
777
+
778
+ def get_conversion_context(self):
779
+ return {
780
+ "file_type": self.file_type_combo.currentText(),
781
+ "output_format": self.output_format_combo.currentText(),
782
+ "file_count": self.file_list.count(),
783
+ "ai_enhancement": self.ai_checkbox.isChecked(),
784
+ "quality": self.quality_combo.currentText()
785
+ }
786
+
787
+
788
+ if __name__ == "__main__":
789
+ app = QApplication(sys.argv)
790
+ window = FileConverterApp()
791
+ window.show()
792
+ sys.exit(app.exec())
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ PyQt6>=6.5.0
2
+ Pillow>=10.0.0
3
+ python-docx>=0.8.11
4
+ PyMuPDF>=1.23.0
5
+ markdown>=3.4.0
6
+ beautifulsoup4>=4.12.0
7
+ pydub>=0.25.1
8
+ opencv-python>=4.8.0
9
+ pytesseract>=0.3.10
10
+ numpy>=1.24.0