Translations, suggestion box, UX
All checks were successful
Gitea/kapitanbooru-uploader/pipeline/head This commit looks good

This commit is contained in:
2025-03-03 00:47:38 +01:00
parent 9f187afc22
commit 9361bc0363
17 changed files with 1515 additions and 135 deletions

View File

@@ -11,8 +11,9 @@ from typing import Dict, Tuple, Optional
import networkx as nx
import requests
from PIL import Image, ImageTk, PngImagePlugin
import wdtagger as wdt
from .I18N import _
from .ProgressFile import ProgressFile
from .TagsRepo import TagsRepo
from .autocomplete import TagManager
@@ -26,11 +27,11 @@ class ProcessingDialog:
def __init__(self, root, target_function, *args):
self.root = root
self.top = tk.Toplevel(root)
self.top.title("Processing...")
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 = tk.Label(self.top, text=_("Processing, please wait..."))
self.label.pack(pady=10)
# Start with indeterminate progress bar
@@ -38,8 +39,15 @@ class ProcessingDialog:
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(
@@ -58,6 +66,10 @@ class ProcessingDialog:
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":
@@ -69,6 +81,26 @@ class ProcessingDialog:
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)
@@ -79,6 +111,8 @@ class ProcessingDialog:
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)
@@ -91,7 +125,7 @@ class ProcessingDialog:
self.running = False
self.progress.stop()
self.top.after(0, self.top.destroy)
self.thread.join()
self.root.after(100, self.thread.join)
def on_close(self):
"""Handle manual window closure"""
@@ -105,6 +139,7 @@ class ImageBrowser(tk.Tk):
super().__init__()
self.title("Kapitanbooru Uploader")
self.geometry("900x600")
self.version = "0.4.0"
self.settings = Settings()
self.tags_repo = TagsRepo(self.settings)
@@ -169,6 +204,15 @@ class ImageBrowser(tk.Tk):
self.create_widgets()
self.bind_events()
def reload_ui(self):
"""Reload UI components with new language"""
# Destroy current widgets
for widget in self.winfo_children():
widget.destroy()
# Rebuild UI
self.create_menu()
self.create_widgets()
def load_implication_graph(self) -> nx.DiGraph:
G = nx.DiGraph()
conn = self.tags_repo.get_conn()
@@ -249,7 +293,7 @@ class ImageBrowser(tk.Tk):
png_tags = set(parse_parameters(parameters, self.tags_repo).split())
img.close()
except Exception as e:
print("Błąd przy otwieraniu pliku", file_path, ":", e)
print(_("Błąd przy otwieraniu pliku"), file_path, ":", e)
png_tags = set()
# Pobierz tagi z Taggera sprawdzając cache
@@ -291,10 +335,10 @@ class ImageBrowser(tk.Tk):
self.tagger_cache[md5] = result
self.tagger_processed.add(md5)
self.after(0, self.update_status_bar)
print(f"Tagger przetworzył: {file_path}")
print(_("Tagger przetworzył:"), f"{file_path}")
return result
except Exception as e:
print("Błąd Taggera dla", file_path, ":", e)
print(_("Błąd Taggera dla"), file_path, ":", e)
def map_tagger_rating(self, result: wdt.Result) -> str:
"""
@@ -320,37 +364,98 @@ class ImageBrowser(tk.Tk):
self.file_menu = tk.Menu(menubar, tearoff=0)
# File menu items - create references for items we need to control
self.file_menu.add_command(label="Otwórz folder", command=self.select_folder)
self.file_menu.add_command(label=_("Otwórz folder"), command=self.select_folder)
self.file_menu.add_separator()
self.file_menu.add_command(
label="Wyślij", command=self.upload_current_image, state=tk.DISABLED
label=_("Wyślij"), command=self.upload_current_image, state=tk.DISABLED
)
self.file_menu.add_command(
label="Wyślij wszystko", command=self.upload_all_files, state=tk.DISABLED
label=_("Wyślij wszystko"), command=self.upload_all_files, state=tk.DISABLED
)
self.file_menu.add_separator()
self.file_menu.add_command(
label="Podmień tagi", command=self.edit_current_image, state=tk.DISABLED
label=_("Podmień tagi"), command=self.edit_current_image, state=tk.DISABLED
)
self.file_menu.add_command(
label="Otwórz post", command=self.view_current_post, state=tk.DISABLED
label=_("Otwórz post"), command=self.view_current_post, state=tk.DISABLED
)
self.file_menu.add_separator()
self.file_menu.add_command(label="Zakończ", command=self.quit)
self.file_menu.add_command(label=_("Zakończ"), command=self.quit)
menubar.add_cascade(label="Plik", menu=self.file_menu)
menubar.add_cascade(label=_("Plik"), menu=self.file_menu)
# Options menu
options_menu = tk.Menu(menubar, tearoff=0)
options_menu.add_command(label="Ustawienia", command=self.open_settings)
options_menu.add_command(label=_("Ustawienia"), command=self.open_settings)
options_menu.add_separator()
options_menu.add_command(
label="Wyczyść cache Taggera", command=self.clear_cache
label=_("Wyczyść cache Taggera"), command=self.clear_cache
)
options_menu.add_command(
label="Zregeneruj bazę tagów", command=self.regenerate_tags_db
label=_("Zregeneruj bazę tagów"), command=self.regenerate_tags_db
)
menubar.add_cascade(label="Opcje", menu=options_menu)
menubar.add_cascade(label=_("Opcje"), menu=options_menu)
help_menu = tk.Menu(menubar, tearoff=0)
help_menu.add_command(label=_("About"), command=self.show_about)
menubar.add_cascade(label=_("Help"), menu=help_menu)
def show_about(self):
"""Multilingual About window (updated version)"""
about = tk.Toplevel(self)
about.title(_("About Kapitanbooru Uploader"))
about.resizable(False, False)
# Window content
frame = ttk.Frame(about, padding=10)
frame.pack(fill="both", expand=True)
ttk.Label(
frame, text="Kapitanbooru Uploader", font=("TkDefaultFont", 16, "bold")
).grid(row=0, column=0, sticky=tk.W)
content = [
(f"Version {self.version}", 1),
("", 2),
(_("A GUI application for uploading images to KapitanBooru."), 3),
(_("Features include image upload, tag management, automatic"), 4),
(_("tagging with wdtagger, and cache management."), 5),
("", 6),
(_("Authors:"), 7),
("Michał Leśniak", 8),
("", 9),
(_("License: MIT License"), 10),
(f"Copyright © 2025 Michał Leśniak", 11),
("", 12),
]
for text, row in content:
ttk.Label(frame, text=text).grid(row=row, column=0, sticky=tk.W)
# Repository link
repo_frame = ttk.Frame(frame)
repo_frame.grid(row=13, sticky=tk.W)
ttk.Label(repo_frame, text=_("Repository:")).pack(side=tk.LEFT)
repo_url = "https://git.mlesniak.pl/kapitan/kapitanbooru-uploader"
repo = ttk.Label(repo_frame, text=repo_url, cursor="hand2", foreground="blue")
repo.pack(side=tk.LEFT, padx=(5, 0))
repo.bind("<Button-1>", lambda e: open_webbrowser(repo_url, self.settings))
# Website link
website_frame = ttk.Frame(frame)
website_frame.grid(row=14, sticky=tk.W)
ttk.Label(website_frame, text=_("Website:")).pack(side=tk.LEFT)
website_url = "https://booru.mlesniak.pl"
website = ttk.Label(
website_frame, text=website_url, cursor="hand2", foreground="blue"
)
website.pack(side=tk.LEFT, padx=(5, 0))
website.bind(
"<Button-1>", lambda e: open_webbrowser(website_url, self.settings)
)
# Close button
ttk.Button(about, text=_("Close"), command=about.destroy).pack(pady=10)
def regenerate_tags_db(self):
self.processing_dialog = ProcessingDialog(self, self.tags_repo.regenerate_db)
@@ -358,41 +463,44 @@ class ImageBrowser(tk.Tk):
def clear_cache(self):
res, err = self.tagger_cache.clear_cache()
if res:
messagebox.showinfo("Cache", "Cache Taggera zostało wyczyszczone.")
messagebox.showinfo(_("Cache"), _("Cache Taggera zostało wyczyszczone."))
else:
messagebox.showerror("Cache", f"Błąd przy czyszczeniu cache: {err}")
messagebox.showerror(
_("Cache"), f"{_('Błąd przy czyszczeniu cache:')} {err}"
)
def open_settings(self):
settings_window = tk.Toplevel(self)
settings_window.title("Ustawienia")
settings_window.geometry("300x350")
settings_window.title(_("Ustawienia"))
settings_window.geometry("300x430") # Enlarged vertically
settings_window.resizable(False, False) # Disable resizing
settings_window.grab_set()
lbl_login = tk.Label(settings_window, text="Login:")
lbl_login = tk.Label(settings_window, text=_("Login:"))
lbl_login.pack(pady=(10, 0))
entry_login = tk.Entry(settings_window)
entry_login.pack(pady=(0, 10), padx=10, fill="x")
entry_login.insert(0, self.settings.username)
lbl_password = tk.Label(settings_window, text="Hasło:")
lbl_password = tk.Label(settings_window, text=_("Hasło:"))
lbl_password.pack(pady=(10, 0))
entry_password = tk.Entry(settings_window, show="*")
entry_password.pack(pady=(0, 10), padx=10, fill="x")
entry_password.insert(0, self.settings.password)
lbl_base_url = tk.Label(settings_window, text="Base URL:")
lbl_base_url = tk.Label(settings_window, text=_("Base URL:"))
lbl_base_url.pack(pady=(10, 0))
entry_base_url = tk.Entry(settings_window)
entry_base_url.pack(pady=(0, 10), padx=10, fill="x")
entry_base_url.insert(0, self.settings.base_url)
lbl_default_tags = tk.Label(settings_window, text="Default Tags:")
lbl_default_tags = tk.Label(settings_window, text=_("Default Tags:"))
lbl_default_tags.pack(pady=(10, 0))
entry_default_tags = tk.Entry(settings_window)
entry_default_tags.pack(pady=(0, 10), padx=10, fill="x")
entry_default_tags.insert(0, self.settings.default_tags)
lbl_browser = tk.Label(settings_window, text="Browser:")
lbl_browser = tk.Label(settings_window, text=_("Browser:"))
lbl_browser.pack(pady=(10, 0))
cb_browser = ttk.Combobox(
settings_window,
@@ -406,16 +514,39 @@ class ImageBrowser(tk.Tk):
)
)
lbl_lang = tk.Label(settings_window, text=_("Language:"))
lbl_lang.pack(pady=(10, 0))
cb_lang = ttk.Combobox(
settings_window,
values=list(self.settings.i18n.languages.values()),
state="readonly",
)
cb_lang.pack(pady=(0, 10), padx=10, fill="x")
cb_lang.set(
self.settings.i18n.languages.get(self.settings.i18n.current_lang, "en")
)
def save_and_close():
self.settings.username = entry_login.get()
self.settings.password = entry_password.get()
self.settings.base_url = entry_base_url.get()
self.settings.default_tags = entry_default_tags.get()
self.settings.browser = self.settings.installed_browsers[cb_browser.get()]
self.settings.i18n.set_language(
next(
(
lang
for lang, name in self.settings.i18n.languages.items()
if name == cb_lang.get()
),
"en",
)
)
self.settings.save_settings()
settings_window.destroy()
self.reload_ui()
btn_save = tk.Button(settings_window, text="Zapisz", command=save_and_close)
btn_save = tk.Button(settings_window, text=_("Zapisz"), command=save_and_close)
btn_save.pack(pady=10)
def create_widgets(self):
@@ -457,7 +588,7 @@ class ImageBrowser(tk.Tk):
right_frame.grid_rowconfigure(4, weight=0) # Upload Panel
# PNG Tags widget Text z scrollbar
png_frame = tk.LabelFrame(right_frame, text="PNG Tags")
png_frame = tk.LabelFrame(right_frame, text=_("PNG Tags"))
png_frame.grid(row=0, column=0, sticky=tk.EW, padx=5, pady=5)
png_frame.grid_columnconfigure(0, weight=1)
self.png_tags_text = tk.Text(png_frame, wrap=tk.WORD)
@@ -469,7 +600,7 @@ class ImageBrowser(tk.Tk):
)
# Tagger Tags widget Text z scrollbar
tagger_frame = tk.LabelFrame(right_frame, text="Tagger Tags")
tagger_frame = tk.LabelFrame(right_frame, text=_("Tagger Tags"))
tagger_frame.grid(row=1, column=0, sticky=tk.EW, padx=5, pady=5)
tagger_frame.grid_columnconfigure(0, weight=1)
self.tagger_tags_text = tk.Text(tagger_frame, wrap=tk.WORD)
@@ -483,7 +614,7 @@ class ImageBrowser(tk.Tk):
)
# Manual Tags Entry (stała wysokość)
manual_frame = tk.LabelFrame(right_frame, text="Manual Tags")
manual_frame = tk.LabelFrame(right_frame, text=_("Manual Tags"))
manual_frame.grid(row=2, column=0, sticky=tk.NSEW, padx=5, pady=5)
self.manual_tags_manager = TagManager(
manual_frame, self.settings, self.tags_repo, self.update_final_tags
@@ -491,7 +622,7 @@ class ImageBrowser(tk.Tk):
self.manual_tags_manager.pack(fill=tk.BOTH, expand=True)
# Final Tags widget Text z scrollbar, który rozszerza się
final_frame = tk.LabelFrame(right_frame, text="Final Tags")
final_frame = tk.LabelFrame(right_frame, text=_("Final Tags"))
final_frame.grid(row=3, column=0, sticky=tk.NSEW, padx=5, pady=5)
final_frame.grid_rowconfigure(0, weight=1)
final_frame.grid_columnconfigure(0, weight=1)
@@ -511,12 +642,12 @@ class ImageBrowser(tk.Tk):
)
self.rating_dropdown.pack(side=tk.LEFT, padx=5)
self.upload_button = tk.Button(
upload_frame, text="Upload", command=self.upload_current_image
upload_frame, text=_("Wyślij"), command=self.upload_current_image
)
self.upload_button.pack(side=tk.LEFT, padx=5)
self.upload_button.config(state=tk.DISABLED)
self.view_post_button = tk.Button(
upload_frame, text="Wyświetl", command=self.view_current_post
upload_frame, text=_("Wyświetl"), command=self.view_current_post
)
self.view_post_button.pack(side=tk.LEFT, padx=5)
self.view_post_button.config(state=tk.DISABLED)
@@ -533,9 +664,9 @@ class ImageBrowser(tk.Tk):
def update_status_bar(self):
status_text = (
f"Przetworzono tagi: {len(self.tagger_processed)}/{self.total_files} plików | "
f"Zweryfikowano status uploadu: {self.upload_verified}/{self.total_files} plików | "
f"Zuploadowano: {self.uploaded_count}/{self.upload_verified} plików"
f"{_('Przetworzono tagi:')} {len(self.tagger_processed)}/{self.total_files} {_('plików')} | "
f"{_('Zweryfikowano status uploadu:')} {self.upload_verified}/{self.total_files} {_('plików')} | "
f"{_('Zuploadowano:')} {self.uploaded_count}/{self.upload_verified} {_('plików')}"
)
self.status_label.config(text=status_text)
@@ -563,7 +694,7 @@ class ImageBrowser(tk.Tk):
Otwiera okno dialogowe wyboru folderu z obrazkami
i wczytuje pliki PNG z wybranego folderu.
"""
folder = filedialog.askdirectory(title="Wybierz folder z obrazkami PNG")
folder = filedialog.askdirectory(title=_("Wybierz folder z obrazkami PNG"))
if folder:
self.folder_path = folder
self.load_images()
@@ -593,7 +724,9 @@ class ImageBrowser(tk.Tk):
self.show_image(0)
self.post_load_processing()
else:
messagebox.showinfo("Informacja", "Brak plików PNG w wybranym folderze.")
messagebox.showinfo(
_("Informacja"), _("Brak plików PNG w wybranym folderze.")
)
def post_load_processing(self):
"""
@@ -675,7 +808,7 @@ class ImageBrowser(tk.Tk):
self.uploaded[file_path] = False
self.after(0, self.update_status_bar)
except Exception as e:
print("Błąd podczas sprawdzania paczki uploadu:", e)
print(_("Błąd podczas sprawdzania paczki uploadu:"), e)
self.after(100, self.join_check_uploaded_files_thread)
def update_button_states(self):
@@ -716,14 +849,14 @@ class ImageBrowser(tk.Tk):
post_ops_state = tk.NORMAL if post_id else tk.DISABLED
# Update menu items
self.file_menu.entryconfig("Wyślij wszystko", state=send_all_state)
self.file_menu.entryconfig("Podmień tagi", state=post_ops_state)
self.file_menu.entryconfig("Otwórz post", state=post_ops_state)
self.file_menu.entryconfig(_("Wyślij wszystko"), state=send_all_state)
self.file_menu.entryconfig(_("Podmień tagi"), state=post_ops_state)
self.file_menu.entryconfig(_("Otwórz post"), state=post_ops_state)
# Update buttons
self.upload_button.config(
state=tk.NORMAL if has_current else tk.DISABLED,
text="Podmień tagi" if post_id else "Wyślij",
text=_("Podmień tagi") if post_id else _("Wyślij"),
command=self.edit_current_image if post_id else self.upload_current_image,
)
self.view_post_button.config(state=post_ops_state)
@@ -731,7 +864,7 @@ class ImageBrowser(tk.Tk):
# Special case for "Wyślij" menu item
wyślij_state = tk.DISABLED if post_id else tk.NORMAL
self.file_menu.entryconfig(
"Wyślij", state=wyślij_state if has_current else tk.DISABLED
_("Wyślij"), state=wyślij_state if has_current else tk.DISABLED
)
def view_current_post(self):
@@ -751,7 +884,7 @@ class ImageBrowser(tk.Tk):
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(_("Błąd przy obliczaniu MD5:"), e)
return ""
return hash_md5.hexdigest()
@@ -807,7 +940,7 @@ class ImageBrowser(tk.Tk):
self.tagger_thread_idx += 1
thread.start()
except Exception as e:
messagebox.showerror("Błąd", f"Nie można załadować obrazka:\n{e}")
messagebox.showerror(_("Błąd"), f"{_('Nie można załadować obrazka:')}\n{e}")
def edit_current_image(self):
"""
@@ -1016,13 +1149,21 @@ class ImageBrowser(tk.Tk):
if tag in self.implication_graph:
implied_by_selected.update(nx.descendants(self.implication_graph, tag))
else:
print(f"Warning: Tag '{tag}' not found in implication graph")
print(
_("Warning: Tag '{tag}' not found in implication graph").format(
tag=tag
)
)
self.missing_tags.add(tag) # Log missing tags
for tag in selected_png_tags:
if tag in self.implication_graph:
implied_by_selected.update(nx.descendants(self.implication_graph, tag))
else:
print(f"Warning: Tag '{tag}' not found in implication graph")
print(
_("Warning: Tag '{tag}' not found in implication graph").format(
tag=tag
)
)
self.missing_tags.add(tag) # Log missing tags
# Build visible list
@@ -1249,7 +1390,7 @@ class ImageBrowser(tk.Tk):
# Ustaw komunikat, że Tagger pracuje
self.tagger_tags_text.config(state=tk.NORMAL)
self.tagger_tags_text.delete("1.0", tk.END)
self.tagger_tags_text.insert("1.0", "Tagger przetwarza...")
self.tagger_tags_text.insert("1.0", _("Tagger przetwarza..."))
self.tagger_tags_text.config(state=tk.DISABLED)
file_path = self.image_files[self.current_index]
result = self.get_tagger_results(file_path)
@@ -1264,14 +1405,21 @@ class ImageBrowser(tk.Tk):
file_path,
final_tags=None,
final_rating=None,
progress_queue=None,
cancel_event=None,
progress_queue: Optional[queue.Queue] = None,
cancel_event: Optional[threading.Event] = None,
):
base_file_name = os.path.basename(file_path)
if progress_queue:
progress_queue.put(("mode", "determinate"))
progress_queue.put(("max", 100))
progress_queue.put(("label", f"Wysyłam plik {base_file_name}..."))
progress_queue.put(
(
"label",
_("Wysyłam plik {base_file_name}...").format(
base_file_name=base_file_name
),
)
)
url = self.settings.base_url.rstrip("/") + "/api/danbooru/add_post"
tags = (
self.final_tags_text.get("1.0", tk.END).strip()
@@ -1308,22 +1456,29 @@ class ImageBrowser(tk.Tk):
show_warn = False
post_url = None
if response.status_code in (200, 201):
message = "Upload zakończony powodzeniem!"
message = _("Wysyłanie zakończone powodzeniem!")
post_url = response.headers.get("X-Danbooru-Location", None)
elif response.status_code == 409:
message = f"Upload zakończony błędem.\nStatus: 409\nTreść: {response.headers.get('X-Danbooru-Errors', '')}"
message = _(
"Wysyłanie zakończone błędem.\nStatus: {status_code}\nTreść: {text}"
).format(
status_code=response.status_code,
text=response.headers.get("X-Danbooru-Errors", ""),
)
post_url = response.headers.get("X-Danbooru-Location", None)
show_warn = True
else:
message = f"Upload zakończony błędem.\nStatus: {response.status_code}\nTreść: {response.text}"
message = _(
"Wysyłanie zakończone błędem.\nStatus: {status_code}\nTreść: {text}"
).format(status_code=response.status_code, text=response.text)
show_warn = True
# Aktualizacja wyglądu listy musimy użyć domyślnych argumentów w lambdzie, aby zachować bieżący indeks
if show_warn:
if not final_tags:
messagebox.showwarning("Upload", message)
messagebox.showwarning(_("Wysyłanie"), message)
else:
if not final_tags:
messagebox.showinfo("Upload", message)
messagebox.showinfo(_("Wysyłanie"), message)
self.after(
0,
lambda idx=self.image_files.index(
@@ -1336,7 +1491,7 @@ class ImageBrowser(tk.Tk):
self.uploaded_count += 1
self.after(0, self.update_status_bar)
except Exception as e:
messagebox.showerror("Błąd uploadu", str(e))
messagebox.showerror(_("Błąd wysyłania"), str(e))
finally:
self.upload_button.after(0, self.update_button_states)
@@ -1356,20 +1511,27 @@ class ImageBrowser(tk.Tk):
if not post_id:
messagebox.showerror(
"Błąd edycji", "Post nie został znaleziony dla tego pliku"
_("Błąd edycji"), _("Post nie został znaleziony dla tego pliku")
)
return
if progress_queue:
progress_queue.put(("mode", "determinate"))
progress_queue.put(("max", 100))
progress_queue.put(("label", f"Aktualizuję tagi dla {base_file_name}..."))
progress_queue.put(
(
"label",
_("Aktualizuję tagi dla {base_file_name}...").format(
base_file_name=base_file_name
),
)
)
try:
# Check for cancellation before starting the operation.
if cancel_event is not None and cancel_event.is_set():
if progress_queue:
progress_queue.put(("label", "Operacja anulowana"))
progress_queue.put(("label", _("Operacja anulowana")))
return
# Get authentication session and token
@@ -1379,7 +1541,7 @@ class ImageBrowser(tk.Tk):
# Check cancellation after login if needed.
if cancel_event is not None and cancel_event.is_set():
if progress_queue:
progress_queue.put(("label", "Operacja anulowana"))
progress_queue.put(("label", _("Operacja anulowana")))
return
# Prepare tags and rating
@@ -1410,7 +1572,7 @@ class ImageBrowser(tk.Tk):
# Check for cancellation before sending the update request.
if cancel_event is not None and cancel_event.is_set():
if progress_queue:
progress_queue.put(("label", "Operacja anulowana"))
progress_queue.put(("label", _("Operacja anulowana")))
return
# Send update request
@@ -1420,23 +1582,23 @@ class ImageBrowser(tk.Tk):
if response.status_code == 302:
if progress_queue:
progress_queue.put(("progress", 100))
message = "Tagi zostały zaktualizowane!"
message = _("Tagi zostały zaktualizowane!")
if not final_tags: # Only show success if not bulk operation
messagebox.showinfo("Sukces edycji", message)
messagebox.showinfo(_("Sukces edycji"), message)
# Update UI state
self.after(0, self.update_button_states)
return
# Handle other status codes
error_msg = (
f"Błąd podczas aktualizacji tagów\nStatus: {response.status_code}"
error_msg = _("Błąd podczas aktualizacji tagów\nStatus: {code}").format(
code=response.status_code
)
if response.text:
error_msg += f"\nTreść: {response.text}"
error_msg += f"\n{_('Treść:')} {response.text}"
messagebox.showerror("Błąd edycji", error_msg)
except Exception as e:
messagebox.showerror("Krytyczny błąd edycji", str(e))
messagebox.showerror(_("Krytyczny błąd edycji"), str(e))
finally:
if progress_queue:
progress_queue.put(("progress", 100))
@@ -1448,12 +1610,18 @@ class ImageBrowser(tk.Tk):
i wywołuje upload_file.
"""
if not messagebox.askyesno(
"Potwierdzenie",
"Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\nKażdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\nUpewnij się, że tagi są poprawne!",
_("Potwierdzenie"),
_(
"Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\nKażdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\nUpewnij się, że tagi są poprawne!"
),
):
return
def worker(progress_queue: queue = None, cancel_event: threading.Event = None):
def worker(
progress_queue: queue.Queue = None,
cancel_event: threading.Event = None,
secondary_progress_queue: queue.Queue = None,
):
files_count = len(self.image_files)
if progress_queue:
progress_queue.put(("mode", "determinate"))
@@ -1461,26 +1629,37 @@ class ImageBrowser(tk.Tk):
file_idx = 0
for file_path in self.image_files:
if progress_queue:
progress_queue.put(("progress", file_idx * 1.0 / files_count))
progress_queue.put(("progress", file_idx * 100 / files_count))
progress_queue.put(
("label", f"Wysyłam plik {file_idx+1}/{files_count}...")
)
if cancel_event is not None and cancel_event.is_set():
if progress_queue:
progress_queue.put(("label", f"Anulowano operację!"))
progress_queue.put(("label", _("Anulowano operację!")))
return
if not self.uploaded.get(file_path, False):
final_tags, final_rating = (
self.compute_final_tags_and_rating_for_file(file_path)
)
print(
f"Uploading {file_path} z tagami: {final_tags} i ratingiem: {final_rating}"
_(
"Wysyłanie {file_path} z tagami: {final_tags} i ratingiem: {final_rating}"
).format(
file_path=file_path,
final_tags=final_tags,
final_rating=final_rating,
)
)
self.upload_file(
file_path, final_tags=final_tags, final_rating=final_rating
file_path,
final_tags=final_tags,
final_rating=final_rating,
progress_queue=secondary_progress_queue,
cancel_event=cancel_event,
)
file_idx += 1
if progress_queue:
progress_queue.put(("label", f"Przesłano pliki!"))
progress_queue.put(("label", _("Przesłano pliki!")))
progress_queue.put(("progress", 100))
self.processing_dialog = ProcessingDialog(self, worker)