File size: 15,166 Bytes
a3fda21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import os
import threading, queue
from PIL import Image, UnidentifiedImageError

# Global variables for managing state and errors
stop_processing = False
error_messages = []
selected_files = []
error_list = []

def open_image_error_fix():
    global stop_processing, error_messages, selected_files, status_var, num_files_var, errors_var, progress, q, error_list, error_window, thread_count_var

    # Initialize the main Tkinter window
    root = tk.Tk()
    root.title("Image Error Fix")

    # Initialize Tkinter variables
    status_var = tk.StringVar()
    num_files_var = tk.StringVar()
    errors_var = tk.StringVar(value="Errors: 0")
    progress = tk.IntVar()
    thread_count_var = tk.StringVar(value="4")  # Default thread count
    q = queue.Queue()

    def center_window(window):
        window.update_idletasks()
        width = window.winfo_width() + 120
        height = window.winfo_height()
        x = (window.winfo_screenwidth() // 2) - (width // 2)
        y = (window.winfo_screenheight() // 2) - (height // 2)
        window.geometry(f'{width}x{height}+{x}+{y}')

    def validate_number(P):
        return P.isdigit() or P == ""

    def validate_thread_count(var):
        value = var.get()
        if value != "":
            try:
                int_value = int(value)
                if int_value <= 0:
                    raise ValueError
            except ValueError:
                messagebox.showerror("Invalid Input", "Please enter a valid number of threads.")
                var.set("")

    def select_files():
        global selected_files
        filetypes = [
            ("All Image files", "*.jpg;*.jpeg;*.png;*.gif;*.bmp;*.tiff;*.tif;*.svg;*.webp"),
            ("JPEG files", "*.jpg;*.jpeg"),
            ("PNG files", "*.png"),
            ("GIF files", "*.gif"),
            ("BMP files", "*.bmp"),
            ("TIFF files", "*.tiff;*.tif"),
            ("SVG files", "*.svg"),
            ("WEBP files", "*.webp")
        ]
        filepaths = filedialog.askopenfilenames(title="Select Image Files", filetypes=filetypes)
        if filepaths:
            selected_files.clear()
            selected_files.extend(filepaths)
            num_files_var.set(f"{len(selected_files)} files selected.")

    def detect_errors(file_path):
        """Detect potential errors in an image file."""
        errors = []
        if not os.access(file_path, os.R_OK):
            errors.append("Permission Denied")
        # Additional error checks
        try:
            with Image.open(file_path) as img:
                img.verify()  # Verify the image integrity
                img = Image.open(file_path)
                img.load()  # Load the image data to ensure it's not truncated
        except UnidentifiedImageError:
            errors.append("Unidentified image format or corrupted file")
        except IOError as e:
            errors.append(f"IOError: {str(e)}")
        except Exception as e:
            errors.append(f"Unknown error: {str(e)}")
        return errors

    def fix_error(file_path, error_type):
        """Fix errors in an image file, if possible."""
        if error_type == "Permission Denied":
            # Attempt to change permissions
            try:
                os.chmod(file_path, 0o644)
                return "Permissions fixed. File permissions were successfully updated."
            except Exception as e:
                return f"Failed to fix permissions. Ensure you have the necessary permissions to modify this file. Error: {str(e)}"
        elif error_type == "Unidentified image format or corrupted file":
            return "The file format is unrecognized or the file is corrupted. Please try to re-download or restore from a backup."
        elif error_type == "Invalid Format":
            return ("Cannot automatically fix invalid formats. Ensure the file extension matches the file content.\n"
                    "To fix this issue, please follow these steps:\n"
                    "1. Identify the correct file format by using a file type checker tool (e.g., CheckFileType.com).\n"
                    "2. Rename the file with the correct extension:\n"
                    "   - Right-click on the file and select 'Rename'.\n"
                    "   - Change the file extension to the correct format (e.g., .jpg, .png).\n"
                    "   - Press Enter to save the changes.\n"
                    "3. Try opening the file again. If the problem persists, the file may be corrupted or contain unsupported data.")
        elif error_type == "File Not Found":
            return ("The file could not be found at the specified path. Please ensure that the file has not been moved or deleted.\n"
                    "1. Verify the file path is correct.\n"
                    "2. If the file has been moved, update the path in the application or locate the file manually.\n"
                    "3. If the file was deleted, check your Recycle Bin or use a data recovery tool to attempt to restore it.")
        elif error_type == "File Path Too Long":
            return ("The file path exceeds the system limit. Windows has a maximum path length of 260 characters.\n"
                    "To fix this issue:\n"
                    "1. Move the file to a location with a shorter path, closer to the root directory (e.g., C:\\).\n"
                    "2. Rename folders in the path to shorter names.\n"
                    "3. Enable long path support in Windows if using Windows 10 (version 1607 hoặc cao hơn):\n"
                    "   - Open the Group Policy Editor (gpedit.msc).\n"
                    "   - Navigate to 'Local Computer Policy > Computer Configuration > Administrative Templates > System > Filesystem'.\n"
                    "   - Enable the 'Enable Win32 long paths' option.")
        elif error_type == "Transmission Errors":
            return ("The file may be incomplete or corrupted due to transmission errors. This can happen if the file was downloaded or transferred incorrectly.\n"
                    "To fix this issue:\n"
                    "1. Re-download hoặc re-transfer the file.\n"
                    "2. Use a reliable network connection to ensure the file is not corrupted during transfer.\n"
                    "3. Verify the integrity of the file by comparing checksums (if available).")
        elif error_type == "Unsupported Format":
            return ("The file format is not supported by this application. Supported formats include JPEG, PNG, GIF, BMP, TIFF, SVG, và WEBP.\n"
                    "To fix this issue:\n"
                    "1. Convert the file to a supported format using an image converter tool.\n"
                    "2. Ensure that the file is not corrupted and can be opened with other image viewers or editors.")
        else:
            return "No action taken. This error type is not recognized or cannot be fixed automatically."

    def delete_file(file_path):
        """Delete the specified file."""
        try:
            os.remove(file_path)
            # Remove from selected files
            selected_files.remove(file_path)
            num_files_var.set(f"{len(selected_files)} files selected.")
            return f"File {file_path} deleted successfully."
        except Exception as e:
            return f"Failed to delete file {file_path}. Error: {str(e)}"

    def process_image(file_path, q):
        if stop_processing:
            return

        errors = detect_errors(file_path)
        if errors:
            q.put((file_path, errors))

    def worker():
        try:
            progress.set(0)
            total_files = len(selected_files)
            thread_count = int(thread_count_var.get())  # Get the number of threads
            for i, input_path in enumerate(selected_files, 1):
                if stop_processing:
                    break

                thread = threading.Thread(target=process_image, args=(input_path, q))
                thread.start()
                thread.join()

                # Update progress bar
                progress.set(int(i / total_files * 100))
            
            q.put(None)
        except Exception as e:
            if not stop_processing:
                q.put(e)

    def update_progress():
        try:
            global error_list
            error_list = []
            while True:
                item = q.get()
                if item is None:
                    break
                if isinstance(item, tuple):
                    error_list.append(item)

            if not error_list:
                messagebox.showinfo("No Errors Found", "No errors were detected in the selected images.")
            else:
                errors_var.set(f"Errors: {len(error_list)}")
                display_errors(error_list)
        except Exception as e:
            if not stop_processing:
                root.after(0, status_var.set, f"Error: {e}")

    def update_error_display():
        """Update the display of errors in the error window."""
        global error_list, frame
        # Clear all existing widgets in the frame
        for widget in frame.winfo_children():
            widget.destroy()

        # Repopulate the frame with updated error list
        for file_path, errors in error_list:
            for error in errors:
                frame_row = tk.Frame(frame)
                frame_row.pack(fill="x", pady=2)

                # Hiển thị đường dẫn đầy đủ và tự động xuống dòng
                file_label = tk.Label(frame_row, text=f"{file_path}: {error}", anchor="w", wraplength=500, justify='left')
                file_label.pack(side=tk.LEFT, fill="x", expand=True)

                delete_button = tk.Button(frame_row, text="Delete", command=lambda fp=file_path: delete_file_action(fp))
                delete_button.pack(side=tk.RIGHT)

                fix_button = tk.Button(frame_row, text="Fix", command=lambda fp=file_path, et=error: fix_error_action(fp, et))
                fix_button.pack(side=tk.RIGHT)

                # Thêm dấu phân cách giữa các lỗi
                separator = ttk.Separator(frame, orient='horizontal')
                separator.pack(fill='x', pady=5)

        frame.update_idletasks()
        canvas.configure(scrollregion=canvas.bbox("all"))

    def display_errors(error_list):
        global error_window, canvas, frame
        # Create or raise the error window
        if 'error_window' in globals() and error_window.winfo_exists():
            error_window.lift()
        else:
            error_window = tk.Toplevel(root)
            error_window.title("Error Details")
            error_window.geometry("600x400")

            canvas = tk.Canvas(error_window)
            canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

            scrollbar = tk.Scrollbar(error_window, command=canvas.yview)
            scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

            canvas.configure(yscrollcommand=scrollbar.set)

            frame = tk.Frame(canvas)
            canvas.create_window((0, 0), window=frame, anchor="nw")

            update_error_display()

    def fix_error_action(file_path, error_type):
        fix_message = fix_error(file_path, error_type)
        messagebox.showinfo("Fix Error", fix_message)

    def delete_file_action(file_path):
        global error_list
        delete_message = delete_file(file_path)
        messagebox.showinfo("Delete File", delete_message)
        # Remove the error from the error_list and refresh the display
        error_list = [(fp, errs) for fp, errs in error_list if fp != file_path]
        errors_var.set(f"Errors: {len(error_list)}")
        update_error_display()

    def delete_all_errors():
        global error_list
        for file_path, _ in error_list:
            delete_file(file_path)
        error_list.clear()
        errors_var.set("Errors: 0")
        messagebox.showinfo("Delete All Files", "All files with errors have been deleted.")
        update_error_display()  # Update the error display after deleting all errors

    def scan_and_fix():
        global stop_processing, error_messages
        stop_processing = False
        error_messages.clear()
        errors_var.set("Errors: 0")
        if not selected_files:
            status_var.set("Please select images to scan.")
            return

        threading.Thread(target=worker).start()
        threading.Thread(target=update_progress).start()

    def stop_processing_func():
        global stop_processing
        stop_processing = True
        status_var.set("Processing stopped.")

    def return_to_menu():
        stop_processing_func()
        root.destroy()
        import main
        main.open_main_menu()

    def on_closing():
        return_to_menu()

    # Create GUI elements
    validate_command = root.register(validate_number)

    back_button = tk.Button(root, text="<-", font=('Helvetica', 14), command=return_to_menu)
    back_button.pack(anchor='nw', padx=10, pady=10)

    title_label = tk.Label(root, text="Image Error Fix", font=('Helvetica', 16))
    title_label.pack(pady=10)

    select_files_button = tk.Button(root, text="Select Files", command=select_files)
    select_files_button.pack(pady=5)

    num_files_label = tk.Label(root, textvariable=num_files_var)
    num_files_label.pack(pady=5)

    thread_count_label = tk.Label(root, text="Number of Threads:")
    thread_count_label.pack(pady=5)

    thread_count_var = tk.StringVar(value="4")
    thread_count_entry = tk.Entry(root, textvariable=thread_count_var, width=3, justify='center', validate="key", validatecommand=(validate_command, '%P'))
    thread_count_entry.pack(pady=5)

    # Separator for aesthetics
    separator = ttk.Separator(root, orient='horizontal')
    separator.pack(fill='x', pady=10)

    scan_fix_button = tk.Button(root, text="Scan and Fix", command=scan_and_fix)
    scan_fix_button.pack(pady=10)

    stop_button = tk.Button(root, text="Stop", command=stop_processing_func)
    stop_button.pack(pady=5)

    progress_bar = ttk.Progressbar(root, variable=progress, maximum=100)
    progress_bar.pack(pady=5, fill=tk.X)

    delete_all_label = tk.Label(root, text="Click 'Delete All' to remove all files with errors.")
    delete_all_label.pack(pady=5)

    delete_all_button = tk.Button(root, text="Delete All", command=delete_all_errors)
    delete_all_button.pack(pady=5)

    errors_label = tk.Label(root, textvariable=errors_var, fg="red")
    errors_label.pack(pady=5)

    status_label = tk.Label(root, textvariable=status_var, fg="green")
    status_label.pack(pady=5)

    center_window(root)
    root.protocol("WM_DELETE_WINDOW", on_closing)
    root.mainloop()

if __name__ == "__main__":
    open_image_error_fix()