Bump version to 0.8.0 and add support for all image formats
All checks were successful
Gitea/kapitanbooru-uploader/pipeline/head This commit looks good

This commit is contained in:
Michał Leśniak 2025-03-27 21:11:36 +01:00
parent dcd8e0824f
commit b1b8ed237e
6 changed files with 450 additions and 360 deletions

View File

@ -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()

View File

@ -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()

View File

@ -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."
#~ msgid "Błąd przy obliczaniu MD5:"
#~ msgstr "Error calculating MD5:"

View File

@ -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."
#~ msgid "Błąd przy obliczaniu MD5:"
#~ msgstr "Błąd przy obliczaniu MD5:"

View File

@ -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<prompt>.*?)\s+(?:Negative prompt:\s+(?P<negative_prompt>.*?)\s+)?Steps:\s+(?P<steps>\d+),\s+Sampler:\s+(?P<sampler>.*?),\s+Schedule type:\s+(?P<schedule_type>.*?),\s+CFG scale:\s+(?P<cfg_scale>.*?),\s+Seed:\s+(?P<seed>.*?),\s+Size:\s+(?P<size>.*?),\s+Model hash:\s+(?P<model_hash>.*?),\s+Model:\s+(?P<model>.*?),\s+(?:Denoising strength:\s+(?P<denoising_strength>.*?),\s+)?(?:Clip skip: (?P<clip_skip>.*?),\s+)?(?:Hires CFG Scale:\s+(?P<hires_cfg_scale>.*?),\s+Hires upscale:\s+(?P<hires_upscale>.*?),\s+Hires steps:\s+(?P<hires_steps>.*?),\s+Hires upscaler:\s+(?P<hires_upscaler>.*?),\s+)?Version:\s+(?P<version>.*?)\s*$",
re.DOTALL,
)
COEFFICIENT_PATTERN = re.compile(r"^.*?(:\d+|\d+\.\d+)$")
UNESCAPED_PATTERN = re.compile(r"(?<!\\)[\(\)\[\]]+")
SLASH_PATTERN = re.compile(r"\\+(?=[\(\)](?!\^))")
@ -107,16 +112,62 @@ MULTI_TAG_FIXES = {
}
def parse_parameters(param_str, tags_repo: TagsRepo):
from PIL import Image, PngImagePlugin
def extract_parameters(img: Image, file_path: str) -> 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

View File

@ -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 = [