diff --git a/kapitanbooru_uploader/ImageBrowser.py b/kapitanbooru_uploader/ImageBrowser.py index eac77c8..3015eb4 100644 --- a/kapitanbooru_uploader/ImageBrowser.py +++ b/kapitanbooru_uploader/ImageBrowser.py @@ -1,148 +1,38 @@ import glob import hashlib -import inspect import os import queue import threading import tkinter as tk from tkinter import filedialog, messagebox, ttk from typing import Dict, Tuple, Optional +import concurrent.futures from packaging.version import parse as parse_version import itertools import networkx as nx import requests -from PIL import Image, ImageTk, PngImagePlugin +from PIL import Image, ImageTk import wdtagger as wdt import tomli +from .ProcessingDialog import ProcessingDialog from .I18N import _ from .ProgressFile import ProgressFile from .TagsRepo import TagsRepo from .autocomplete import TagManager from .common import get_auth_token, login, open_tag_wiki_url, open_webbrowser from .settings import Settings -from .tag_processing import TAG_FIXES, parse_parameters, process_tag +from .tag_processing import TAG_FIXES, extract_parameters, parse_parameters, process_tag from .tagger_cache import TaggerCache -class ProcessingDialog: - def __init__(self, root, target_function, *args): - self.root = root - self.top = tk.Toplevel(root) - self.top.title(_("Processing...")) - self.top.geometry("300x150") - self.top.protocol("WM_DELETE_WINDOW", self.on_close) - - self.label = tk.Label(self.top, text=_("Processing, please wait...")) - self.label.pack(pady=10) - - # Start with indeterminate progress bar - self.progress = ttk.Progressbar(self.top, mode="indeterminate") - self.progress.pack(pady=10, fill="x") - self.progress.start(10) - - sig = inspect.signature(target_function) - if "secondary_progress_queue" in sig.parameters: - self.sub_progress = ttk.Progressbar(self.top, mode="indeterminate") - self.sub_progress.pack(pady=10, fill="x") - self.sub_progress.start(10) - - # Setup communication queue and periodic checker - self.queue = queue.Queue() - self.sub_queue = queue.Queue() - self.running = True - self.cancel_event = threading.Event() # Cancellation flag - self.thread = threading.Thread( - target=self.run_task, args=(target_function, *args) - ) - self.thread.start() - self.top.after(100, self.process_queue) - - def process_queue(self): - """Process messages from the background thread""" - while self.running: - try: - msg = self.queue.get_nowait() - - if msg[0] == "mode": - self.progress.config(mode=msg[1]) - if msg[1] == "determinate": - self.progress["value"] = 0 - self.progress.stop() - elif msg[1] == "indeterminate": - self.progress["value"] = 0 - self.progress.start() - elif msg[0] == "max": - self.progress["maximum"] = msg[1] - elif msg[0] == "progress": - self.progress["value"] = msg[1] - elif msg[0] == "label": - self.label.config(text=msg[1]) - - self.top.update_idletasks() - except queue.Empty: - break - - try: - msg = self.sub_queue.get_nowait() - - if msg[0] == "mode": - self.sub_progress.config(mode=msg[1]) - if msg[1] == "determinate": - self.sub_progress["value"] = 0 - self.sub_progress.stop() - elif msg[1] == "indeterminate": - self.sub_progress["value"] = 0 - self.sub_progress.start() - elif msg[0] == "max": - self.sub_progress["maximum"] = msg[1] - elif msg[0] == "progress": - self.sub_progress["value"] = msg[1] - - self.top.update_idletasks() - except queue.Empty: - break - - if self.running: - self.top.after(100, self.process_queue) - - def run_task(self, target_function, *args): - """Execute target function with progress queue if supported""" - try: - sig = inspect.signature(target_function) - kwargs = {} - if "progress_queue" in sig.parameters: - kwargs["progress_queue"] = self.queue - if "secondary_progress_queue" in sig.parameters: - kwargs["secondary_progress_queue"] = self.sub_queue - if "cancel_event" in sig.parameters: - kwargs["cancel_event"] = self.cancel_event - target_function(*args, **kwargs) - finally: - self.close_dialog() - - def close_dialog(self): - """Safely close the dialog""" - if self.running: - self.running = False - self.progress.stop() - self.top.after(0, self.top.destroy) - self.root.after(100, self.thread.join) - - def on_close(self): - """Handle manual window closure""" - self.running = False - self.cancel_event.set() # Notify target function that cancellation is requested - self.top.destroy() - - class ImageBrowser(tk.Tk): def __init__(self): super().__init__() self.title("Kapitanbooru Uploader") self.geometry("900x600") - self.version = "0.7.0" + self.version = "0.8.0" self.acknowledged_version = parse_version(self.version) self.settings = Settings() @@ -359,9 +249,7 @@ class ImageBrowser(tk.Tk): # Pobierz tagi z pliku try: img = Image.open(file_path) - parameters = "" - if isinstance(img, PngImagePlugin.PngImageFile): - parameters = img.info.get("parameters", "") + parameters = extract_parameters(img, file_path) png_tags = set( [ x @@ -785,27 +673,37 @@ class ImageBrowser(tk.Tk): def select_folder(self): """ Otwiera okno dialogowe wyboru folderu z obrazkami - i wczytuje pliki PNG z wybranego folderu. + i wczytuje pliki PNG, JPEG, WebP, AVIF i GIF z wybranego folderu. """ - folder = filedialog.askdirectory(title=_("Wybierz folder z obrazkami PNG")) + folder = filedialog.askdirectory( + title=_("Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF") + ) if folder: self.folder_path = folder self.load_images() def load_images(self): """ - Ładuje pliki PNG z wybranego folderu. + Ładuje pliki PNG, JPEG, WebP, AVIF i GIF z wybranego folderu. """ - pattern = os.path.join(self.folder_path, "*.png") - self.image_files = sorted(glob.glob(pattern)) + extensions = ("*.png", "*.jpg", "*.jpeg", "*.webp", "*.avif", "*.gif") + self.image_files = sorted( + file + for ext in extensions + for file in glob.glob(os.path.join(self.folder_path, ext), recursive=True) + ) self.total_files = len(self.image_files) self.image_files_md5 = { - file: self.compute_md5(file) for file in self.image_files + file: md5 + for file, md5 in zip( + self.image_files, self.compute_md5_parallel(self.image_files) + ) } self.tagger_processed.clear() for md5 in self.image_files_md5.values(): if self.tagger_cache[md5]: self.tagger_processed.add(md5) + # Clear the entire listbox to prepare for reloading new items self.listbox.delete(0, tk.END) self.uploaded.clear() self.upload_verified = 0 @@ -820,7 +718,8 @@ class ImageBrowser(tk.Tk): self.post_load_processing() else: messagebox.showinfo( - _("Informacja"), _("Brak plików PNG w wybranym folderze.") + _("Informacja"), + _("Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze."), ) def post_load_processing(self): @@ -972,17 +871,22 @@ class ImageBrowser(tk.Tk): open_webbrowser(url, self.settings) def compute_md5(self, file_path, chunk_size=8192): - """Oblicza MD5 dla danego pliku.""" + """Compute MD5 for a single file.""" hash_md5 = hashlib.md5() try: with open(file_path, "rb") as f: for chunk in iter(lambda: f.read(chunk_size), b""): hash_md5.update(chunk) except Exception as e: - print(_("Błąd przy obliczaniu MD5:"), e) + print(_("Error computing MD5:"), e) return "" return hash_md5.hexdigest() + def compute_md5_parallel(self, file_paths): + """Compute MD5 for multiple files in parallel.""" + with concurrent.futures.ThreadPoolExecutor() as executor: + return list(executor.map(self.compute_md5, file_paths)) + def on_listbox_select(self, event): """ Wywoływane po wybraniu pliku z listy. @@ -1019,9 +923,7 @@ class ImageBrowser(tk.Tk): file_path = self.image_files[index] try: img = Image.open(file_path) - parameters = "" - if isinstance(img, PngImagePlugin.PngImageFile): - parameters = img.info.get("parameters", "") + parameters = extract_parameters(img, file_path) self.current_image_original = img.copy() self.current_parameters = parameters self.update_display_image() diff --git a/kapitanbooru_uploader/ProcessingDialog.py b/kapitanbooru_uploader/ProcessingDialog.py new file mode 100644 index 0000000..d3b39b2 --- /dev/null +++ b/kapitanbooru_uploader/ProcessingDialog.py @@ -0,0 +1,119 @@ +from .I18N import _ + + +import inspect +import queue +import threading +import tkinter as tk +from tkinter import ttk + + +class ProcessingDialog: + def __init__(self, root, target_function, *args): + self.root = root + self.top = tk.Toplevel(root) + self.top.title(_("Processing...")) + self.top.geometry("300x150") + self.top.protocol("WM_DELETE_WINDOW", self.on_close) + + self.label = tk.Label(self.top, text=_("Processing, please wait...")) + self.label.pack(pady=10) + + # Start with indeterminate progress bar + self.progress = ttk.Progressbar(self.top, mode="indeterminate") + self.progress.pack(pady=10, fill="x") + self.progress.start(10) + + sig = inspect.signature(target_function) + if "secondary_progress_queue" in sig.parameters: + self.sub_progress = ttk.Progressbar(self.top, mode="indeterminate") + self.sub_progress.pack(pady=10, fill="x") + self.sub_progress.start(10) + + # Setup communication queue and periodic checker + self.queue = queue.Queue() + self.sub_queue = queue.Queue() + self.running = True + self.cancel_event = threading.Event() # Cancellation flag + self.thread = threading.Thread( + target=self.run_task, args=(target_function, *args) + ) + self.thread.start() + self.top.after(100, self.process_queue) + + def process_queue(self): + """Process messages from the background thread""" + while self.running: + try: + msg = self.queue.get_nowait() + + if msg[0] == "mode": + self.progress.config(mode=msg[1]) + if msg[1] == "determinate": + self.progress["value"] = 0 + self.progress.stop() + elif msg[1] == "indeterminate": + self.progress["value"] = 0 + self.progress.start() + elif msg[0] == "max": + self.progress["maximum"] = msg[1] + elif msg[0] == "progress": + self.progress["value"] = msg[1] + elif msg[0] == "label": + self.label.config(text=msg[1]) + + self.top.update_idletasks() + except queue.Empty: + break + + try: + msg = self.sub_queue.get_nowait() + + if msg[0] == "mode": + self.sub_progress.config(mode=msg[1]) + if msg[1] == "determinate": + self.sub_progress["value"] = 0 + self.sub_progress.stop() + elif msg[1] == "indeterminate": + self.sub_progress["value"] = 0 + self.sub_progress.start() + elif msg[0] == "max": + self.sub_progress["maximum"] = msg[1] + elif msg[0] == "progress": + self.sub_progress["value"] = msg[1] + + self.top.update_idletasks() + except queue.Empty: + break + + if self.running: + self.top.after(100, self.process_queue) + + def run_task(self, target_function, *args): + """Execute target function with progress queue if supported""" + try: + sig = inspect.signature(target_function) + kwargs = {} + if "progress_queue" in sig.parameters: + kwargs["progress_queue"] = self.queue + if "secondary_progress_queue" in sig.parameters: + kwargs["secondary_progress_queue"] = self.sub_queue + if "cancel_event" in sig.parameters: + kwargs["cancel_event"] = self.cancel_event + target_function(*args, **kwargs) + finally: + self.close_dialog() + + def close_dialog(self): + """Safely close the dialog""" + if self.running: + self.running = False + self.progress.stop() + self.top.after(0, self.top.destroy) + self.root.after(100, self.thread.join) + + def on_close(self): + """Handle manual window closure""" + self.running = False + self.cancel_event.set() # Notify target function that cancellation is requested + self.top.destroy() diff --git a/kapitanbooru_uploader/locales/en/LC_MESSAGES/messages.po b/kapitanbooru_uploader/locales/en/LC_MESSAGES/messages.po index 5dffcd0..383895c 100644 --- a/kapitanbooru_uploader/locales/en/LC_MESSAGES/messages.po +++ b/kapitanbooru_uploader/locales/en/LC_MESSAGES/messages.po @@ -1,8 +1,8 @@ msgid "" msgstr "" -"Project-Id-Version: Kapitanbooru Uploader 0.7.0\n" +"Project-Id-Version: Kapitanbooru Uploader 0.8.0\n" "Report-Msgid-Bugs-To: kapitan@mlesniak.pl\n" -"POT-Creation-Date: 2025-03-16 01:22+0100\n" +"POT-Creation-Date: 2025-03-27 20:47+0100\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -43,34 +43,26 @@ msgstr "Found auth_token:" msgid "auth_token not found in the HTML page." msgstr "auth_token not found in the HTML page." -#: ImageBrowser.py:33 -msgid "Processing..." -msgstr "Processing..." - -#: ImageBrowser.py:37 -msgid "Processing, please wait..." -msgstr "Processing, please wait..." - -#: ImageBrowser.py:248 +#: ImageBrowser.py:138 #, python-brace-format msgid "Update check failed: {error}" msgstr "Update check failed: {error}" -#: ImageBrowser.py:250 +#: ImageBrowser.py:140 #, python-brace-format msgid "Malformed pyproject.toml: {error}" msgstr "Malformed pyproject.toml: {error}" -#: ImageBrowser.py:253 +#: ImageBrowser.py:143 #, python-brace-format msgid "Unexpected error during update check: {error}" msgstr "Unexpected error during update check: {error}" -#: ImageBrowser.py:260 +#: ImageBrowser.py:150 msgid "Update Available" msgstr "Update Available" -#: ImageBrowser.py:262 +#: ImageBrowser.py:152 #, python-brace-format msgid "" "A new version {new_version} is available!\n" @@ -83,240 +75,241 @@ msgstr "" "\n" "Update using: {update_command}" -#: ImageBrowser.py:375 +#: ImageBrowser.py:263 msgid "Błąd przy otwieraniu pliku" msgstr "Error opening file" -#: ImageBrowser.py:417 +#: ImageBrowser.py:305 msgid "Tagger przetworzył:" msgstr "Tagger processed:" -#: ImageBrowser.py:420 +#: ImageBrowser.py:308 msgid "Błąd Taggera dla" msgstr "Tagger error for" -#: ImageBrowser.py:446 +#: ImageBrowser.py:334 msgid "Otwórz folder" msgstr "Open folder" -#: ImageBrowser.py:449 ImageBrowser.py:738 ImageBrowser.py:954 -#: ImageBrowser.py:962 +#: ImageBrowser.py:337 ImageBrowser.py:626 ImageBrowser.py:853 +#: ImageBrowser.py:861 msgid "Wyślij" msgstr "Send" -#: ImageBrowser.py:452 ImageBrowser.py:947 +#: ImageBrowser.py:340 ImageBrowser.py:846 msgid "Wyślij wszystko" msgstr "Send all" -#: ImageBrowser.py:456 ImageBrowser.py:948 ImageBrowser.py:954 +#: ImageBrowser.py:344 ImageBrowser.py:847 ImageBrowser.py:853 msgid "Podmień tagi" msgstr "Replace tags" -#: ImageBrowser.py:459 ImageBrowser.py:949 +#: ImageBrowser.py:347 ImageBrowser.py:848 msgid "Otwórz post" msgstr "Open post" -#: ImageBrowser.py:462 +#: ImageBrowser.py:350 msgid "Zakończ" msgstr "Finish" -#: ImageBrowser.py:464 +#: ImageBrowser.py:352 msgid "Plik" msgstr "File" -#: ImageBrowser.py:468 ImageBrowser.py:567 +#: ImageBrowser.py:356 ImageBrowser.py:455 msgid "Ustawienia" msgstr "Settings" -#: ImageBrowser.py:471 +#: ImageBrowser.py:359 msgid "Wyczyść cache Taggera" msgstr "Clear Tagger cache" -#: ImageBrowser.py:474 +#: ImageBrowser.py:362 msgid "Zregeneruj bazę tagów" msgstr "Regenerate tag database" -#: ImageBrowser.py:476 +#: ImageBrowser.py:364 msgid "Opcje" msgstr "Options" -#: ImageBrowser.py:479 +#: ImageBrowser.py:367 msgid "About" msgstr "About" -#: ImageBrowser.py:480 +#: ImageBrowser.py:368 msgid "Help" msgstr "Help" -#: ImageBrowser.py:485 +#: ImageBrowser.py:373 msgid "About Kapitanbooru Uploader" msgstr "About Kapitanbooru Uploader" -#: ImageBrowser.py:502 +#: ImageBrowser.py:390 #, python-brace-format msgid "A new version {new_version} is available!" msgstr "A new version {new_version} is available!" -#: ImageBrowser.py:509 +#: ImageBrowser.py:397 #, python-brace-format msgid "Current version: {version}" msgstr "Current version: {version}" -#: ImageBrowser.py:511 +#: ImageBrowser.py:399 msgid "A GUI application for uploading images to KapitanBooru." msgstr "A GUI application for uploading images to KapitanBooru." -#: ImageBrowser.py:512 +#: ImageBrowser.py:400 msgid "Features include image upload, tag management, automatic" msgstr "Features include image upload, tag management, automatic" -#: ImageBrowser.py:513 +#: ImageBrowser.py:401 msgid "tagging with wdtagger, and cache management." msgstr "tagging with wdtagger, and cache management." -#: ImageBrowser.py:515 +#: ImageBrowser.py:403 msgid "Authors:" msgstr "Authors:" -#: ImageBrowser.py:518 +#: ImageBrowser.py:406 msgid "License: MIT License" msgstr "License: MIT License" -#: ImageBrowser.py:531 +#: ImageBrowser.py:419 msgid "Repository:" msgstr "Repository:" -#: ImageBrowser.py:540 +#: ImageBrowser.py:428 msgid "Website:" msgstr "Website:" -#: ImageBrowser.py:551 +#: ImageBrowser.py:439 msgid "Close" msgstr "Close" -#: ImageBrowser.py:559 ImageBrowser.py:562 +#: ImageBrowser.py:447 ImageBrowser.py:450 msgid "Cache" msgstr "Cache" -#: ImageBrowser.py:559 +#: ImageBrowser.py:447 msgid "Cache Taggera zostało wyczyszczone." msgstr "Tagger cache has been cleared." -#: ImageBrowser.py:562 +#: ImageBrowser.py:450 msgid "Błąd przy czyszczeniu cache:" msgstr "Error clearing cache:" -#: ImageBrowser.py:572 +#: ImageBrowser.py:460 msgid "Login:" msgstr "Login:" -#: ImageBrowser.py:578 +#: ImageBrowser.py:466 msgid "Hasło:" msgstr "Password:" -#: ImageBrowser.py:584 +#: ImageBrowser.py:472 msgid "Base URL:" msgstr "Base URL:" -#: ImageBrowser.py:590 +#: ImageBrowser.py:478 msgid "Default Tags:" msgstr "Default Tags:" -#: ImageBrowser.py:596 +#: ImageBrowser.py:484 msgid "Browser:" msgstr "Browser:" -#: ImageBrowser.py:610 +#: ImageBrowser.py:498 msgid "Language:" msgstr "Language:" -#: ImageBrowser.py:642 +#: ImageBrowser.py:530 msgid "Zapisz" msgstr "Save" -#: ImageBrowser.py:684 +#: ImageBrowser.py:572 msgid "PNG Tags" msgstr "PNG Tags" -#: ImageBrowser.py:696 +#: ImageBrowser.py:584 msgid "Tagger Tags" msgstr "Tagger Tags" -#: ImageBrowser.py:710 +#: ImageBrowser.py:598 msgid "Manual Tags" msgstr "Manual Tags" -#: ImageBrowser.py:718 +#: ImageBrowser.py:606 msgid "Final Tags" msgstr "Final Tags" -#: ImageBrowser.py:743 +#: ImageBrowser.py:631 msgid "Wyświetl" msgstr "Display" -#: ImageBrowser.py:760 +#: ImageBrowser.py:648 msgid "Przetworzono tagi:" msgstr "Processed tags:" -#: ImageBrowser.py:760 ImageBrowser.py:761 ImageBrowser.py:762 +#: ImageBrowser.py:648 ImageBrowser.py:649 ImageBrowser.py:650 msgid "plików" msgstr "files" -#: ImageBrowser.py:761 +#: ImageBrowser.py:649 msgid "Zweryfikowano status uploadu:" msgstr "Upload status verified:" -#: ImageBrowser.py:762 +#: ImageBrowser.py:650 msgid "Zuploadowano:" msgstr "Uploaded:" -#: ImageBrowser.py:790 -msgid "Wybierz folder z obrazkami PNG" -msgstr "Select folder with PNG images" +#: ImageBrowser.py:679 +msgid "Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF" +msgstr "Select folder with PNG, JPEG, WebP, AVIF, and GIF images" -#: ImageBrowser.py:823 +#: ImageBrowser.py:721 msgid "Informacja" msgstr "Information" -#: ImageBrowser.py:823 -msgid "Brak plików PNG w wybranym folderze." +#: ImageBrowser.py:722 +#, fuzzy +msgid "Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze." msgstr "No PNG files in the selected folder." -#: ImageBrowser.py:906 +#: ImageBrowser.py:805 msgid "Błąd podczas sprawdzania paczki uploadu:" msgstr "Error while checking upload package:" -#: ImageBrowser.py:982 -msgid "Błąd przy obliczaniu MD5:" -msgstr "Error calculating MD5:" - -#: ImageBrowser.py:1038 +#: ImageBrowser.py:881 +#: ImageBrowser.py:881 +msgid "Error computing MD5:" +msgstr "Error computing MD5:" +#: ImageBrowser.py:940 msgid "Błąd" msgstr "Error" -#: ImageBrowser.py:1038 +#: ImageBrowser.py:940 msgid "Nie można załadować obrazka:" msgstr "Unable to load image:" -#: ImageBrowser.py:1248 ImageBrowser.py:1258 +#: ImageBrowser.py:1150 ImageBrowser.py:1160 #, python-brace-format msgid "Warning: Tag '{tag}' not found in implication graph" msgstr "Warning: Tag '{tag}' not found in implication graph" -#: ImageBrowser.py:1488 +#: ImageBrowser.py:1390 msgid "Tagger przetwarza..." msgstr "Tagger processing..." -#: ImageBrowser.py:1513 +#: ImageBrowser.py:1415 #, python-brace-format msgid "Wysyłam plik {base_file_name}..." msgstr "Sending file {base_file_name}..." -#: ImageBrowser.py:1554 +#: ImageBrowser.py:1456 msgid "Wysyłanie zakończone powodzeniem!" msgstr "Upload completed successfully!" -#: ImageBrowser.py:1558 ImageBrowser.py:1567 +#: ImageBrowser.py:1460 ImageBrowser.py:1469 #, python-brace-format msgid "" "Wysyłanie zakończone błędem.\n" @@ -327,40 +320,40 @@ msgstr "" "Status: {status_code}\n" "Content: {text}" -#: ImageBrowser.py:1573 ImageBrowser.py:1576 +#: ImageBrowser.py:1475 ImageBrowser.py:1478 msgid "Wysyłanie" msgstr "Uploading" -#: ImageBrowser.py:1589 +#: ImageBrowser.py:1491 msgid "Błąd wysyłania" msgstr "Upload error" -#: ImageBrowser.py:1609 +#: ImageBrowser.py:1511 msgid "Błąd edycji" msgstr "Edit error" -#: ImageBrowser.py:1609 +#: ImageBrowser.py:1511 msgid "Post nie został znaleziony dla tego pliku" msgstr "Post not found for this file" -#: ImageBrowser.py:1619 +#: ImageBrowser.py:1521 #, python-brace-format msgid "Aktualizuję tagi dla {base_file_name}..." msgstr "Updating tags for {base_file_name}..." -#: ImageBrowser.py:1629 ImageBrowser.py:1639 ImageBrowser.py:1670 +#: ImageBrowser.py:1531 ImageBrowser.py:1541 ImageBrowser.py:1572 msgid "Operacja anulowana" msgstr "Operation cancelled" -#: ImageBrowser.py:1680 +#: ImageBrowser.py:1582 msgid "Tagi zostały zaktualizowane!" msgstr "Tags have been updated!" -#: ImageBrowser.py:1682 +#: ImageBrowser.py:1584 msgid "Sukces edycji" msgstr "Edit successful" -#: ImageBrowser.py:1688 +#: ImageBrowser.py:1590 #, python-brace-format msgid "" "Błąd podczas aktualizacji tagów\n" @@ -369,19 +362,19 @@ msgstr "" "Error updating tags\n" "Status: {code}" -#: ImageBrowser.py:1692 +#: ImageBrowser.py:1594 msgid "Treść:" msgstr "Content:" -#: ImageBrowser.py:1696 +#: ImageBrowser.py:1598 msgid "Krytyczny błąd edycji" msgstr "Critical edit error" -#: ImageBrowser.py:1708 +#: ImageBrowser.py:1610 msgid "Potwierdzenie" msgstr "Confirmation" -#: ImageBrowser.py:1710 +#: ImageBrowser.py:1612 msgid "" "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" @@ -391,21 +384,29 @@ msgstr "" "Each will be tagged with 'meta:auto_upload'.\n" "Make sure the tags are correct!" -#: ImageBrowser.py:1734 +#: ImageBrowser.py:1636 msgid "Anulowano operację!" msgstr "Operation cancelled!" -#: ImageBrowser.py:1742 +#: ImageBrowser.py:1644 #, python-brace-format msgid "" "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" msgstr "" "Uploading {file_path} with tags: {final_tags} and rating: {final_rating}" -#: ImageBrowser.py:1758 +#: ImageBrowser.py:1660 msgid "Przesłano pliki!" msgstr "Files have been uploaded!" +#: ProcessingDialog.py:15 +msgid "Processing..." +msgstr "Processing..." + +#: ProcessingDialog.py:19 +msgid "Processing, please wait..." +msgstr "Processing, please wait..." + #: ProgressFile.py:16 msgid "Upload cancelled by user." msgstr "Upload cancelled by user." @@ -426,15 +427,19 @@ msgstr "Error fetching character tags:" msgid "Błąd przy pobieraniu tagów copyright:" msgstr "Error fetching copyright tags:" -#: tag_processing.py:48 +#: tag_processing.py:49 msgid "Błąd przy pobieraniu tagów artystów:" msgstr "Error fetching artist tags:" -#: tag_processing.py:200 +#: tag_processing.py:164 +#: tag_processing.py:164 +msgid "Nie można sparsować parametrów." +msgstr "Unable to parse parameters." +#: tag_processing.py:249 msgid "Błąd podczas odczytu tag_aliases:" msgstr "Error reading tag aliases:" -#: tag_processing.py:216 +#: tag_processing.py:265 msgid "Błąd podczas odczytu tags:" msgstr "Error reading tags:" @@ -490,52 +495,55 @@ msgstr "Tag processing cancelled." msgid "Pobrano {count} tagów..." msgstr "Fetched {count} tags..." -#: TagsRepo.py:271 +#: TagsRepo.py:301 msgid "Anulowano pobieranie aliasów tagów." msgstr "Tag alias fetching cancelled." -#: TagsRepo.py:280 +#: TagsRepo.py:310 #, python-brace-format msgid "Pobieranie aliasów tagów (od ID {last_id})..." msgstr "Fetching tag aliases (from ID {last_id})..." -#: TagsRepo.py:297 +#: TagsRepo.py:327 #, python-brace-format msgid "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" msgstr "Error fetching tag aliases from ID {last_id}: HTTP {code}" -#: TagsRepo.py:312 +#: TagsRepo.py:342 msgid "Anulowano przetwarzanie aliasów tagów." msgstr "Tag alias processing cancelled." -#: TagsRepo.py:334 +#: TagsRepo.py:364 #, python-brace-format msgid "Pobrano {count} aliasów tagów..." msgstr "Fetched {count} tag aliases..." -#: TagsRepo.py:375 +#: TagsRepo.py:405 msgid "Anulowano pobieranie implikacji tagów." msgstr "Tag implication fetching cancelled." -#: TagsRepo.py:384 +#: TagsRepo.py:414 #, python-brace-format msgid "Pobieranie implikacji tagów (od ID {last_id})..." msgstr "Fetching tag implications (from ID {last_id})..." -#: TagsRepo.py:398 +#: TagsRepo.py:428 #, python-brace-format msgid "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" msgstr "Error fetching tag implications from ID {last_id}: HTTP {code}" -#: TagsRepo.py:413 +#: TagsRepo.py:443 msgid "Anulowano przetwarzanie implikacji tagów." msgstr "Tag implication processing cancelled." -#: TagsRepo.py:460 +#: TagsRepo.py:490 #, python-brace-format msgid "Pobrano implikacje dla {count} tagów..." msgstr "Fetched implications for {count} tags..." -#: TagsRepo.py:487 +#: TagsRepo.py:517 msgid "Regeneracja bazy zakończona." -msgstr "Database regeneration complete." \ No newline at end of file +msgstr "Database regeneration complete." + +#~ msgid "Błąd przy obliczaniu MD5:" +#~ msgstr "Error calculating MD5:" \ No newline at end of file diff --git a/kapitanbooru_uploader/locales/pl/LC_MESSAGES/messages.po b/kapitanbooru_uploader/locales/pl/LC_MESSAGES/messages.po index a99817d..25b982a 100644 --- a/kapitanbooru_uploader/locales/pl/LC_MESSAGES/messages.po +++ b/kapitanbooru_uploader/locales/pl/LC_MESSAGES/messages.po @@ -1,8 +1,8 @@ msgid "" msgstr "" -"Project-Id-Version: Kapitanbooru Uploader 0.7.0\n" +"Project-Id-Version: Kapitanbooru Uploader 0.8.0\n" "Report-Msgid-Bugs-To: kapitan@mlesniak.pl\n" -"POT-Creation-Date: 2025-03-16 01:22+0100\n" +"POT-Creation-Date: 2025-03-27 20:47+0100\n" "Language: pl\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -45,34 +45,26 @@ msgstr "Znaleziono auth_token:" msgid "auth_token not found in the HTML page." msgstr "Nie znaleziono auth_token w stronie HTML." -#: ImageBrowser.py:33 -msgid "Processing..." -msgstr "Przetwarzanie..." - -#: ImageBrowser.py:37 -msgid "Processing, please wait..." -msgstr "Przetwarzanie, proszę czekać..." - -#: ImageBrowser.py:248 +#: ImageBrowser.py:138 #, python-brace-format msgid "Update check failed: {error}" msgstr "Sprawdzenie aktualizacji nie powiodło się: {error}" -#: ImageBrowser.py:250 +#: ImageBrowser.py:140 #, python-brace-format msgid "Malformed pyproject.toml: {error}" msgstr "Nieprawidłowy plik pyproject.toml: {error}" -#: ImageBrowser.py:253 +#: ImageBrowser.py:143 #, python-brace-format msgid "Unexpected error during update check: {error}" msgstr "Nieoczekiwany błąd podczas sprawdzania aktualizacji: {error}" -#: ImageBrowser.py:260 +#: ImageBrowser.py:150 msgid "Update Available" msgstr "Aktualizacja dostępna" -#: ImageBrowser.py:262 +#: ImageBrowser.py:152 #, python-brace-format msgid "" "A new version {new_version} is available!\n" @@ -85,240 +77,242 @@ msgstr "" "\n" "Aktualizuj za pomocą: {update_command}" -#: ImageBrowser.py:375 +#: ImageBrowser.py:263 msgid "Błąd przy otwieraniu pliku" msgstr "Błąd przy otwieraniu pliku" -#: ImageBrowser.py:417 +#: ImageBrowser.py:305 msgid "Tagger przetworzył:" msgstr "Tagger przetworzył:" -#: ImageBrowser.py:420 +#: ImageBrowser.py:308 msgid "Błąd Taggera dla" msgstr "Błąd Taggera dla" -#: ImageBrowser.py:446 +#: ImageBrowser.py:334 msgid "Otwórz folder" msgstr "Otwórz folder" -#: ImageBrowser.py:449 ImageBrowser.py:738 ImageBrowser.py:954 -#: ImageBrowser.py:962 +#: ImageBrowser.py:337 ImageBrowser.py:626 ImageBrowser.py:853 +#: ImageBrowser.py:861 msgid "Wyślij" msgstr "Wyślij" -#: ImageBrowser.py:452 ImageBrowser.py:947 +#: ImageBrowser.py:340 ImageBrowser.py:846 msgid "Wyślij wszystko" msgstr "Wyślij wszystko" -#: ImageBrowser.py:456 ImageBrowser.py:948 ImageBrowser.py:954 +#: ImageBrowser.py:344 ImageBrowser.py:847 ImageBrowser.py:853 msgid "Podmień tagi" msgstr "Podmień tagi" -#: ImageBrowser.py:459 ImageBrowser.py:949 +#: ImageBrowser.py:347 ImageBrowser.py:848 msgid "Otwórz post" msgstr "Otwórz post" -#: ImageBrowser.py:462 +#: ImageBrowser.py:350 msgid "Zakończ" msgstr "Zakończ" -#: ImageBrowser.py:464 +#: ImageBrowser.py:352 msgid "Plik" msgstr "Plik" -#: ImageBrowser.py:468 ImageBrowser.py:567 +#: ImageBrowser.py:356 ImageBrowser.py:455 msgid "Ustawienia" msgstr "Ustawienia" -#: ImageBrowser.py:471 +#: ImageBrowser.py:359 msgid "Wyczyść cache Taggera" msgstr "Wyczyść cache Taggera" -#: ImageBrowser.py:474 +#: ImageBrowser.py:362 msgid "Zregeneruj bazę tagów" msgstr "Zregeneruj bazę tagów" -#: ImageBrowser.py:476 +#: ImageBrowser.py:364 msgid "Opcje" msgstr "Opcje" -#: ImageBrowser.py:479 +#: ImageBrowser.py:367 msgid "About" msgstr "O programie" -#: ImageBrowser.py:480 +#: ImageBrowser.py:368 msgid "Help" msgstr "Pomoc" -#: ImageBrowser.py:485 +#: ImageBrowser.py:373 msgid "About Kapitanbooru Uploader" msgstr "O programie Kapitanbooru Uploader" -#: ImageBrowser.py:502 +#: ImageBrowser.py:390 #, python-brace-format msgid "A new version {new_version} is available!" msgstr "Dostępna jest nowa wersja {new_version}!" -#: ImageBrowser.py:509 +#: ImageBrowser.py:397 #, python-brace-format msgid "Current version: {version}" msgstr "Obecna wersja: {version}" -#: ImageBrowser.py:511 +#: ImageBrowser.py:399 msgid "A GUI application for uploading images to KapitanBooru." msgstr "Aplikacja GUI do przesyłania obrazów do KapitanBooru." -#: ImageBrowser.py:512 +#: ImageBrowser.py:400 msgid "Features include image upload, tag management, automatic" msgstr "Funkcje obejmują przesyłanie obrazów, zarządzanie tagami, automatyczne" -#: ImageBrowser.py:513 +#: ImageBrowser.py:401 msgid "tagging with wdtagger, and cache management." msgstr "tagowanie za pomocą wdtagger oraz zarządzanie cache." -#: ImageBrowser.py:515 +#: ImageBrowser.py:403 msgid "Authors:" msgstr "Autorzy:" -#: ImageBrowser.py:518 +#: ImageBrowser.py:406 msgid "License: MIT License" msgstr "Licencja: MIT License" -#: ImageBrowser.py:531 +#: ImageBrowser.py:419 msgid "Repository:" msgstr "Repozytorium:" -#: ImageBrowser.py:540 +#: ImageBrowser.py:428 msgid "Website:" msgstr "Strona internetowa:" -#: ImageBrowser.py:551 +#: ImageBrowser.py:439 msgid "Close" msgstr "Zamknij" -#: ImageBrowser.py:559 ImageBrowser.py:562 +#: ImageBrowser.py:447 ImageBrowser.py:450 msgid "Cache" msgstr "Cache" -#: ImageBrowser.py:559 +#: ImageBrowser.py:447 msgid "Cache Taggera zostało wyczyszczone." msgstr "Cache Taggera zostało wyczyszczone." -#: ImageBrowser.py:562 +#: ImageBrowser.py:450 msgid "Błąd przy czyszczeniu cache:" msgstr "Błąd przy czyszczeniu cache:" -#: ImageBrowser.py:572 +#: ImageBrowser.py:460 msgid "Login:" msgstr "Login:" -#: ImageBrowser.py:578 +#: ImageBrowser.py:466 msgid "Hasło:" msgstr "Hasło:" -#: ImageBrowser.py:584 +#: ImageBrowser.py:472 msgid "Base URL:" msgstr "Base URL:" -#: ImageBrowser.py:590 +#: ImageBrowser.py:478 msgid "Default Tags:" msgstr "Domyślne tagi:" -#: ImageBrowser.py:596 +#: ImageBrowser.py:484 msgid "Browser:" msgstr "Przeglądarka:" -#: ImageBrowser.py:610 +#: ImageBrowser.py:498 msgid "Language:" msgstr "Język:" -#: ImageBrowser.py:642 +#: ImageBrowser.py:530 msgid "Zapisz" msgstr "Zapisz" -#: ImageBrowser.py:684 +#: ImageBrowser.py:572 msgid "PNG Tags" msgstr "Tagi PNG" -#: ImageBrowser.py:696 +#: ImageBrowser.py:584 msgid "Tagger Tags" msgstr "Tagi Taggera" -#: ImageBrowser.py:710 +#: ImageBrowser.py:598 msgid "Manual Tags" msgstr "Tagi ręczne" -#: ImageBrowser.py:718 +#: ImageBrowser.py:606 msgid "Final Tags" msgstr "Ostateczne tagi" -#: ImageBrowser.py:743 +#: ImageBrowser.py:631 msgid "Wyświetl" msgstr "Wyświetl" -#: ImageBrowser.py:760 +#: ImageBrowser.py:648 msgid "Przetworzono tagi:" msgstr "Przetworzono tagi:" -#: ImageBrowser.py:760 ImageBrowser.py:761 ImageBrowser.py:762 +#: ImageBrowser.py:648 ImageBrowser.py:649 ImageBrowser.py:650 msgid "plików" msgstr "plików" -#: ImageBrowser.py:761 +#: ImageBrowser.py:649 msgid "Zweryfikowano status uploadu:" msgstr "Zweryfikowano status uploadu:" -#: ImageBrowser.py:762 +#: ImageBrowser.py:650 msgid "Zuploadowano:" msgstr "Zuploadowano:" -#: ImageBrowser.py:790 -msgid "Wybierz folder z obrazkami PNG" +#: ImageBrowser.py:679 +#, fuzzy +msgid "Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF" msgstr "Wybierz folder z obrazami PNG" -#: ImageBrowser.py:823 +#: ImageBrowser.py:721 msgid "Informacja" msgstr "Informacja" -#: ImageBrowser.py:823 -msgid "Brak plików PNG w wybranym folderze." +#: ImageBrowser.py:722 +#, fuzzy +msgid "Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze." msgstr "Brak plików PNG w wybranym folderze." -#: ImageBrowser.py:906 +#: ImageBrowser.py:805 msgid "Błąd podczas sprawdzania paczki uploadu:" msgstr "Błąd podczas sprawdzania paczki uploadu:" -#: ImageBrowser.py:982 -msgid "Błąd przy obliczaniu MD5:" +#: ImageBrowser.py:881 +msgid "Error computing MD5:" msgstr "Błąd przy obliczaniu MD5:" -#: ImageBrowser.py:1038 +#: ImageBrowser.py:940 msgid "Błąd" msgstr "Błąd" -#: ImageBrowser.py:1038 +#: ImageBrowser.py:940 msgid "Nie można załadować obrazka:" msgstr "Nie można załadować obrazka:" -#: ImageBrowser.py:1248 ImageBrowser.py:1258 +#: ImageBrowser.py:1150 ImageBrowser.py:1160 #, python-brace-format msgid "Warning: Tag '{tag}' not found in implication graph" msgstr "Ostrzeżenie: Tag '{tag}' nie został znaleziony w grafie implikacji" -#: ImageBrowser.py:1488 +#: ImageBrowser.py:1390 msgid "Tagger przetwarza..." msgstr "Tagger przetwarza..." -#: ImageBrowser.py:1513 +#: ImageBrowser.py:1415 #, python-brace-format msgid "Wysyłam plik {base_file_name}..." msgstr "Wysyłam plik {base_file_name}..." -#: ImageBrowser.py:1554 +#: ImageBrowser.py:1456 msgid "Wysyłanie zakończone powodzeniem!" msgstr "Wysyłanie zakończone powodzeniem!" -#: ImageBrowser.py:1558 ImageBrowser.py:1567 +#: ImageBrowser.py:1460 ImageBrowser.py:1469 #, python-brace-format msgid "" "Wysyłanie zakończone błędem.\n" @@ -329,40 +323,40 @@ msgstr "" "Status: {status_code}\n" "Treść: {text}" -#: ImageBrowser.py:1573 ImageBrowser.py:1576 +#: ImageBrowser.py:1475 ImageBrowser.py:1478 msgid "Wysyłanie" msgstr "Wysyłanie" -#: ImageBrowser.py:1589 +#: ImageBrowser.py:1491 msgid "Błąd wysyłania" msgstr "Błąd wysyłania" -#: ImageBrowser.py:1609 +#: ImageBrowser.py:1511 msgid "Błąd edycji" msgstr "Błąd edycji" -#: ImageBrowser.py:1609 +#: ImageBrowser.py:1511 msgid "Post nie został znaleziony dla tego pliku" msgstr "Post nie został znaleziony dla tego pliku" -#: ImageBrowser.py:1619 +#: ImageBrowser.py:1521 #, python-brace-format msgid "Aktualizuję tagi dla {base_file_name}..." msgstr "Aktualizuję tagi dla {base_file_name}..." -#: ImageBrowser.py:1629 ImageBrowser.py:1639 ImageBrowser.py:1670 +#: ImageBrowser.py:1531 ImageBrowser.py:1541 ImageBrowser.py:1572 msgid "Operacja anulowana" msgstr "Operacja anulowana" -#: ImageBrowser.py:1680 +#: ImageBrowser.py:1582 msgid "Tagi zostały zaktualizowane!" msgstr "Tagi zostały zaktualizowane!" -#: ImageBrowser.py:1682 +#: ImageBrowser.py:1584 msgid "Sukces edycji" msgstr "Sukces edycji" -#: ImageBrowser.py:1688 +#: ImageBrowser.py:1590 #, python-brace-format msgid "" "Błąd podczas aktualizacji tagów\n" @@ -371,19 +365,19 @@ msgstr "" "Błąd podczas aktualizacji tagów\n" "Status: {code}" -#: ImageBrowser.py:1692 +#: ImageBrowser.py:1594 msgid "Treść:" msgstr "Treść:" -#: ImageBrowser.py:1696 +#: ImageBrowser.py:1598 msgid "Krytyczny błąd edycji" msgstr "Krytyczny błąd edycji" -#: ImageBrowser.py:1708 +#: ImageBrowser.py:1610 msgid "Potwierdzenie" msgstr "Potwierdzenie" -#: ImageBrowser.py:1710 +#: ImageBrowser.py:1612 msgid "" "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" @@ -393,21 +387,29 @@ msgstr "" "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" "Upewnij się, że tagi są poprawne!" -#: ImageBrowser.py:1734 +#: ImageBrowser.py:1636 msgid "Anulowano operację!" msgstr "Operacja anulowana!" -#: ImageBrowser.py:1742 +#: ImageBrowser.py:1644 #, python-brace-format msgid "" "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" msgstr "" "Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}" -#: ImageBrowser.py:1758 +#: ImageBrowser.py:1660 msgid "Przesłano pliki!" msgstr "Pliki zostały przesłane!" +#: ProcessingDialog.py:15 +msgid "Processing..." +msgstr "Przetwarzanie..." + +#: ProcessingDialog.py:19 +msgid "Processing, please wait..." +msgstr "Przetwarzanie, proszę czekać..." + #: ProgressFile.py:16 msgid "Upload cancelled by user." msgstr "Przesyłanie anulowane przez użytkownika." @@ -428,15 +430,20 @@ msgstr "Błąd przy pobieraniu tagów postaci:" msgid "Błąd przy pobieraniu tagów copyright:" msgstr "Błąd przy pobieraniu tagów copyright:" -#: tag_processing.py:48 +#: tag_processing.py:49 msgid "Błąd przy pobieraniu tagów artystów:" msgstr "Błąd przy pobieraniu tagów artystów:" -#: tag_processing.py:200 +#: tag_processing.py:164 +#, fuzzy +msgid "Nie można sparsować parametrów." +msgstr "Nie można załadować obrazka:" + +#: tag_processing.py:249 msgid "Błąd podczas odczytu tag_aliases:" msgstr "Błąd podczas odczytu tag_aliases:" -#: tag_processing.py:216 +#: tag_processing.py:265 msgid "Błąd podczas odczytu tags:" msgstr "Błąd podczas odczytu tags:" @@ -492,52 +499,55 @@ msgstr "Przetwarzanie tagów anulowane." msgid "Pobrano {count} tagów..." msgstr "Pobrano {count} tagów..." -#: TagsRepo.py:271 +#: TagsRepo.py:301 msgid "Anulowano pobieranie aliasów tagów." msgstr "Pobieranie aliasów tagów anulowane." -#: TagsRepo.py:280 +#: TagsRepo.py:310 #, python-brace-format msgid "Pobieranie aliasów tagów (od ID {last_id})..." msgstr "Pobieranie aliasów tagów (od ID {last_id})..." -#: TagsRepo.py:297 +#: TagsRepo.py:327 #, python-brace-format msgid "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" msgstr "Błąd przy pobieraniu aliasów tagów od ID {last_id}: HTTP {code}" -#: TagsRepo.py:312 +#: TagsRepo.py:342 msgid "Anulowano przetwarzanie aliasów tagów." msgstr "Przetwarzanie aliasów tagów anulowane." -#: TagsRepo.py:334 +#: TagsRepo.py:364 #, python-brace-format msgid "Pobrano {count} aliasów tagów..." msgstr "Pobrano {count} aliasów tagów..." -#: TagsRepo.py:375 +#: TagsRepo.py:405 msgid "Anulowano pobieranie implikacji tagów." msgstr "Pobieranie implikacji tagów anulowane." -#: TagsRepo.py:384 +#: TagsRepo.py:414 #, python-brace-format msgid "Pobieranie implikacji tagów (od ID {last_id})..." msgstr "Pobieranie implikacji tagów (od ID {last_id})..." -#: TagsRepo.py:398 +#: TagsRepo.py:428 #, python-brace-format msgid "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" msgstr "Błąd przy pobieraniu implikacji tagów od ID {last_id}: HTTP {code}" -#: TagsRepo.py:413 +#: TagsRepo.py:443 msgid "Anulowano przetwarzanie implikacji tagów." msgstr "Przetwarzanie implikacji tagów anulowane." -#: TagsRepo.py:460 +#: TagsRepo.py:490 #, python-brace-format msgid "Pobrano implikacje dla {count} tagów..." msgstr "Pobrano implikacje dla {count} tagów..." -#: TagsRepo.py:487 +#: TagsRepo.py:517 msgid "Regeneracja bazy zakończona." -msgstr "Regeneracja bazy zakończona." \ No newline at end of file +msgstr "Regeneracja bazy zakończona." + +#~ msgid "Błąd przy obliczaniu MD5:" +#~ msgstr "Błąd przy obliczaniu MD5:" \ No newline at end of file diff --git a/kapitanbooru_uploader/tag_processing.py b/kapitanbooru_uploader/tag_processing.py index cca62a4..6f48ad4 100644 --- a/kapitanbooru_uploader/tag_processing.py +++ b/kapitanbooru_uploader/tag_processing.py @@ -34,6 +34,7 @@ def get_copyright_tags(tags_repo: TagsRepo): print(_("Błąd przy pobieraniu tagów copyright:"), e) return set() + @lru_cache(maxsize=1) def get_artist_tags(tags_repo: TagsRepo): """Zwraca zbiór nazw tagów z kategorii Artist (kategoria = 1) z bazy tags.sqlite""" @@ -50,6 +51,10 @@ def get_artist_tags(tags_repo: TagsRepo): # Wzorce i ustawienia związane z tagami +PARAMETERS_PATTERN = re.compile( + r"^(?P.*?)\s+(?:Negative prompt:\s+(?P.*?)\s+)?Steps:\s+(?P\d+),\s+Sampler:\s+(?P.*?),\s+Schedule type:\s+(?P.*?),\s+CFG scale:\s+(?P.*?),\s+Seed:\s+(?P.*?),\s+Size:\s+(?P.*?),\s+Model hash:\s+(?P.*?),\s+Model:\s+(?P.*?),\s+(?:Denoising strength:\s+(?P.*?),\s+)?(?:Clip skip: (?P.*?),\s+)?(?:Hires CFG Scale:\s+(?P.*?),\s+Hires upscale:\s+(?P.*?),\s+Hires steps:\s+(?P.*?),\s+Hires upscaler:\s+(?P.*?),\s+)?Version:\s+(?P.*?)\s*$", + re.DOTALL, +) COEFFICIENT_PATTERN = re.compile(r"^.*?(:\d+|\d+\.\d+)$") UNESCAPED_PATTERN = re.compile(r"(? str: + """Funkcja do ekstrakcji parametrów z pliku graficznego. + Obsługuje PNG, JPEG, WebP, AVIF i GIF. + """ + parameters = "" + lower_path = file_path.lower() + + # For PNG: use the custom "parameters" field. + if isinstance(img, PngImagePlugin.PngImageFile): + parameters = img.info.get("parameters", "") + + # For JPEG, WebP, and AVIF: extract EXIF UserComment (EXIF tag 37510). + elif lower_path.endswith((".jpg", ".jpeg", ".webp", ".avif")): + exif_data = img.getexif() + user_comment = exif_data.get(37510) + if user_comment: + # UserComment might be stored as bytes; decode to string. + if isinstance(user_comment, bytes): + try: + parameters = user_comment.decode("utf-8", errors="replace") + except Exception: + parameters = str(user_comment) + else: + parameters = str(user_comment) + + # For GIF: extract the GIF comment. + elif lower_path.endswith(".gif"): + comment = img.info.get("comment") + if comment: + if isinstance(comment, bytes): + try: + parameters = comment.decode("utf-8", errors="replace") + except Exception: + parameters = str(comment) + else: + parameters = str(comment) + + return parameters + + +def parse_parameters(param_str): """ Funkcja do parsowania zawartości pola 'parameters' z pliku PNG. """ - tags = ( - param_str.split("\nNegative", 1)[0] - .removesuffix(",") - .replace("\n", " ") - .split(",") - ) + match = PARAMETERS_PATTERN.match(param_str) + if not match: + print(_("Nie można sparsować parametrów.")) + return "" + tags = match.group("prompt").removesuffix(",").replace("\n", " ").split(",") + + if "pony" in match.group("model"): + tags = [tag.strip() for tag in tags if not tag.strip().startswith("score_")] + tags = tags[1:] tags = set([WHITESPACE_PATTERN.sub("_", param.strip()) for param in tags]) tags = set( [ @@ -141,9 +192,9 @@ def parse_parameters(param_str, tags_repo: TagsRepo): # Usuń tagi tekstowe tags = {tag for tag in tags if not tag.startswith("text:")} # Pobierz tagi z bazy - character_tags = get_character_tags(tags_repo) - copyright_tags = get_copyright_tags(tags_repo) - artist_tags = get_artist_tags(tags_repo) + character_tags = get_character_tags() + copyright_tags = get_copyright_tags() + artist_tags = get_artist_tags() # Dla tagów należących do kategorii Character for tag in list(tags): # iterujemy po kopii zbioru diff --git a/pyproject.toml b/pyproject.toml index def2ba3..7d0c353 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "kapitanbooru-uploader" -version = "0.7.0" +version = "0.8.0" description = "A GUI application for uploading images to KapitanBooru" authors = [{ name = "Michał Leśniak", email = "kapitan@mlesniak.pl" }] dependencies = [