From 6a36179136bf147ca01d872775003391a5ae1af7 Mon Sep 17 00:00:00 2001 From: Yuvi9587 <114073886+Yuvi9587@users.noreply.github.com> Date: Sun, 28 Dec 2025 09:34:49 +0530 Subject: [PATCH] Commit --- src/core/api_client.py | 9 +++------ src/core/hentaifox_client.py | 9 ++++----- src/core/workers.py | 4 +--- src/ui/dialogs/SinglePDF.py | 10 ++-------- src/ui/main_window.py | 24 ++++++------------------ 5 files changed, 16 insertions(+), 40 deletions(-) diff --git a/src/core/api_client.py b/src/core/api_client.py index 271824a..789a2d5 100644 --- a/src/core/api_client.py +++ b/src/core/api_client.py @@ -15,7 +15,6 @@ from ..config.constants import ( STYLE_POST_TITLE_GLOBAL_NUMBERING ) -# --- NEW: Custom Adapter to fix SSL errors --- class CustomSSLAdapter(HTTPAdapter): """ A custom HTTPAdapter that forces check_hostname=False when using SSL. @@ -130,13 +129,13 @@ def fetch_single_post_data(api_domain, service, user_id, post_id, headers, logge response = scraper.get(post_api_url, headers=headers, timeout=request_timeout, cookies=cookies_dict, proxies=proxies, verify=False) - # --- FIX: Handle 429 Rate Limit explicitly --- + if response.status_code == 429: wait_time = 20 + (attempt * 10) # 20s, 30s, 40s... logger(f" ⚠️ Rate Limited (429) on post {post_id}. Waiting {wait_time} seconds before retrying...") time.sleep(wait_time) continue # Try loop again - # --------------------------------------------- + response.raise_for_status() @@ -266,11 +265,9 @@ def download_from_api( if target_post_id and (start_page or end_page): logger("⚠️ Page range (start/end page) is ignored when a specific post URL is provided (searching all pages for the post).") - # --- FIXED LOGIC HERE --- - # Define which styles require fetching ALL posts first (Sequential Mode) + styles_requiring_fetch_all = [STYLE_DATE_BASED, STYLE_POST_TITLE_GLOBAL_NUMBERING] - # Only enable "fetch all and sort" if the current style is explicitly in the list above is_manga_mode_fetch_all_and_sort_oldest_first = ( manga_mode and (manga_filename_style_for_sort_check in styles_requiring_fetch_all) and diff --git a/src/core/hentaifox_client.py b/src/core/hentaifox_client.py index 0d6766b..5943667 100644 --- a/src/core/hentaifox_client.py +++ b/src/core/hentaifox_client.py @@ -1,8 +1,8 @@ import requests import re -from bs4 import BeautifulSoup # Optional, but regex is faster for this specific site +from bs4 import BeautifulSoup + -# Logic derived from NHdownloader.sh 'hentaifox' function BASE_URL = "https://hentaifox.com" HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", @@ -25,11 +25,10 @@ def get_gallery_metadata(gallery_id): response.raise_for_status() html = response.text - # Extract Title (Bash: grep -o '.*') + title_match = re.search(r'(.*?)', html) title = title_match.group(1).replace(" - HentaiFox", "").strip() if title_match else f"Gallery {gallery_id}" - - # Extract Total Pages (Bash: grep -Eo 'Pages: [0-9]*') + pages_match = re.search(r'Pages: (\d+)', html) if not pages_match: raise ValueError("Could not find total pages count.") diff --git a/src/core/workers.py b/src/core/workers.py index 171f732..6d0f434 100644 --- a/src/core/workers.py +++ b/src/core/workers.py @@ -686,7 +686,6 @@ class PostProcessorWorker: response = requests.get(new_url, headers=file_download_headers, timeout=(30, 300), stream=True, cookies=cookies_to_use_for_file, proxies=self.proxies, verify=False) response.raise_for_status() - # --- REVISED AND MOVED SIZE CHECK LOGIC --- total_size_bytes = int(response.headers.get('Content-Length', 0)) if self.skip_file_size_mb is not None: @@ -695,8 +694,7 @@ class PostProcessorWorker: if file_size_mb < self.skip_file_size_mb: self.logger(f" -> Skip File (Size): '{api_original_filename}' is {file_size_mb:.2f} MB, which is smaller than the {self.skip_file_size_mb} MB limit.") return 0, 1, api_original_filename, False, FILE_DOWNLOAD_STATUS_SKIPPED, None - # If Content-Length is missing, we can't check, so we no longer log a warning here and just proceed. - # --- END OF REVISED LOGIC --- + num_parts_for_file = min(self.multipart_parts_count, MAX_PARTS_FOR_MULTIPART_DOWNLOAD) diff --git a/src/ui/dialogs/SinglePDF.py b/src/ui/dialogs/SinglePDF.py index d8ff49a..c1043fb 100644 --- a/src/ui/dialogs/SinglePDF.py +++ b/src/ui/dialogs/SinglePDF.py @@ -75,11 +75,6 @@ def add_metadata_page(pdf, post, font_family): if link_url: # Styling for clickable link: Blue + Underline pdf.set_text_color(0, 0, 255) - # Check if font supports underline style directly or just use 'U' - # FPDF standard allows 'U' in style string. - # We use 'U' combined with the font family. - # Note: DejaVu implementation in fpdf2 might handle 'U' automatically or ignore it depending on version, - # but setting text color indicates link clearly enough usually. pdf.set_font(font_family, 'U', 11) # Pass the URL to the 'link' parameter @@ -131,9 +126,9 @@ def create_individual_pdf(post_data, output_filename, font_path, add_info_page=F font_family = _setup_pdf_fonts(pdf, font_path, logger) if add_info_page: - # add_metadata_page adds the page start itself +f add_metadata_page(pdf, post_data, font_family) - # REMOVED: pdf.add_page() <-- This ensures content starts right below the line + else: pdf.add_page() @@ -210,7 +205,6 @@ def create_single_pdf_from_content(posts_data, output_filename, font_path, add_i for i, post in enumerate(posts_data): if add_info_page: add_metadata_page(pdf, post, font_family) - # REMOVED: pdf.add_page() <-- This ensures content starts right below the line else: pdf.add_page() diff --git a/src/ui/main_window.py b/src/ui/main_window.py index a9b1ff5..8c5511f 100644 --- a/src/ui/main_window.py +++ b/src/ui/main_window.py @@ -6636,11 +6636,11 @@ class DownloaderApp (QWidget ): # Look up the name in the cache, falling back to the ID if not found. creator_name = self.creator_name_cache.get((service, user_id), user_id) - # Add the new 'creator_name' key to the format_values dictionary. + . format_values = { 'id': str(job_details.get('original_post_id_for_log', '')), 'user': user_id, - 'creator_name': creator_name, # <-- ADDED + 'creator_name': creator_name, 'service': str(job_details.get('service', '')), 'title': post_title, 'name': base, @@ -7075,7 +7075,6 @@ class DownloaderApp (QWidget ): self.log_signal.emit(f" Fetched a total of {len(all_posts_from_api)} posts from the server.") - # CORRECTED LINE: Assign the list directly without re-filtering self.new_posts_for_update = all_posts_from_api if not self.new_posts_for_update: @@ -7103,7 +7102,6 @@ class DownloaderApp (QWidget ): self.log_signal.emit(f" Update session will save to base folder: {base_download_dir_from_ui}") raw_character_filters_text = self.character_input.text().strip() - # FIX: Parse both filters and commands from the input string parsed_character_filter_objects, download_commands = self._parse_character_filters(raw_character_filters_text) try: @@ -7181,11 +7179,7 @@ class DownloaderApp (QWidget ): 'single_pdf_mode': self.single_pdf_setting, 'project_root_dir': self.app_base_dir, 'processed_post_ids': list(self.active_update_profile['processed_post_ids']), - - # FIX: Use the parsed commands dictionary to get the sfp_threshold 'sfp_threshold': download_commands.get('sfp_threshold'), - - # FIX: Add all the missing keys 'date_prefix_format': self.date_prefix_format, 'domain_override': download_commands.get('domain_override'), 'archive_only_mode': download_commands.get('archive_only', False), @@ -7218,11 +7212,9 @@ class DownloaderApp (QWidget ): dialog = EmptyPopupDialog(self.user_data_path, self) if dialog.exec_() == QDialog.Accepted: -# --- START OF MODIFICATION --- + if hasattr(dialog, 'update_profiles_list') and dialog.update_profiles_list: self.active_update_profiles_list = dialog.update_profiles_list - - # --- NEW LOGIC: Check if user wants to load settings into UI --- load_settings_requested = getattr(dialog, 'load_settings_into_ui_requested', False) self.override_update_profile_settings = load_settings_requested @@ -7239,7 +7231,7 @@ class DownloaderApp (QWidget ): self.link_input.setText(f"{len(self.active_update_profiles_list)} profiles loaded for update check...") self._start_batch_update_check(self.active_update_profiles_list) - # --- END OF MODIFICATION --- + elif hasattr(dialog, 'selected_creators_for_queue') and dialog.selected_creators_for_queue: self.active_update_profile = None # Ensure single update mode is off @@ -7488,17 +7480,13 @@ class DownloaderApp (QWidget ): should_create_artist_folder = False -# --- Check for popup selection scope --- + if item_type == 'creator_popup_selection' and item_scope == EmptyPopupDialog.SCOPE_CREATORS: - should_create_artist_folder = True - # --- Check for global "Artist Folders" scope --- + should_create_artist_folder = True elif item_type != 'creator_popup_selection' and self.favorite_download_scope == FAVORITE_SCOPE_ARTIST_FOLDERS: should_create_artist_folder = True - - # --- NEW: Check for forced folder flag from batch --- if self.current_processing_favorite_item_info.get('force_artist_folder'): should_create_artist_folder = True - # --------------------------------------------------- if should_create_artist_folder and main_download_dir: folder_name_key = self.current_processing_favorite_item_info.get('name_for_folder', 'Unknown_Folder')