Update Kapitanbooru Uploader to version 0.9.0 with significant changes
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Gitea/kapitanbooru-uploader/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Gitea/kapitanbooru-uploader/pipeline/head This commit looks good
				
			- Updated version number in pyproject.toml and messages.po files. - Added new translations and updated existing ones in messages.po for better localization. - Implemented core functionality in Core.py, including image processing, tagging, and upload logic. - Enhanced the autotagging feature to support multiple image formats (PNG, JPEG, WebP, AVIF, GIF). - Improved error handling and logging for file operations and network requests. - Added functionality to check for uploaded files and manage tag updates for existing posts. - Introduced threading for background processing to improve application responsiveness.
This commit is contained in:
		
							
								
								
									
										744
									
								
								kapitanbooru_uploader/Core.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										744
									
								
								kapitanbooru_uploader/Core.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,744 @@ | ||||
| import glob | ||||
| import hashlib | ||||
| import os | ||||
| import threading | ||||
| from typing import Any, Callable, Tuple | ||||
| import concurrent.futures | ||||
| from packaging.version import parse as parse_version | ||||
| import queue | ||||
| import time | ||||
|  | ||||
| import networkx as nx | ||||
| import requests | ||||
| from PIL import Image | ||||
| import wdtagger as wdt | ||||
|  | ||||
| from .I18N import _ | ||||
| from .TagsRepo import TagsRepo | ||||
| from .settings import Settings | ||||
| from .common import login, get_auth_token | ||||
| from .ProgressFile import ProgressFile | ||||
| from .tag_processing import ( | ||||
|     TAG_FIXES, | ||||
|     extract_parameters, | ||||
|     parse_parameters, | ||||
|     process_tag, | ||||
|     extract_artist_from_filename, | ||||
| ) | ||||
| from .tagger_cache import TaggerCache | ||||
|  | ||||
|  | ||||
| class Core: | ||||
|     """ | ||||
|     Core functionality for Kapitanbooru Uploader. | ||||
|     Handles image processing, tagging, and upload logic. | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, settings: Settings, gui_mode: bool = True): | ||||
|         self.version = "0.9.0" | ||||
|         self.acknowledged_version = parse_version(self.version) | ||||
|         self.settings = settings | ||||
|         self.tags_repo = TagsRepo(settings) | ||||
|         # Dodatkowe ustawienia dla Taggera | ||||
|         self.tagger_name = "wdtagger" | ||||
|         self.tagger_version = ( | ||||
|             "1.0"  # możesz ustawić wersję dynamicznie, jeśli to możliwe | ||||
|         ) | ||||
|         self.tagger_cache = TaggerCache( | ||||
|             self.settings, self.tagger_name, self.tagger_version | ||||
|         ) | ||||
|         self.gui_mode = gui_mode | ||||
|         self.main_thread_queue = queue.Queue() | ||||
|         self.after_events = [] | ||||
|  | ||||
|         self.implication_graph = self.load_implication_graph() | ||||
|         self.missing_tags = set()  # Track tags not in the graph | ||||
|  | ||||
|         self.check_uploaded_files_stop_event = threading.Event() | ||||
|         self.check_uploaded_files_thread: threading.Thread | None = None | ||||
|         self.process_tagger_queue_stop_event = threading.Event() | ||||
|         self.process_tagger_queue_thread: threading.Thread | None = None | ||||
|  | ||||
|         self.check_uploaded_files_callback = None | ||||
|         self.update_status_bar_callback = None | ||||
|         self.process_tagger_for_image_callback = None | ||||
|         self.upload_file_success_callback: Callable[[str], Any] | None = None | ||||
|         self.upload_file_completed_callback: Callable | None = None | ||||
|  | ||||
|         self.folder_path = "" | ||||
|         self.image_files = [] | ||||
|         self.image_files_md5 = [] | ||||
|         self.current_index = None | ||||
|         self.image_cache = None | ||||
|         self.tagger_thread_idx = 0 | ||||
|         self.tagger = wdt.Tagger() | ||||
|  | ||||
|         # Liczniki statusu | ||||
|         self.total_files = 0 | ||||
|         self.tagger_processed = set() | ||||
|         self.upload_verified = 0 | ||||
|         self.uploaded_count = 0 | ||||
|  | ||||
|         # Oryginalny obraz (do skalowania) | ||||
|         self.current_image_original = None | ||||
|         self.current_parameters = "" | ||||
|  | ||||
|         # Mapa ratingów: wyświetlana nazwa -> wartość wysyłana | ||||
|         self.rating_map = { | ||||
|             "General": "g", | ||||
|             "Sensitive": "s", | ||||
|             "Questionable": "q", | ||||
|             "Explicit": "e", | ||||
|             "Unrated": "", | ||||
|         } | ||||
|  | ||||
|         # Słowniki przechowujące stany tagów (dla PNG i Taggera) | ||||
|         self.png_tags_states = {} | ||||
|         self.tagger_tags_states = {} | ||||
|  | ||||
|         # Nowy słownik przechowujący informację, czy dany plik (ścieżka) został już uploadowany | ||||
|         self.uploaded = {}  # key: file path, value: True/False | ||||
|  | ||||
|     def schedule_in_main_thread(self, func, delay_ms=0): | ||||
|         """Schedule a function to run in the main thread""" | ||||
|         if self.gui_mode: | ||||
|             # In GUI mode, use the after mechanism | ||||
|             self.after_events.append((time.time() + delay_ms / 1000, func)) | ||||
|         else: | ||||
|             # In non-GUI mode, add to queue for immediate execution | ||||
|             self.main_thread_queue.put(func) | ||||
|  | ||||
|     def process_main_thread_queue(self): | ||||
|         """Process pending main thread tasks""" | ||||
|         if self.gui_mode: | ||||
|             # Process scheduled events | ||||
|             now = time.time() | ||||
|             new_events = [] | ||||
|             for schedule_time, func in self.after_events: | ||||
|                 if now >= schedule_time: | ||||
|                     try: | ||||
|                         func() | ||||
|                     except Exception as e: | ||||
|                         print(f"Error in scheduled task: {e}") | ||||
|                 else: | ||||
|                     new_events.append((schedule_time, func)) | ||||
|             self.after_events = new_events | ||||
|         else: | ||||
|             # Process all queued tasks in non-GUI mode | ||||
|             while not self.main_thread_queue.empty(): | ||||
|                 try: | ||||
|                     func = self.main_thread_queue.get_nowait() | ||||
|                     func() | ||||
|                 except queue.Empty: | ||||
|                     break | ||||
|                 except Exception as e: | ||||
|                     print(f"Error in main thread task: {e}") | ||||
|  | ||||
|     def load_implication_graph(self) -> nx.DiGraph: | ||||
|         G = nx.DiGraph() | ||||
|         conn = self.tags_repo.get_conn() | ||||
|         cursor = conn.cursor() | ||||
|  | ||||
|         # Step 1: Add all tags from the 'tags' table | ||||
|         cursor.execute( | ||||
|             """ | ||||
|             SELECT | ||||
|             CASE category | ||||
|             WHEN 1 THEN 'artist:' || name | ||||
|             WHEN 3 THEN 'copyright:' || name | ||||
|             WHEN 4 THEN 'character:' || name | ||||
|             WHEN 5 THEN 'meta:' || name | ||||
|             ELSE name | ||||
|             END AS prefixed_name | ||||
|             FROM tags | ||||
|             """ | ||||
|         ) | ||||
|         db_tags = {row[0] for row in cursor.fetchall()} | ||||
|         G.add_nodes_from(db_tags) | ||||
|  | ||||
|         # Step 2: Add nodes from implications (antecedents/consequents not in 'tags' table) | ||||
|         cursor.execute("SELECT antecedent, consequent FROM tag_closure") | ||||
|         edge_tags = set() | ||||
|         for ant, cons in cursor.fetchall(): | ||||
|             edge_tags.add(ant) | ||||
|             edge_tags.add(cons) | ||||
|         G.add_nodes_from(edge_tags - db_tags)  # Add tags only in implications | ||||
|  | ||||
|         # Step 3: Add edges | ||||
|         cursor.execute("SELECT antecedent, consequent FROM tag_closure") | ||||
|         G.add_edges_from(cursor.fetchall()) | ||||
|  | ||||
|         conn.close() | ||||
|         return G | ||||
|  | ||||
|     def compute_final_tags_and_rating_for_file( | ||||
|         self, file_path, update_status_callback, manual_tags=set() | ||||
|     ) -> Tuple[str, str]: | ||||
|         """ | ||||
|         Oblicza finalną listę tagów dla danego pliku oraz rating. | ||||
|         Łączy tagi z: | ||||
|         - pliku (PNG): parsowane przez parse_parameters, | ||||
|         - Taggera (wynik z cache lub wyliczony na bieżąco), | ||||
|         - ustawień (default tags), | ||||
|         - manualnych tagów (z pola manual_tags_entry), | ||||
|         oraz dodaje tag "meta:auto_upload". | ||||
|         Zwraca finalny ciąg tagów oraz rating. | ||||
|         """ | ||||
|         # Pobierz tagi z pliku | ||||
|         try: | ||||
|             img = Image.open(file_path) | ||||
|             parameters = extract_parameters(img, file_path) | ||||
|             artist_tag = extract_artist_from_filename(file_path) | ||||
|             png_tags = set( | ||||
|                 [ | ||||
|                     x | ||||
|                     for x in parse_parameters(parameters, self.tags_repo).split() | ||||
|                     if process_tag(x, self.tags_repo)[1] | ||||
|                     is not None  # Ignoruj nieistniejące tagi | ||||
|                 ] | ||||
|             ) | ||||
|             if artist_tag: | ||||
|                 png_tags.add("artist:" + artist_tag.replace(" ", "_").replace("\\", "")) | ||||
|             img.close() | ||||
|         except Exception as e: | ||||
|             print(_("Błąd przy otwieraniu pliku"), file_path, ":", e) | ||||
|             png_tags = set() | ||||
|  | ||||
|         # Pobierz tagi z Taggera – sprawdzając cache | ||||
|         result = self.get_tagger_results(file_path, update_status_callback) | ||||
|         tagger_tags = set() | ||||
|         rating = "Unrated" | ||||
|         tagger_tags.update( | ||||
|             ( | ||||
|                 TAG_FIXES[tag] if tag in TAG_FIXES else tag | ||||
|                 for tag in result.general_tag_data.keys() | ||||
|             ) | ||||
|         )  # Zamień nieprawidłowe tagi na poprawne | ||||
|         for t in result.character_tags: | ||||
|             full_tag = "character:" + t.replace(" ", "_").replace("\\", "") | ||||
|             # Zamień nieprawidłowe tagi na poprawne | ||||
|             if full_tag in TAG_FIXES: | ||||
|                 full_tag = TAG_FIXES[full_tag] | ||||
|             tagger_tags.add(full_tag) | ||||
|         rating = self.map_tagger_rating(result) | ||||
|  | ||||
|         # Pobierz tagi z ustawień i manualne | ||||
|         default_tags = set(self.settings.default_tags.split()) | ||||
|  | ||||
|         # Finalna lista: suma wszystkich tagów | ||||
|         final_tags = default_tags.union(png_tags).union(tagger_tags).union(manual_tags) | ||||
|         final_tags.add("meta:auto_upload") | ||||
|         return " ".join(sorted(final_tags)), rating | ||||
|  | ||||
|     def get_tagger_results(self, file_path, callback) -> wdt.Result: | ||||
|         md5 = self.image_files_md5[file_path] | ||||
|         cached = self.tagger_cache[md5] | ||||
|         if cached: | ||||
|             self.tagger_processed.add(md5) | ||||
|             return cached["result"] | ||||
|         try: | ||||
|             with Image.open(file_path) as img: | ||||
|                 result = self.tagger.tag(img) | ||||
|             self.tagger_cache[md5] = result | ||||
|             self.tagger_processed.add(md5) | ||||
|             callback() | ||||
|             print(_("Tagger przetworzył:"), f"{file_path}") | ||||
|             return result | ||||
|         except Exception as e: | ||||
|             print(_("Błąd Taggera dla"), file_path, ":", e) | ||||
|  | ||||
|     def map_tagger_rating(self, result: wdt.Result) -> str: | ||||
|         """ | ||||
|         Mapuje rating z Taggera na wartość używaną w Kapitanbooru. | ||||
|         """ | ||||
|         if result.rating == "general": | ||||
|             new_rating = "General" | ||||
|         elif result.rating == "sensitive": | ||||
|             new_rating = "Sensitive" | ||||
|         elif result.rating == "questionable": | ||||
|             new_rating = "Questionable" | ||||
|         elif result.rating == "explicit": | ||||
|             new_rating = "Explicit" | ||||
|         else: | ||||
|             new_rating = "Unrated" | ||||
|         return new_rating | ||||
|  | ||||
|     def load_images(self): | ||||
|         """ | ||||
|         Ładuje pliki PNG, JPEG, WebP, AVIF i GIF z wybranego folderu. | ||||
|         """ | ||||
|         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: 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) | ||||
|         self.uploaded.clear() | ||||
|         self.upload_verified = 0 | ||||
|         self.uploaded_count = 0 | ||||
|         for file in self.image_files: | ||||
|             self.uploaded[file] = False | ||||
|         if self.image_files: | ||||
|             self.post_load_processing() | ||||
|  | ||||
|     def compute_md5(self, file_path, chunk_size=8192): | ||||
|         """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(_("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 post_load_processing(self): | ||||
|         """ | ||||
|         Po załadowaniu plików, sprawdza czy są jakieś pliki do uploadu oraz przetwarza Taggerem pliki. | ||||
|         """ | ||||
|         self.join_check_uploaded_files_thread() | ||||
|         self.check_uploaded_files_thread = threading.Thread( | ||||
|             target=self.check_uploaded_files | ||||
|         ) | ||||
|         self.check_uploaded_files_thread.start() | ||||
|         self.join_process_tagger_queue_thread() | ||||
|         self.process_tagger_queue_thread = threading.Thread( | ||||
|             target=self.process_tagger_queue | ||||
|         ) | ||||
|         self.process_tagger_queue_thread.start() | ||||
|  | ||||
|     def wait_for_completion(self): | ||||
|         """Wait for background threads to finish (non-GUI mode)""" | ||||
|         # Join the checking thread if running | ||||
|         if ( | ||||
|             self.check_uploaded_files_thread | ||||
|             and self.check_uploaded_files_thread.is_alive() | ||||
|         ): | ||||
|             self.check_uploaded_files_stop_event.set() | ||||
|             self.check_uploaded_files_thread.join() | ||||
|  | ||||
|         # Join the tagger processing thread if running | ||||
|         if ( | ||||
|             self.process_tagger_queue_thread | ||||
|             and self.process_tagger_queue_thread.is_alive() | ||||
|         ): | ||||
|             self.process_tagger_queue_stop_event.set() | ||||
|             self.process_tagger_queue_thread.join() | ||||
|  | ||||
|         # Process any remaining main thread tasks | ||||
|         self.process_main_thread_queue() | ||||
|  | ||||
|     def join_check_uploaded_files_thread(self): | ||||
|         if self.check_uploaded_files_thread is not None: | ||||
|             self.check_uploaded_files_stop_event.set() | ||||
|             self.check_uploaded_files_thread.join() | ||||
|             self.check_uploaded_files_thread = None | ||||
|             self.check_uploaded_files_stop_event = threading.Event() | ||||
|  | ||||
|     def join_process_tagger_queue_thread(self): | ||||
|         if self.process_tagger_queue_thread is not None: | ||||
|             self.process_tagger_queue_stop_event.set() | ||||
|             self.process_tagger_queue_thread.join() | ||||
|             self.process_tagger_queue_thread = None | ||||
|             self.process_tagger_queue_stop_event = threading.Event() | ||||
|  | ||||
|     def process_tagger_for_image(self, file_path): | ||||
|         """Przetwarza obrazek przy użyciu Taggera i zapisuje wynik do cache.""" | ||||
|         result = self.get_tagger_results(file_path) | ||||
|         if self.process_tagger_for_image_callback: | ||||
|             self.process_tagger_for_image_callback(file_path, result.rating) | ||||
|  | ||||
|     def process_tagger_queue(self): | ||||
|         """Przetwarza wszystkie obrazki w tle (pomijając aktualnie wybrany).""" | ||||
|         for file_path in self.image_files: | ||||
|             if self.process_tagger_queue_stop_event.is_set(): | ||||
|                 break | ||||
|             # Jeśli obrazek jest aktualnie wybrany, pomijamy – on będzie przetwarzany w foreground | ||||
|             if ( | ||||
|                 self.current_index is not None | ||||
|                 and file_path == self.image_files[self.current_index] | ||||
|             ): | ||||
|                 continue | ||||
|             self.process_tagger_for_image(file_path) | ||||
|         self.schedule_in_main_thread(self.join_process_tagger_queue_thread, 100) | ||||
|  | ||||
|     def check_uploaded_files(self): | ||||
|         """ | ||||
|         Dla każdego obrazu oblicza MD5, grupuje je w paczki (do 100 skrótów), | ||||
|         wysyła zapytanie do endpointa 'posts.json' dla każdej paczki, | ||||
|         a następnie na podstawie odpowiedzi ustawia w self.uploaded post id dla uploadowanych plików. | ||||
|         """ | ||||
|         file_md5_list = [ | ||||
|             (idx, file, self.image_files_md5[file]) | ||||
|             for idx, file in enumerate(self.image_files) | ||||
|         ] | ||||
|  | ||||
|         batch_size = 100 | ||||
|         for i in range(0, len(file_md5_list), batch_size): | ||||
|             if self.check_uploaded_files_stop_event.is_set(): | ||||
|                 break | ||||
|             batch = file_md5_list[i : i + batch_size] | ||||
|             batch_md5 = [item[2] for item in batch] | ||||
|             md5_param = ",".join(batch_md5) | ||||
|             url = self.settings.base_url.rstrip("/") + "/posts.json" | ||||
|             try: | ||||
|                 response = requests.get(url, params={"md5": md5_param}) | ||||
|  | ||||
|                 root = response.json() | ||||
|                 found = {} | ||||
|                 for elem in root: | ||||
|                     if self.check_uploaded_files_stop_event.is_set(): | ||||
|                         break | ||||
|                     post_md5 = elem.get("md5", "").lower() | ||||
|                     post_id = elem.get("id") | ||||
|                     if post_md5 and post_id: | ||||
|                         found[post_md5] = post_id | ||||
|                 for idx, file_path, md5 in batch: | ||||
|                     if self.check_uploaded_files_stop_event.is_set(): | ||||
|                         break | ||||
|                     self.upload_verified += 1  # Każdy plik w batchu jest zweryfikowany | ||||
|                     if md5.lower() in found: | ||||
|                         self.uploaded[file_path] = found[md5.lower()] | ||||
|                         self.uploaded_count += 1 | ||||
|                         if self.check_uploaded_files_callback: | ||||
|                             self.check_uploaded_files_callback(idx) | ||||
|                     else: | ||||
|                         self.uploaded[file_path] = False | ||||
|                     if self.update_status_bar_callback: | ||||
|                         self.update_status_bar_callback() | ||||
|  | ||||
|             except Exception as e: | ||||
|                 print(_("Błąd podczas sprawdzania paczki uploadu:"), e) | ||||
|         self.schedule_in_main_thread(self.join_check_uploaded_files_thread, 100) | ||||
|  | ||||
|     def autotag_files(self, file_paths): | ||||
|         """ | ||||
|         Autotaguje pliki przy użyciu Taggera i wysyła je na serwer. | ||||
|         """ | ||||
|         for file_path in file_paths: | ||||
|             if not os.path.isfile(file_path): | ||||
|                 print(_("Plik nie istnieje:"), file_path) | ||||
|                 continue | ||||
|             try: | ||||
|                 tags, rating = self.compute_final_tags_and_rating_for_file( | ||||
|                     file_path, lambda: None | ||||
|                 ) | ||||
|                 print(_("Tagi dla pliku"), file_path, ":", tags, "Rating:", rating) | ||||
|                 self.upload_file( | ||||
|                     file_path, | ||||
|                     final_tags=tags, | ||||
|                     final_rating=rating, | ||||
|                     progress_queue=None, | ||||
|                     cancel_event=None, | ||||
|                 ) | ||||
|             except Exception as e: | ||||
|                 print(_("Błąd podczas autotagowania pliku"), file_path, ":", e) | ||||
|  | ||||
|     def autotag_dir(self, dir): | ||||
|         """ | ||||
|         Autotaguje wszystkie pliki w katalogu przy użyciu Taggera i wysyła je na serwer. | ||||
|         """ | ||||
|         if not os.path.isdir(dir): | ||||
|             print(_("Podana ścieżka nie jest katalogiem:"), dir) | ||||
|             return | ||||
|         self.folder_path = dir | ||||
|         self.load_images() | ||||
|         self.check_uploaded_files_thread.join() | ||||
|         self.process_tagger_queue_thread.join() | ||||
|         files_to_upload = [x for x in self.image_files if not self.uploaded[x]] | ||||
|         if not files_to_upload: | ||||
|             print(_("Brak obrazów do przetworzenia w katalogu:"), dir) | ||||
|             return | ||||
|  | ||||
|         self.autotag_files(files_to_upload) | ||||
|  | ||||
|     def upload_file( | ||||
|         self, | ||||
|         file_path, | ||||
|         final_tags=None, | ||||
|         final_rating=None, | ||||
|         progress_queue: queue.Queue | None = None, | ||||
|         cancel_event: threading.Event | None = None, | ||||
|         info_callback: Callable[[str], Any] | None = None, | ||||
|         warning_callback: Callable[[str], Any] | None = None, | ||||
|         error_callback: Callable[[str], Any] | None = 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", | ||||
|                     _("Wysyłam plik {base_file_name}...").format( | ||||
|                         base_file_name=base_file_name | ||||
|                     ), | ||||
|                 ) | ||||
|             ) | ||||
|         url = self.settings.base_url.rstrip("/") + "/api/danbooru/add_post" | ||||
|         tags = final_tags | ||||
|         fields = { | ||||
|             "login": self.settings.username, | ||||
|             "password": self.settings.password, | ||||
|             "tags": tags, | ||||
|             "source": "", | ||||
|         } | ||||
|         rating_value = self.rating_map.get(final_rating, "") | ||||
|         if rating_value: | ||||
|             fields["rating"] = rating_value | ||||
|         try: | ||||
|             total_size = os.path.getsize(file_path) | ||||
|  | ||||
|             def progress_callback(bytes_read, total_size): | ||||
|                 if progress_queue: | ||||
|                     percentage = int(bytes_read / total_size * 100) | ||||
|                     progress_queue.put(("progress", percentage)) | ||||
|  | ||||
|             with open(file_path, "rb") as f: | ||||
|                 wrapped_file = ProgressFile( | ||||
|                     f, progress_callback, total_size, cancel_event | ||||
|                 ) | ||||
|                 files = {"file": (base_file_name, wrapped_file, "image/png")} | ||||
|                 response = requests.post(url, data=fields, files=files) | ||||
|             if progress_queue: | ||||
|                 progress_queue.put(("progress", 100)) | ||||
|             show_warn = False | ||||
|             post_url = None | ||||
|             if response.status_code in (200, 201): | ||||
|                 message = _("Wysyłanie zakończone powodzeniem!") | ||||
|                 post_url = response.headers.get("X-Danbooru-Location", None) | ||||
|             elif response.status_code == 409: | ||||
|                 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 = _( | ||||
|                     "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: | ||||
|                     if warning_callback: | ||||
|                         warning_callback(message) | ||||
|                     else: | ||||
|                         print("[WARN]", _("Wysyłanie"), message) | ||||
|             else: | ||||
|                 if not final_tags: | ||||
|                     if info_callback: | ||||
|                         info_callback(message) | ||||
|                     else: | ||||
|                         print("[INFO]", _("Wysyłanie"), message) | ||||
|                 if self.upload_file_success_callback: | ||||
|                     self.upload_file_success_callback(file_path) | ||||
|             if post_url: | ||||
|                 post_id = post_url.split("/")[-1] | ||||
|                 self.uploaded[file_path] = post_id | ||||
|                 self.uploaded_count += 1 | ||||
|                 self.after(0, self.update_status_bar) | ||||
|         except Exception as e: | ||||
|             if error_callback: | ||||
|                 error_callback(str(e)) | ||||
|             else: | ||||
|                 print("[ERROR]", _("Błąd wysyłania pliku"), file_path, ":", e) | ||||
|         finally: | ||||
|             if self.upload_file_completed_callback: | ||||
|                 self.upload_file_completed_callback() | ||||
|  | ||||
|     def upload_all_files( | ||||
|         self, | ||||
|         progress_queue: queue.Queue = None, | ||||
|         cancel_event: threading.Event = None, | ||||
|         secondary_progress_queue: queue.Queue = None, | ||||
|         update_status_callback=None, | ||||
|         manual_tags: set = set(), | ||||
|         info_callback: Callable[[str], Any] | None = None, | ||||
|         warning_callback: Callable[[str], Any] | None = None, | ||||
|         error_callback: Callable[[str], Any] | None = None, | ||||
|     ): | ||||
|         files_to_upload = [x for x in self.image_files if not self.uploaded[x]] | ||||
|         files_count = len(files_to_upload) | ||||
|         if progress_queue: | ||||
|             progress_queue.put(("mode", "determinate")) | ||||
|             progress_queue.put(("max", 100)) | ||||
|         file_idx = 0 | ||||
|         for file_path in files_to_upload: | ||||
|             if progress_queue: | ||||
|                 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", _("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, | ||||
|                     update_status_callback,  # lambda: self.after(0, self.update_status_bar) | ||||
|                     manual_tags,  # set(self.manual_tags_manager.manual_tags) | ||||
|                 ) | ||||
|                 print( | ||||
|                     _( | ||||
|                         "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, | ||||
|                     progress_queue=secondary_progress_queue, | ||||
|                     cancel_event=cancel_event, | ||||
|                     info_callback=info_callback, | ||||
|                     warning_callback=warning_callback, | ||||
|                     error_callback=error_callback, | ||||
|                 ) | ||||
|             file_idx += 1 | ||||
|         if progress_queue: | ||||
|             progress_queue.put(("label", _("Przesłano pliki!"))) | ||||
|             progress_queue.put(("progress", 100)) | ||||
|  | ||||
|     def edit_file( | ||||
|         self, | ||||
|         file_path, | ||||
|         final_tags=None, | ||||
|         final_rating=None, | ||||
|         progress_queue=None, | ||||
|         cancel_event=None, | ||||
|         info_callback: Callable[[str], Any] | None = None, | ||||
|         error_callback: Callable[[str], Any] | None = None, | ||||
|     ): | ||||
|         """ | ||||
|         Update tags and rating for an existing post without uploading the file. | ||||
|         """ | ||||
|         base_file_name = os.path.basename(file_path) | ||||
|         post_id = self.uploaded.get(file_path) | ||||
|  | ||||
|         if not post_id: | ||||
|             if error_callback: | ||||
|                 error_callback(_("Post nie został znaleziony dla tego pliku")) | ||||
|             else: | ||||
|                 print( | ||||
|                     "[ERROR]", | ||||
|                     _("Post nie został znaleziony dla tego pliku"), | ||||
|                     file_path, | ||||
|                 ) | ||||
|             return | ||||
|  | ||||
|         if progress_queue: | ||||
|             progress_queue.put(("mode", "determinate")) | ||||
|             progress_queue.put(("max", 100)) | ||||
|             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"))) | ||||
|                 return | ||||
|  | ||||
|             # Get authentication session and token | ||||
|             session = login(self.settings) | ||||
|             auth_token = get_auth_token(session, self.settings) | ||||
|  | ||||
|             # 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"))) | ||||
|                 return | ||||
|  | ||||
|             # Prepare tags and rating | ||||
|             tags = final_tags | ||||
|             rating_value = self.rating_map.get(final_rating, "?") | ||||
|  | ||||
|             # Prepare API request | ||||
|             url = self.settings.base_url.rstrip("/") + "/post/set" | ||||
|             payload = { | ||||
|                 "auth_token": auth_token, | ||||
|                 "image_id": post_id, | ||||
|                 "title": base_file_name, | ||||
|                 "owner": self.settings.username, | ||||
|                 "tags": tags, | ||||
|                 "source": "", | ||||
|                 "rating": rating_value, | ||||
|             } | ||||
|  | ||||
|             if progress_queue: | ||||
|                 progress_queue.put(("progress", 50)) | ||||
|  | ||||
|             # 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"))) | ||||
|                 return | ||||
|  | ||||
|             # Send update request | ||||
|             response = session.post(url, data=payload, allow_redirects=False) | ||||
|  | ||||
|             # Handle 302 redirect as success case | ||||
|             if response.status_code == 302: | ||||
|                 if progress_queue: | ||||
|                     progress_queue.put(("progress", 100)) | ||||
|                 message = _("Tagi zostały zaktualizowane!") | ||||
|                 if not final_tags:  # Only show success if not bulk operation | ||||
|                     if info_callback: | ||||
|                         info_callback(message) | ||||
|                     else: | ||||
|                         print("[INFO]", _("Sukces edycji"), message) | ||||
|                 # Update UI state | ||||
|                 if self.upload_file_completed_callback: | ||||
|                     self.upload_file_completed_callback() | ||||
|                 return | ||||
|  | ||||
|             # Handle other status codes | ||||
|             error_msg = _("Błąd podczas aktualizacji tagów\nStatus: {code}").format( | ||||
|                 code=response.status_code | ||||
|             ) | ||||
|             if response.text: | ||||
|                 error_msg += f"\n{_('Treść:')} {response.text}" | ||||
|             if error_callback: | ||||
|                 error_callback(error_msg) | ||||
|             else: | ||||
|                 print("[ERROR]", _("Błąd edycji"), error_msg) | ||||
|  | ||||
|         except Exception as e: | ||||
|             if error_callback: | ||||
|                 error_callback(str(e)) | ||||
|             else: | ||||
|                 print("[ERROR]", _("Krytyczny błąd edycji"), file_path, ":", e) | ||||
|         finally: | ||||
|             if progress_queue: | ||||
|                 progress_queue.put(("progress", 100)) | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,11 +1,43 @@ | ||||
| """kapitanbooru_uploader.__main__: executed | ||||
| when kapitanbooru_uploader directory is called as script.""" | ||||
|  | ||||
| from .Core import Core | ||||
| from .settings import Settings | ||||
| from .ImageBrowser import ImageBrowser | ||||
| import argparse | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     app = ImageBrowser() | ||||
|     app.mainloop() | ||||
|     parser = argparse.ArgumentParser( | ||||
|         prog="kapitanbooru-uploader", description="KapitanBooru Uploader" | ||||
|     ) | ||||
|     group = parser.add_mutually_exclusive_group() | ||||
|  | ||||
|     # Add arguments to the group | ||||
|     group.add_argument( | ||||
|         "--autotag-files", | ||||
|         nargs="+", | ||||
|         metavar="PATH", | ||||
|         help="Specify one or more files to process", | ||||
|     ) | ||||
|     group.add_argument( | ||||
|         "--autotag-dir", metavar="PATH", help="Specify a directory to process" | ||||
|     ) | ||||
|  | ||||
|     args = parser.parse_args() | ||||
|  | ||||
|     # Determine which function to call | ||||
|     if args.autotag_files: | ||||
|         core = Core(Settings(), gui_mode=False) | ||||
|         core.autotag_files(args.autotag_files) | ||||
|         core.wait_for_completion()  # Wait for threads to finish | ||||
|     elif args.autotag_dir: | ||||
|         core = Core(Settings(), gui_mode=False) | ||||
|         core.autotag_dir(args.autotag_dir) | ||||
|         core.wait_for_completion()  # Wait for threads to finish | ||||
|     else: | ||||
|         app = ImageBrowser() | ||||
|         app.mainloop() | ||||
|  | ||||
|  | ||||
| if __name__ == "__main__": | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: Kapitanbooru Uploader 0.8.3\n" | ||||
| "Project-Id-Version: Kapitanbooru Uploader 0.9.0\n" | ||||
| "Report-Msgid-Bugs-To: kapitan@mlesniak.pl\n" | ||||
| "POT-Creation-Date: 2025-06-25 23:49+0200\n" | ||||
| "POT-Creation-Date: 2025-06-26 17:07+0200\n" | ||||
| "Content-Type: text/plain; charset=UTF-8\n" | ||||
| "Content-Transfer-Encoding: 8bit\n" | ||||
|  | ||||
| @@ -43,273 +43,56 @@ msgstr "Found auth_token:" | ||||
| msgid "auth_token not found in the HTML page." | ||||
| msgstr "auth_token not found in the HTML page." | ||||
|  | ||||
| #: ImageBrowser.py:138 | ||||
| #, python-brace-format | ||||
| msgid "Update check failed: {error}" | ||||
| msgstr "Update check failed: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:140 | ||||
| #, python-brace-format | ||||
| msgid "Malformed pyproject.toml: {error}" | ||||
| msgstr "Malformed pyproject.toml: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:143 | ||||
| #, python-brace-format | ||||
| msgid "Unexpected error during update check: {error}" | ||||
| msgstr "Unexpected error during update check: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:150 | ||||
| msgid "Update Available" | ||||
| msgstr "Update Available" | ||||
|  | ||||
| #: ImageBrowser.py:152 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "A new version {new_version} is available!\n" | ||||
| "You have version {current_version}.\n" | ||||
| "\n" | ||||
| "Update using: {update_command}" | ||||
| msgstr "" | ||||
| "A new version {new_version} is available!\n" | ||||
| "You have version {current_version}.\n" | ||||
| "\n" | ||||
| "Update using: {update_command}" | ||||
|  | ||||
| #: ImageBrowser.py:266 | ||||
| #: Core.py:204 | ||||
| msgid "Błąd przy otwieraniu pliku" | ||||
| msgstr "Error opening file" | ||||
|  | ||||
| #: ImageBrowser.py:308 | ||||
| #: Core.py:245 | ||||
| msgid "Tagger przetworzył:" | ||||
| msgstr "Tagger processed:" | ||||
|  | ||||
| #: ImageBrowser.py:311 | ||||
| #: Core.py:248 | ||||
| msgid "Błąd Taggera dla" | ||||
| msgstr "Tagger error for" | ||||
|  | ||||
| #: ImageBrowser.py:337 | ||||
| msgid "Otwórz folder" | ||||
| msgstr "Open folder" | ||||
|  | ||||
| #: ImageBrowser.py:340 ImageBrowser.py:629 ImageBrowser.py:856 | ||||
| #: ImageBrowser.py:864 | ||||
| msgid "Wyślij" | ||||
| msgstr "Send" | ||||
|  | ||||
| #: ImageBrowser.py:343 ImageBrowser.py:849 | ||||
| msgid "Wyślij wszystko" | ||||
| msgstr "Send all" | ||||
|  | ||||
| #: ImageBrowser.py:347 ImageBrowser.py:850 ImageBrowser.py:856 | ||||
| msgid "Podmień tagi" | ||||
| msgstr "Replace tags" | ||||
|  | ||||
| #: ImageBrowser.py:350 ImageBrowser.py:851 | ||||
| msgid "Otwórz post" | ||||
| msgstr "Open post" | ||||
|  | ||||
| #: ImageBrowser.py:353 | ||||
| msgid "Zakończ" | ||||
| msgstr "Finish" | ||||
|  | ||||
| #: ImageBrowser.py:355 | ||||
| msgid "Plik" | ||||
| msgstr "File" | ||||
|  | ||||
| #: ImageBrowser.py:359 ImageBrowser.py:458 | ||||
| msgid "Ustawienia" | ||||
| msgstr "Settings" | ||||
|  | ||||
| #: ImageBrowser.py:362 | ||||
| msgid "Wyczyść cache Taggera" | ||||
| msgstr "Clear Tagger cache" | ||||
|  | ||||
| #: ImageBrowser.py:365 | ||||
| msgid "Zregeneruj bazę tagów" | ||||
| msgstr "Regenerate tag database" | ||||
|  | ||||
| #: ImageBrowser.py:367 | ||||
| msgid "Opcje" | ||||
| msgstr "Options" | ||||
|  | ||||
| #: ImageBrowser.py:370 | ||||
| msgid "About" | ||||
| msgstr "About" | ||||
|  | ||||
| #: ImageBrowser.py:371 | ||||
| msgid "Help" | ||||
| msgstr "Help" | ||||
|  | ||||
| #: ImageBrowser.py:376 | ||||
| msgid "About Kapitanbooru Uploader" | ||||
| msgstr "About Kapitanbooru Uploader" | ||||
|  | ||||
| #: ImageBrowser.py:393 | ||||
| #, python-brace-format | ||||
| msgid "A new version {new_version} is available!" | ||||
| msgstr "A new version {new_version} is available!" | ||||
|  | ||||
| #: ImageBrowser.py:400 | ||||
| #, python-brace-format | ||||
| msgid "Current version: {version}" | ||||
| msgstr "Current version: {version}" | ||||
|  | ||||
| #: ImageBrowser.py:402 | ||||
| msgid "A GUI application for uploading images to KapitanBooru." | ||||
| msgstr "A GUI application for uploading images to KapitanBooru." | ||||
|  | ||||
| #: ImageBrowser.py:403 | ||||
| msgid "Features include image upload, tag management, automatic" | ||||
| msgstr "Features include image upload, tag management, automatic" | ||||
|  | ||||
| #: ImageBrowser.py:404 | ||||
| msgid "tagging with wdtagger, and cache management." | ||||
| msgstr "tagging with wdtagger, and cache management." | ||||
|  | ||||
| #: ImageBrowser.py:406 | ||||
| msgid "Authors:" | ||||
| msgstr "Authors:" | ||||
|  | ||||
| #: ImageBrowser.py:409 | ||||
| msgid "License: MIT License" | ||||
| msgstr "License: MIT License" | ||||
|  | ||||
| #: ImageBrowser.py:422 | ||||
| msgid "Repository:" | ||||
| msgstr "Repository:" | ||||
|  | ||||
| #: ImageBrowser.py:431 | ||||
| msgid "Website:" | ||||
| msgstr "Website:" | ||||
|  | ||||
| #: ImageBrowser.py:442 | ||||
| msgid "Close" | ||||
| msgstr "Close" | ||||
|  | ||||
| #: ImageBrowser.py:450 ImageBrowser.py:453 | ||||
| msgid "Cache" | ||||
| msgstr "Cache" | ||||
|  | ||||
| #: ImageBrowser.py:450 | ||||
| msgid "Cache Taggera zostało wyczyszczone." | ||||
| msgstr "Tagger cache has been cleared." | ||||
|  | ||||
| #: ImageBrowser.py:453 | ||||
| msgid "Błąd przy czyszczeniu cache:" | ||||
| msgstr "Error clearing cache:" | ||||
|  | ||||
| #: ImageBrowser.py:463 | ||||
| msgid "Login:" | ||||
| msgstr "Login:" | ||||
|  | ||||
| #: ImageBrowser.py:469 | ||||
| msgid "Hasło:" | ||||
| msgstr "Password:" | ||||
|  | ||||
| #: ImageBrowser.py:475 | ||||
| msgid "Base URL:" | ||||
| msgstr "Base URL:" | ||||
|  | ||||
| #: ImageBrowser.py:481 | ||||
| msgid "Default Tags:" | ||||
| msgstr "Default Tags:" | ||||
|  | ||||
| #: ImageBrowser.py:487 | ||||
| msgid "Browser:" | ||||
| msgstr "Browser:" | ||||
|  | ||||
| #: ImageBrowser.py:501 | ||||
| msgid "Language:" | ||||
| msgstr "Language:" | ||||
|  | ||||
| #: ImageBrowser.py:533 | ||||
| msgid "Zapisz" | ||||
| msgstr "Save" | ||||
|  | ||||
| #: ImageBrowser.py:575 | ||||
| msgid "PNG Tags" | ||||
| msgstr "PNG Tags" | ||||
|  | ||||
| #: ImageBrowser.py:587 | ||||
| msgid "Tagger Tags" | ||||
| msgstr "Tagger Tags" | ||||
|  | ||||
| #: ImageBrowser.py:601 | ||||
| msgid "Manual Tags" | ||||
| msgstr "Manual Tags" | ||||
|  | ||||
| #: ImageBrowser.py:609 | ||||
| msgid "Final Tags" | ||||
| msgstr "Final Tags" | ||||
|  | ||||
| #: ImageBrowser.py:634 | ||||
| msgid "Wyświetl" | ||||
| msgstr "Display" | ||||
|  | ||||
| #: ImageBrowser.py:651 | ||||
| msgid "Przetworzono tagi:" | ||||
| msgstr "Processed tags:" | ||||
|  | ||||
| #: ImageBrowser.py:651 ImageBrowser.py:652 ImageBrowser.py:653 | ||||
| msgid "plików" | ||||
| msgstr "files" | ||||
|  | ||||
| #: ImageBrowser.py:652 | ||||
| msgid "Zweryfikowano status uploadu:" | ||||
| msgstr "Upload status verified:" | ||||
|  | ||||
| #: ImageBrowser.py:653 | ||||
| msgid "Zuploadowano:" | ||||
| msgstr "Uploaded:" | ||||
|  | ||||
| #: ImageBrowser.py:682 | ||||
| msgid "Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF" | ||||
| msgstr "Select folder with PNG, JPEG, WebP, AVIF, and GIF images" | ||||
|  | ||||
| #: ImageBrowser.py:724 | ||||
| msgid "Informacja" | ||||
| msgstr "Information" | ||||
|  | ||||
| #: ImageBrowser.py:725 | ||||
| #, fuzzy | ||||
| msgid "Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze." | ||||
| msgstr "No PNG files in the selected folder." | ||||
|  | ||||
| #: ImageBrowser.py:808 | ||||
| msgid "Błąd podczas sprawdzania paczki uploadu:" | ||||
| msgstr "Error while checking upload package:" | ||||
|  | ||||
| #: ImageBrowser.py:884 | ||||
| #: Core.py:303 | ||||
| msgid "Error computing MD5:" | ||||
| msgstr "Error computing MD5:" | ||||
|  | ||||
| #: ImageBrowser.py:946 | ||||
| msgid "Błąd" | ||||
| msgstr "Error" | ||||
| #: Core.py:428 | ||||
| msgid "Błąd podczas sprawdzania paczki uploadu:" | ||||
| msgstr "Error while checking upload package:" | ||||
|  | ||||
| #: ImageBrowser.py:946 | ||||
| msgid "Nie można załadować obrazka:" | ||||
| msgstr "Unable to load image:" | ||||
| #: Core.py:437 | ||||
| msgid "Plik nie istnieje:" | ||||
| msgstr "File does not exist:" | ||||
|  | ||||
| #: ImageBrowser.py:1156 ImageBrowser.py:1166 | ||||
| #, python-brace-format | ||||
| msgid "Warning: Tag '{tag}' not found in implication graph" | ||||
| msgstr "Warning: Tag '{tag}' not found in implication graph" | ||||
| #: Core.py:443 | ||||
| msgid "Tagi dla pliku" | ||||
| msgstr "Tags for file" | ||||
|  | ||||
| #: ImageBrowser.py:1396 | ||||
| msgid "Tagger przetwarza..." | ||||
| msgstr "Tagger processing..." | ||||
| #: Core.py:452 | ||||
| msgid "Błąd podczas autotagowania pliku" | ||||
| msgstr "Error during file autotagowania" | ||||
|  | ||||
| #: ImageBrowser.py:1421 | ||||
| #: Core.py:459 | ||||
| msgid "Podana ścieżka nie jest katalogiem:" | ||||
| msgstr "Given path is not a directory:" | ||||
|  | ||||
| #: Core.py:467 | ||||
| msgid "Brak obrazów do przetworzenia w katalogu:" | ||||
| msgstr "No images to process in directory:" | ||||
|  | ||||
| #: Core.py:490 | ||||
| #, python-brace-format | ||||
| msgid "Wysyłam plik {base_file_name}..." | ||||
| msgstr "Sending file {base_file_name}..." | ||||
| msgstr "Uploading file {base_file_name}..." | ||||
|  | ||||
| #: ImageBrowser.py:1462 | ||||
| #: Core.py:525 | ||||
| msgid "Wysyłanie zakończone powodzeniem!" | ||||
| msgstr "Upload completed successfully!" | ||||
|  | ||||
| #: ImageBrowser.py:1466 ImageBrowser.py:1475 | ||||
| #: Core.py:529 Core.py:538 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "Wysyłanie zakończone błędem.\n" | ||||
| @@ -320,40 +103,52 @@ msgstr "" | ||||
| "Status: {status_code}\n" | ||||
| "Content: {text}" | ||||
|  | ||||
| #: ImageBrowser.py:1481 ImageBrowser.py:1484 | ||||
| #: Core.py:547 Core.py:553 ImageBrowser.py:654 ImageBrowser.py:657 | ||||
| #: ImageBrowser.py:1177 ImageBrowser.py:1180 | ||||
| msgid "Wysyłanie" | ||||
| msgstr "Uploading" | ||||
|  | ||||
| #: ImageBrowser.py:1497 | ||||
| msgid "Błąd wysyłania" | ||||
| #: Core.py:565 | ||||
| msgid "Błąd wysyłania pliku" | ||||
| msgstr "Upload error" | ||||
|  | ||||
| #: ImageBrowser.py:1517 | ||||
| msgid "Błąd edycji" | ||||
| msgstr "Edit error" | ||||
| #: Core.py:595 | ||||
| msgid "Anulowano operację!" | ||||
| msgstr "Operation cancelled!" | ||||
|  | ||||
| #: ImageBrowser.py:1517 | ||||
| #: Core.py:605 | ||||
| #, 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}" | ||||
|  | ||||
| #: Core.py:624 | ||||
| msgid "Przesłano pliki!" | ||||
| msgstr "Files have been uploaded!" | ||||
|  | ||||
| #: Core.py:645 Core.py:649 | ||||
| msgid "Post nie został znaleziony dla tego pliku" | ||||
| msgstr "Post not found for this file" | ||||
|  | ||||
| #: ImageBrowser.py:1527 | ||||
| #: Core.py:660 | ||||
| #, python-brace-format | ||||
| msgid "Aktualizuję tagi dla {base_file_name}..." | ||||
| msgstr "Updating tags for {base_file_name}..." | ||||
|  | ||||
| #: ImageBrowser.py:1537 ImageBrowser.py:1547 ImageBrowser.py:1578 | ||||
| #: Core.py:670 Core.py:680 Core.py:705 | ||||
| msgid "Operacja anulowana" | ||||
| msgstr "Operation cancelled" | ||||
|  | ||||
| #: ImageBrowser.py:1588 | ||||
| #: Core.py:715 | ||||
| msgid "Tagi zostały zaktualizowane!" | ||||
| msgstr "Tags have been updated!" | ||||
|  | ||||
| #: ImageBrowser.py:1590 | ||||
| #: Core.py:720 ImageBrowser.py:719 | ||||
| msgid "Sukces edycji" | ||||
| msgstr "Edit successful" | ||||
|  | ||||
| #: ImageBrowser.py:1596 | ||||
| #: Core.py:727 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "Błąd podczas aktualizacji tagów\n" | ||||
| @@ -362,19 +157,263 @@ msgstr "" | ||||
| "Error updating tags\n" | ||||
| "Status: {code}" | ||||
|  | ||||
| #: ImageBrowser.py:1600 | ||||
| #: Core.py:731 | ||||
| msgid "Treść:" | ||||
| msgstr "Content:" | ||||
|  | ||||
| #: ImageBrowser.py:1604 | ||||
| #: Core.py:735 ImageBrowser.py:716 | ||||
| msgid "Błąd edycji" | ||||
| msgstr "Edit error" | ||||
|  | ||||
| #: Core.py:741 | ||||
| msgid "Krytyczny błąd edycji" | ||||
| msgstr "Critical edit error" | ||||
|  | ||||
| #: ImageBrowser.py:1616 | ||||
| #: ImageBrowser.py:112 | ||||
| #, python-brace-format | ||||
| msgid "Update check failed: {error}" | ||||
| msgstr "Update check failed: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:114 | ||||
| #, python-brace-format | ||||
| msgid "Malformed pyproject.toml: {error}" | ||||
| msgstr "Malformed pyproject.toml: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:117 | ||||
| #, python-brace-format | ||||
| msgid "Unexpected error during update check: {error}" | ||||
| msgstr "Unexpected error during update check: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:124 | ||||
| msgid "Update Available" | ||||
| msgstr "Update Available" | ||||
|  | ||||
| #: ImageBrowser.py:126 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "A new version {new_version} is available!\n" | ||||
| "You have version {current_version}.\n" | ||||
| "\n" | ||||
| "Update using: {update_command}" | ||||
| msgstr "" | ||||
| "A new version {new_version} is available!\n" | ||||
| "You have version {current_version}.\n" | ||||
| "\n" | ||||
| "Update using: {update_command}" | ||||
|  | ||||
| #: ImageBrowser.py:183 | ||||
| msgid "Otwórz folder" | ||||
| msgstr "Open folder" | ||||
|  | ||||
| #: ImageBrowser.py:186 ImageBrowser.py:484 ImageBrowser.py:601 | ||||
| #: ImageBrowser.py:609 | ||||
| msgid "Wyślij" | ||||
| msgstr "Upload" | ||||
|  | ||||
| #: ImageBrowser.py:189 ImageBrowser.py:594 | ||||
| msgid "Wyślij wszystko" | ||||
| msgstr "Upload all" | ||||
|  | ||||
| #: ImageBrowser.py:193 ImageBrowser.py:595 ImageBrowser.py:601 | ||||
| msgid "Podmień tagi" | ||||
| msgstr "Replace tags" | ||||
|  | ||||
| #: ImageBrowser.py:196 ImageBrowser.py:596 | ||||
| msgid "Otwórz post" | ||||
| msgstr "Open post" | ||||
|  | ||||
| #: ImageBrowser.py:199 | ||||
| msgid "Zakończ" | ||||
| msgstr "Finish" | ||||
|  | ||||
| #: ImageBrowser.py:201 | ||||
| msgid "Plik" | ||||
| msgstr "File" | ||||
|  | ||||
| #: ImageBrowser.py:205 ImageBrowser.py:306 | ||||
| msgid "Ustawienia" | ||||
| msgstr "Settings" | ||||
|  | ||||
| #: ImageBrowser.py:208 | ||||
| msgid "Wyczyść cache Taggera" | ||||
| msgstr "Clear Tagger cache" | ||||
|  | ||||
| #: ImageBrowser.py:211 | ||||
| msgid "Zregeneruj bazę tagów" | ||||
| msgstr "Regenerate tag database" | ||||
|  | ||||
| #: ImageBrowser.py:213 | ||||
| msgid "Opcje" | ||||
| msgstr "Options" | ||||
|  | ||||
| #: ImageBrowser.py:216 | ||||
| msgid "About" | ||||
| msgstr "About" | ||||
|  | ||||
| #: ImageBrowser.py:217 | ||||
| msgid "Help" | ||||
| msgstr "Help" | ||||
|  | ||||
| #: ImageBrowser.py:222 | ||||
| msgid "About Kapitanbooru Uploader" | ||||
| msgstr "About Kapitanbooru Uploader" | ||||
|  | ||||
| #: ImageBrowser.py:239 | ||||
| #, python-brace-format | ||||
| msgid "A new version {new_version} is available!" | ||||
| msgstr "A new version {new_version} is available!" | ||||
|  | ||||
| #: ImageBrowser.py:246 | ||||
| #, python-brace-format | ||||
| msgid "Current version: {version}" | ||||
| msgstr "Current version: {version}" | ||||
|  | ||||
| #: ImageBrowser.py:248 | ||||
| msgid "A GUI application for uploading images to KapitanBooru." | ||||
| msgstr "A GUI application for uploading images to KapitanBooru." | ||||
|  | ||||
| #: ImageBrowser.py:249 | ||||
| msgid "Features include image upload, tag management, automatic" | ||||
| msgstr "Features include image upload, tag management, automatic" | ||||
|  | ||||
| #: ImageBrowser.py:250 | ||||
| msgid "tagging with wdtagger, and cache management." | ||||
| msgstr "tagging with wdtagger, and cache management." | ||||
|  | ||||
| #: ImageBrowser.py:252 | ||||
| msgid "Authors:" | ||||
| msgstr "Authors:" | ||||
|  | ||||
| #: ImageBrowser.py:255 | ||||
| msgid "License: MIT License" | ||||
| msgstr "License: MIT License" | ||||
|  | ||||
| #: ImageBrowser.py:268 | ||||
| msgid "Repository:" | ||||
| msgstr "Repository:" | ||||
|  | ||||
| #: ImageBrowser.py:277 | ||||
| msgid "Website:" | ||||
| msgstr "Website:" | ||||
|  | ||||
| #: ImageBrowser.py:288 | ||||
| msgid "Close" | ||||
| msgstr "Close" | ||||
|  | ||||
| #: ImageBrowser.py:298 ImageBrowser.py:301 | ||||
| msgid "Cache" | ||||
| msgstr "Cache" | ||||
|  | ||||
| #: ImageBrowser.py:298 | ||||
| msgid "Cache Taggera zostało wyczyszczone." | ||||
| msgstr "Tagger cache has been cleared." | ||||
|  | ||||
| #: ImageBrowser.py:301 | ||||
| msgid "Błąd przy czyszczeniu cache:" | ||||
| msgstr "Error clearing cache:" | ||||
|  | ||||
| #: ImageBrowser.py:311 | ||||
| msgid "Login:" | ||||
| msgstr "Login:" | ||||
|  | ||||
| #: ImageBrowser.py:317 | ||||
| msgid "Hasło:" | ||||
| msgstr "Password:" | ||||
|  | ||||
| #: ImageBrowser.py:323 | ||||
| msgid "Base URL:" | ||||
| msgstr "Base URL:" | ||||
|  | ||||
| #: ImageBrowser.py:329 | ||||
| msgid "Default Tags:" | ||||
| msgstr "Default Tags:" | ||||
|  | ||||
| #: ImageBrowser.py:335 | ||||
| msgid "Browser:" | ||||
| msgstr "Browser:" | ||||
|  | ||||
| #: ImageBrowser.py:349 | ||||
| msgid "Language:" | ||||
| msgstr "Language:" | ||||
|  | ||||
| #: ImageBrowser.py:385 | ||||
| msgid "Zapisz" | ||||
| msgstr "Save" | ||||
|  | ||||
| #: ImageBrowser.py:427 | ||||
| msgid "PNG Tags" | ||||
| msgstr "PNG Tags" | ||||
|  | ||||
| #: ImageBrowser.py:439 | ||||
| msgid "Tagger Tags" | ||||
| msgstr "Tagger Tags" | ||||
|  | ||||
| #: ImageBrowser.py:453 | ||||
| msgid "Manual Tags" | ||||
| msgstr "Manual Tags" | ||||
|  | ||||
| #: ImageBrowser.py:464 | ||||
| msgid "Final Tags" | ||||
| msgstr "Final Tags" | ||||
|  | ||||
| #: ImageBrowser.py:489 | ||||
| msgid "Wyświetl" | ||||
| msgstr "Display" | ||||
|  | ||||
| #: ImageBrowser.py:506 | ||||
| msgid "Przetworzono tagi:" | ||||
| msgstr "Processed tags:" | ||||
|  | ||||
| #: ImageBrowser.py:506 ImageBrowser.py:507 ImageBrowser.py:508 | ||||
| msgid "plików" | ||||
| msgstr "files" | ||||
|  | ||||
| #: ImageBrowser.py:507 | ||||
| msgid "Zweryfikowano status uploadu:" | ||||
| msgstr "Upload status verified:" | ||||
|  | ||||
| #: ImageBrowser.py:508 | ||||
| msgid "Zuploadowano:" | ||||
| msgstr "Uploaded:" | ||||
|  | ||||
| #: ImageBrowser.py:537 | ||||
| msgid "Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF" | ||||
| msgstr "Select folder with PNG, JPEG, WebP, AVIF, and GIF images" | ||||
|  | ||||
| #: ImageBrowser.py:552 | ||||
| msgid "Informacja" | ||||
| msgstr "Information" | ||||
|  | ||||
| #: ImageBrowser.py:553 | ||||
| msgid "Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze." | ||||
| msgstr "No PNG, JPEG, WebP, AVIF or GIF files in the selected folder." | ||||
|  | ||||
| #: ImageBrowser.py:660 ImageBrowser.py:1183 | ||||
| msgid "Błąd wysyłania" | ||||
| msgstr "Upload error" | ||||
|  | ||||
| #: ImageBrowser.py:695 | ||||
| msgid "Błąd" | ||||
| msgstr "Error" | ||||
|  | ||||
| #: ImageBrowser.py:695 | ||||
| msgid "Nie można załadować obrazka:" | ||||
| msgstr "Unable to load image:" | ||||
|  | ||||
| #: ImageBrowser.py:923 ImageBrowser.py:935 | ||||
| #, python-brace-format | ||||
| msgid "Warning: Tag '{tag}' not found in implication graph" | ||||
| msgstr "Warning: Tag '{tag}' not found in implication graph" | ||||
|  | ||||
| #: ImageBrowser.py:1144 | ||||
| msgid "Tagger przetwarza..." | ||||
| msgstr "Tagger processing..." | ||||
|  | ||||
| #: ImageBrowser.py:1161 | ||||
| msgid "Potwierdzenie" | ||||
| msgstr "Confirmation" | ||||
|  | ||||
| #: ImageBrowser.py:1618 | ||||
| #: ImageBrowser.py:1163 | ||||
| msgid "" | ||||
| "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" | ||||
| "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" | ||||
| @@ -384,21 +423,6 @@ msgstr "" | ||||
| "Each will be tagged with 'meta:auto_upload'.\n" | ||||
| "Make sure the tags are correct!" | ||||
|  | ||||
| #: ImageBrowser.py:1642 | ||||
| msgid "Anulowano operację!" | ||||
| msgstr "Operation cancelled!" | ||||
|  | ||||
| #: ImageBrowser.py:1650 | ||||
| #, 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:1666 | ||||
| msgid "Przesłano pliki!" | ||||
| msgstr "Files have been uploaded!" | ||||
|  | ||||
| #: ProcessingDialog.py:15 | ||||
| msgid "Processing..." | ||||
| msgstr "Processing..." | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| msgid "" | ||||
| msgstr "" | ||||
| "Project-Id-Version: Kapitanbooru Uploader 0.8.3\n" | ||||
| "Project-Id-Version: Kapitanbooru Uploader 0.9.0\n" | ||||
| "Report-Msgid-Bugs-To: kapitan@mlesniak.pl\n" | ||||
| "POT-Creation-Date: 2025-06-25 23:49+0200\n" | ||||
| "POT-Creation-Date: 2025-06-26 17:07+0200\n" | ||||
| "Language: pl\n" | ||||
| "Content-Type: text/plain; charset=UTF-8\n" | ||||
| "Content-Transfer-Encoding: 8bit\n" | ||||
| @@ -45,26 +45,152 @@ msgstr "Znaleziono auth_token:" | ||||
| msgid "auth_token not found in the HTML page." | ||||
| msgstr "Nie znaleziono auth_token w stronie HTML." | ||||
|  | ||||
| #: ImageBrowser.py:138 | ||||
| #: Core.py:204 | ||||
| msgid "Błąd przy otwieraniu pliku" | ||||
| msgstr "Błąd przy otwieraniu pliku" | ||||
|  | ||||
| #: Core.py:245 | ||||
| msgid "Tagger przetworzył:" | ||||
| msgstr "Tagger przetworzył:" | ||||
|  | ||||
| #: Core.py:248 | ||||
| msgid "Błąd Taggera dla" | ||||
| msgstr "Błąd Taggera dla" | ||||
|  | ||||
| #: Core.py:303 | ||||
| msgid "Error computing MD5:" | ||||
| msgstr "Błąd przy obliczaniu MD5:" | ||||
|  | ||||
| #: Core.py:428 | ||||
| msgid "Błąd podczas sprawdzania paczki uploadu:" | ||||
| msgstr "Błąd podczas sprawdzania paczki uploadu:" | ||||
|  | ||||
| #: Core.py:437 | ||||
| msgid "Plik nie istnieje:" | ||||
| msgstr "" | ||||
|  | ||||
| #: Core.py:443 | ||||
| msgid "Tagi dla pliku" | ||||
| msgstr "" | ||||
|  | ||||
| #: Core.py:452 | ||||
| msgid "Błąd podczas autotagowania pliku" | ||||
| msgstr "Błąd podczas autotagowania pliku" | ||||
|  | ||||
| #: Core.py:459 | ||||
| msgid "Podana ścieżka nie jest katalogiem:" | ||||
| msgstr "" | ||||
|  | ||||
| #: Core.py:467 | ||||
| msgid "Brak obrazów do przetworzenia w katalogu:" | ||||
| msgstr "" | ||||
|  | ||||
| #: Core.py:490 | ||||
| #, python-brace-format | ||||
| msgid "Wysyłam plik {base_file_name}..." | ||||
| msgstr "Wysyłam plik {base_file_name}..." | ||||
|  | ||||
| #: Core.py:525 | ||||
| msgid "Wysyłanie zakończone powodzeniem!" | ||||
| msgstr "Wysyłanie zakończone powodzeniem!" | ||||
|  | ||||
| #: Core.py:529 Core.py:538 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "Wysyłanie zakończone błędem.\n" | ||||
| "Status: {status_code}\n" | ||||
| "Treść: {text}" | ||||
| msgstr "" | ||||
| "Wysyłanie zakończone błędem.\n" | ||||
| "Status: {status_code}\n" | ||||
| "Treść: {text}" | ||||
|  | ||||
| #: Core.py:547 Core.py:553 ImageBrowser.py:654 ImageBrowser.py:657 | ||||
| #: ImageBrowser.py:1177 ImageBrowser.py:1180 | ||||
| msgid "Wysyłanie" | ||||
| msgstr "Wysyłanie" | ||||
|  | ||||
| #: Core.py:565 | ||||
| msgid "Błąd wysyłania pliku" | ||||
| msgstr "Błąd wysyłania pliku" | ||||
|  | ||||
| #: Core.py:595 | ||||
| msgid "Anulowano operację!" | ||||
| msgstr "Operacja anulowana!" | ||||
|  | ||||
| #: Core.py:605 | ||||
| #, 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}" | ||||
|  | ||||
| #: Core.py:624 | ||||
| msgid "Przesłano pliki!" | ||||
| msgstr "Pliki zostały przesłane!" | ||||
|  | ||||
| #: Core.py:645 Core.py:649 | ||||
| msgid "Post nie został znaleziony dla tego pliku" | ||||
| msgstr "Post nie został znaleziony dla tego pliku" | ||||
|  | ||||
| #: Core.py:660 | ||||
| #, python-brace-format | ||||
| msgid "Aktualizuję tagi dla {base_file_name}..." | ||||
| msgstr "Aktualizuję tagi dla {base_file_name}..." | ||||
|  | ||||
| #: Core.py:670 Core.py:680 Core.py:705 | ||||
| msgid "Operacja anulowana" | ||||
| msgstr "Operacja anulowana" | ||||
|  | ||||
| #: Core.py:715 | ||||
| msgid "Tagi zostały zaktualizowane!" | ||||
| msgstr "Tagi zostały zaktualizowane!" | ||||
|  | ||||
| #: Core.py:720 ImageBrowser.py:719 | ||||
| msgid "Sukces edycji" | ||||
| msgstr "Sukces edycji" | ||||
|  | ||||
| #: Core.py:727 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "Błąd podczas aktualizacji tagów\n" | ||||
| "Status: {code}" | ||||
| msgstr "" | ||||
| "Błąd podczas aktualizacji tagów\n" | ||||
| "Status: {code}" | ||||
|  | ||||
| #: Core.py:731 | ||||
| msgid "Treść:" | ||||
| msgstr "Treść:" | ||||
|  | ||||
| #: Core.py:735 ImageBrowser.py:716 | ||||
| msgid "Błąd edycji" | ||||
| msgstr "Błąd edycji" | ||||
|  | ||||
| #: Core.py:741 | ||||
| msgid "Krytyczny błąd edycji" | ||||
| msgstr "Krytyczny błąd edycji" | ||||
|  | ||||
| #: ImageBrowser.py:112 | ||||
| #, python-brace-format | ||||
| msgid "Update check failed: {error}" | ||||
| msgstr "Sprawdzenie aktualizacji nie powiodło się: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:140 | ||||
| #: ImageBrowser.py:114 | ||||
| #, python-brace-format | ||||
| msgid "Malformed pyproject.toml: {error}" | ||||
| msgstr "Nieprawidłowy plik pyproject.toml: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:143 | ||||
| #: ImageBrowser.py:117 | ||||
| #, python-brace-format | ||||
| msgid "Unexpected error during update check: {error}" | ||||
| msgstr "Nieoczekiwany błąd podczas sprawdzania aktualizacji: {error}" | ||||
|  | ||||
| #: ImageBrowser.py:150 | ||||
| #: ImageBrowser.py:124 | ||||
| msgid "Update Available" | ||||
| msgstr "Aktualizacja dostępna" | ||||
|  | ||||
| #: ImageBrowser.py:152 | ||||
| #: ImageBrowser.py:126 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "A new version {new_version} is available!\n" | ||||
| @@ -77,307 +203,219 @@ msgstr "" | ||||
| "\n" | ||||
| "Aktualizuj za pomocą: {update_command}" | ||||
|  | ||||
| #: ImageBrowser.py:266 | ||||
| msgid "Błąd przy otwieraniu pliku" | ||||
| msgstr "Błąd przy otwieraniu pliku" | ||||
|  | ||||
| #: ImageBrowser.py:308 | ||||
| msgid "Tagger przetworzył:" | ||||
| msgstr "Tagger przetworzył:" | ||||
|  | ||||
| #: ImageBrowser.py:311 | ||||
| msgid "Błąd Taggera dla" | ||||
| msgstr "Błąd Taggera dla" | ||||
|  | ||||
| #: ImageBrowser.py:337 | ||||
| #: ImageBrowser.py:183 | ||||
| msgid "Otwórz folder" | ||||
| msgstr "Otwórz folder" | ||||
|  | ||||
| #: ImageBrowser.py:340 ImageBrowser.py:629 ImageBrowser.py:856 | ||||
| #: ImageBrowser.py:864 | ||||
| #: ImageBrowser.py:186 ImageBrowser.py:484 ImageBrowser.py:601 | ||||
| #: ImageBrowser.py:609 | ||||
| msgid "Wyślij" | ||||
| msgstr "Wyślij" | ||||
|  | ||||
| #: ImageBrowser.py:343 ImageBrowser.py:849 | ||||
| #: ImageBrowser.py:189 ImageBrowser.py:594 | ||||
| msgid "Wyślij wszystko" | ||||
| msgstr "Wyślij wszystko" | ||||
|  | ||||
| #: ImageBrowser.py:347 ImageBrowser.py:850 ImageBrowser.py:856 | ||||
| #: ImageBrowser.py:193 ImageBrowser.py:595 ImageBrowser.py:601 | ||||
| msgid "Podmień tagi" | ||||
| msgstr "Podmień tagi" | ||||
|  | ||||
| #: ImageBrowser.py:350 ImageBrowser.py:851 | ||||
| #: ImageBrowser.py:196 ImageBrowser.py:596 | ||||
| msgid "Otwórz post" | ||||
| msgstr "Otwórz post" | ||||
|  | ||||
| #: ImageBrowser.py:353 | ||||
| #: ImageBrowser.py:199 | ||||
| msgid "Zakończ" | ||||
| msgstr "Zakończ" | ||||
|  | ||||
| #: ImageBrowser.py:355 | ||||
| #: ImageBrowser.py:201 | ||||
| msgid "Plik" | ||||
| msgstr "Plik" | ||||
|  | ||||
| #: ImageBrowser.py:359 ImageBrowser.py:458 | ||||
| #: ImageBrowser.py:205 ImageBrowser.py:306 | ||||
| msgid "Ustawienia" | ||||
| msgstr "Ustawienia" | ||||
|  | ||||
| #: ImageBrowser.py:362 | ||||
| #: ImageBrowser.py:208 | ||||
| msgid "Wyczyść cache Taggera" | ||||
| msgstr "Wyczyść cache Taggera" | ||||
|  | ||||
| #: ImageBrowser.py:365 | ||||
| #: ImageBrowser.py:211 | ||||
| msgid "Zregeneruj bazę tagów" | ||||
| msgstr "Zregeneruj bazę tagów" | ||||
|  | ||||
| #: ImageBrowser.py:367 | ||||
| #: ImageBrowser.py:213 | ||||
| msgid "Opcje" | ||||
| msgstr "Opcje" | ||||
|  | ||||
| #: ImageBrowser.py:370 | ||||
| #: ImageBrowser.py:216 | ||||
| msgid "About" | ||||
| msgstr "O programie" | ||||
|  | ||||
| #: ImageBrowser.py:371 | ||||
| #: ImageBrowser.py:217 | ||||
| msgid "Help" | ||||
| msgstr "Pomoc" | ||||
|  | ||||
| #: ImageBrowser.py:376 | ||||
| #: ImageBrowser.py:222 | ||||
| msgid "About Kapitanbooru Uploader" | ||||
| msgstr "O programie Kapitanbooru Uploader" | ||||
|  | ||||
| #: ImageBrowser.py:393 | ||||
| #: ImageBrowser.py:239 | ||||
| #, python-brace-format | ||||
| msgid "A new version {new_version} is available!" | ||||
| msgstr "Dostępna jest nowa wersja {new_version}!" | ||||
|  | ||||
| #: ImageBrowser.py:400 | ||||
| #: ImageBrowser.py:246 | ||||
| #, python-brace-format | ||||
| msgid "Current version: {version}" | ||||
| msgstr "Obecna wersja: {version}" | ||||
|  | ||||
| #: ImageBrowser.py:402 | ||||
| #: ImageBrowser.py:248 | ||||
| msgid "A GUI application for uploading images to KapitanBooru." | ||||
| msgstr "Aplikacja GUI do przesyłania obrazów do KapitanBooru." | ||||
|  | ||||
| #: ImageBrowser.py:403 | ||||
| #: ImageBrowser.py:249 | ||||
| msgid "Features include image upload, tag management, automatic" | ||||
| msgstr "Funkcje obejmują przesyłanie obrazów, zarządzanie tagami, automatyczne" | ||||
|  | ||||
| #: ImageBrowser.py:404 | ||||
| #: ImageBrowser.py:250 | ||||
| msgid "tagging with wdtagger, and cache management." | ||||
| msgstr "tagowanie za pomocą wdtagger oraz zarządzanie cache." | ||||
|  | ||||
| #: ImageBrowser.py:406 | ||||
| #: ImageBrowser.py:252 | ||||
| msgid "Authors:" | ||||
| msgstr "Autorzy:" | ||||
|  | ||||
| #: ImageBrowser.py:409 | ||||
| #: ImageBrowser.py:255 | ||||
| msgid "License: MIT License" | ||||
| msgstr "Licencja: MIT License" | ||||
|  | ||||
| #: ImageBrowser.py:422 | ||||
| #: ImageBrowser.py:268 | ||||
| msgid "Repository:" | ||||
| msgstr "Repozytorium:" | ||||
|  | ||||
| #: ImageBrowser.py:431 | ||||
| #: ImageBrowser.py:277 | ||||
| msgid "Website:" | ||||
| msgstr "Strona internetowa:" | ||||
|  | ||||
| #: ImageBrowser.py:442 | ||||
| #: ImageBrowser.py:288 | ||||
| msgid "Close" | ||||
| msgstr "Zamknij" | ||||
|  | ||||
| #: ImageBrowser.py:450 ImageBrowser.py:453 | ||||
| #: ImageBrowser.py:298 ImageBrowser.py:301 | ||||
| msgid "Cache" | ||||
| msgstr "Cache" | ||||
|  | ||||
| #: ImageBrowser.py:450 | ||||
| #: ImageBrowser.py:298 | ||||
| msgid "Cache Taggera zostało wyczyszczone." | ||||
| msgstr "Cache Taggera zostało wyczyszczone." | ||||
|  | ||||
| #: ImageBrowser.py:453 | ||||
| #: ImageBrowser.py:301 | ||||
| msgid "Błąd przy czyszczeniu cache:" | ||||
| msgstr "Błąd przy czyszczeniu cache:" | ||||
|  | ||||
| #: ImageBrowser.py:463 | ||||
| #: ImageBrowser.py:311 | ||||
| msgid "Login:" | ||||
| msgstr "Login:" | ||||
|  | ||||
| #: ImageBrowser.py:469 | ||||
| #: ImageBrowser.py:317 | ||||
| msgid "Hasło:" | ||||
| msgstr "Hasło:" | ||||
|  | ||||
| #: ImageBrowser.py:475 | ||||
| #: ImageBrowser.py:323 | ||||
| msgid "Base URL:" | ||||
| msgstr "Base URL:" | ||||
|  | ||||
| #: ImageBrowser.py:481 | ||||
| #: ImageBrowser.py:329 | ||||
| msgid "Default Tags:" | ||||
| msgstr "Domyślne tagi:" | ||||
|  | ||||
| #: ImageBrowser.py:487 | ||||
| #: ImageBrowser.py:335 | ||||
| msgid "Browser:" | ||||
| msgstr "Przeglądarka:" | ||||
|  | ||||
| #: ImageBrowser.py:501 | ||||
| #: ImageBrowser.py:349 | ||||
| msgid "Language:" | ||||
| msgstr "Język:" | ||||
|  | ||||
| #: ImageBrowser.py:533 | ||||
| #: ImageBrowser.py:385 | ||||
| msgid "Zapisz" | ||||
| msgstr "Zapisz" | ||||
|  | ||||
| #: ImageBrowser.py:575 | ||||
| #: ImageBrowser.py:427 | ||||
| msgid "PNG Tags" | ||||
| msgstr "Tagi PNG" | ||||
|  | ||||
| #: ImageBrowser.py:587 | ||||
| #: ImageBrowser.py:439 | ||||
| msgid "Tagger Tags" | ||||
| msgstr "Tagi Taggera" | ||||
|  | ||||
| #: ImageBrowser.py:601 | ||||
| #: ImageBrowser.py:453 | ||||
| msgid "Manual Tags" | ||||
| msgstr "Tagi ręczne" | ||||
|  | ||||
| #: ImageBrowser.py:609 | ||||
| #: ImageBrowser.py:464 | ||||
| msgid "Final Tags" | ||||
| msgstr "Ostateczne tagi" | ||||
|  | ||||
| #: ImageBrowser.py:634 | ||||
| #: ImageBrowser.py:489 | ||||
| msgid "Wyświetl" | ||||
| msgstr "Wyświetl" | ||||
|  | ||||
| #: ImageBrowser.py:651 | ||||
| #: ImageBrowser.py:506 | ||||
| msgid "Przetworzono tagi:" | ||||
| msgstr "Przetworzono tagi:" | ||||
|  | ||||
| #: ImageBrowser.py:651 ImageBrowser.py:652 ImageBrowser.py:653 | ||||
| #: ImageBrowser.py:506 ImageBrowser.py:507 ImageBrowser.py:508 | ||||
| msgid "plików" | ||||
| msgstr "plików" | ||||
|  | ||||
| #: ImageBrowser.py:652 | ||||
| #: ImageBrowser.py:507 | ||||
| msgid "Zweryfikowano status uploadu:" | ||||
| msgstr "Zweryfikowano status uploadu:" | ||||
|  | ||||
| #: ImageBrowser.py:653 | ||||
| #: ImageBrowser.py:508 | ||||
| msgid "Zuploadowano:" | ||||
| msgstr "Zuploadowano:" | ||||
|  | ||||
| #: ImageBrowser.py:682 | ||||
| #, fuzzy | ||||
| #: ImageBrowser.py:537 | ||||
| msgid "Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF" | ||||
| msgstr "Wybierz folder z obrazami PNG" | ||||
| msgstr "Wybierz folder z obrazkami PNG, JPEG, WebP, AVIF i GIF" | ||||
|  | ||||
| #: ImageBrowser.py:724 | ||||
| #: ImageBrowser.py:552 | ||||
| msgid "Informacja" | ||||
| msgstr "Informacja" | ||||
|  | ||||
| #: ImageBrowser.py:725 | ||||
| #, fuzzy | ||||
| #: ImageBrowser.py:553 | ||||
| msgid "Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze." | ||||
| msgstr "Brak plików PNG w wybranym folderze." | ||||
| msgstr "Brak plików PNG, JPEG, WebP, AVIF lub GIF w wybranym folderze." | ||||
|  | ||||
| #: ImageBrowser.py:808 | ||||
| msgid "Błąd podczas sprawdzania paczki uploadu:" | ||||
| msgstr "Błąd podczas sprawdzania paczki uploadu:" | ||||
| #: ImageBrowser.py:660 ImageBrowser.py:1183 | ||||
| msgid "Błąd wysyłania" | ||||
| msgstr "Błąd wysyłania" | ||||
|  | ||||
| #: ImageBrowser.py:884 | ||||
| msgid "Error computing MD5:" | ||||
| msgstr "Błąd przy obliczaniu MD5:" | ||||
|  | ||||
| #: ImageBrowser.py:946 | ||||
| #: ImageBrowser.py:695 | ||||
| msgid "Błąd" | ||||
| msgstr "Błąd" | ||||
|  | ||||
| #: ImageBrowser.py:946 | ||||
| #: ImageBrowser.py:695 | ||||
| msgid "Nie można załadować obrazka:" | ||||
| msgstr "Nie można załadować obrazka:" | ||||
|  | ||||
| #: ImageBrowser.py:1156 ImageBrowser.py:1166 | ||||
| #: ImageBrowser.py:923 ImageBrowser.py:935 | ||||
| #, 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:1396 | ||||
| #: ImageBrowser.py:1144 | ||||
| msgid "Tagger przetwarza..." | ||||
| msgstr "Tagger przetwarza..." | ||||
|  | ||||
| #: ImageBrowser.py:1421 | ||||
| #, python-brace-format | ||||
| msgid "Wysyłam plik {base_file_name}..." | ||||
| msgstr "Wysyłam plik {base_file_name}..." | ||||
|  | ||||
| #: ImageBrowser.py:1462 | ||||
| msgid "Wysyłanie zakończone powodzeniem!" | ||||
| msgstr "Wysyłanie zakończone powodzeniem!" | ||||
|  | ||||
| #: ImageBrowser.py:1466 ImageBrowser.py:1475 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "Wysyłanie zakończone błędem.\n" | ||||
| "Status: {status_code}\n" | ||||
| "Treść: {text}" | ||||
| msgstr "" | ||||
| "Wysyłanie zakończone błędem.\n" | ||||
| "Status: {status_code}\n" | ||||
| "Treść: {text}" | ||||
|  | ||||
| #: ImageBrowser.py:1481 ImageBrowser.py:1484 | ||||
| msgid "Wysyłanie" | ||||
| msgstr "Wysyłanie" | ||||
|  | ||||
| #: ImageBrowser.py:1497 | ||||
| msgid "Błąd wysyłania" | ||||
| msgstr "Błąd wysyłania" | ||||
|  | ||||
| #: ImageBrowser.py:1517 | ||||
| msgid "Błąd edycji" | ||||
| msgstr "Błąd edycji" | ||||
|  | ||||
| #: ImageBrowser.py:1517 | ||||
| msgid "Post nie został znaleziony dla tego pliku" | ||||
| msgstr "Post nie został znaleziony dla tego pliku" | ||||
|  | ||||
| #: ImageBrowser.py:1527 | ||||
| #, python-brace-format | ||||
| msgid "Aktualizuję tagi dla {base_file_name}..." | ||||
| msgstr "Aktualizuję tagi dla {base_file_name}..." | ||||
|  | ||||
| #: ImageBrowser.py:1537 ImageBrowser.py:1547 ImageBrowser.py:1578 | ||||
| msgid "Operacja anulowana" | ||||
| msgstr "Operacja anulowana" | ||||
|  | ||||
| #: ImageBrowser.py:1588 | ||||
| msgid "Tagi zostały zaktualizowane!" | ||||
| msgstr "Tagi zostały zaktualizowane!" | ||||
|  | ||||
| #: ImageBrowser.py:1590 | ||||
| msgid "Sukces edycji" | ||||
| msgstr "Sukces edycji" | ||||
|  | ||||
| #: ImageBrowser.py:1596 | ||||
| #, python-brace-format | ||||
| msgid "" | ||||
| "Błąd podczas aktualizacji tagów\n" | ||||
| "Status: {code}" | ||||
| msgstr "" | ||||
| "Błąd podczas aktualizacji tagów\n" | ||||
| "Status: {code}" | ||||
|  | ||||
| #: ImageBrowser.py:1600 | ||||
| msgid "Treść:" | ||||
| msgstr "Treść:" | ||||
|  | ||||
| #: ImageBrowser.py:1604 | ||||
| msgid "Krytyczny błąd edycji" | ||||
| msgstr "Krytyczny błąd edycji" | ||||
|  | ||||
| #: ImageBrowser.py:1616 | ||||
| #: ImageBrowser.py:1161 | ||||
| msgid "Potwierdzenie" | ||||
| msgstr "Potwierdzenie" | ||||
|  | ||||
| #: ImageBrowser.py:1618 | ||||
| #: ImageBrowser.py:1163 | ||||
| msgid "" | ||||
| "Czy na pewno chcesz wrzucić wszystkie niewrzucone pliki?\n" | ||||
| "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" | ||||
| @@ -387,21 +425,6 @@ msgstr "" | ||||
| "Każdy z nich zostanie oznaczony tagiem 'meta:auto_upload'.\n" | ||||
| "Upewnij się, że tagi są poprawne!" | ||||
|  | ||||
| #: ImageBrowser.py:1642 | ||||
| msgid "Anulowano operację!" | ||||
| msgstr "Operacja anulowana!" | ||||
|  | ||||
| #: ImageBrowser.py:1650 | ||||
| #, 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:1666 | ||||
| msgid "Przesłano pliki!" | ||||
| msgstr "Pliki zostały przesłane!" | ||||
|  | ||||
| #: ProcessingDialog.py:15 | ||||
| msgid "Processing..." | ||||
| msgstr "Przetwarzanie..." | ||||
| @@ -435,9 +458,8 @@ msgid "Błąd przy pobieraniu tagów artystów:" | ||||
| msgstr "Błąd przy pobieraniu tagów artystów:" | ||||
|  | ||||
| #: tag_processing.py:208 | ||||
| #, fuzzy | ||||
| msgid "Nie można sparsować parametrów." | ||||
| msgstr "Nie można załadować obrazka:" | ||||
| msgstr "Nie można sparsować parametrów." | ||||
|  | ||||
| #: tag_processing.py:295 | ||||
| msgid "Błąd podczas odczytu tag_aliases:" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" | ||||
|  | ||||
| [project] | ||||
| name = "kapitanbooru-uploader" | ||||
| version = "0.8.3" | ||||
| version = "0.9.0" | ||||
| description = "A GUI application for uploading images to KapitanBooru" | ||||
| authors = [{ name = "Michał Leśniak", email = "kapitan@mlesniak.pl" }] | ||||
| dependencies = [ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user