This commit is contained in:
Yuvi9587
2025-05-25 11:38:38 +05:30
parent fc94f4c691
commit e665fd3cde
2 changed files with 182 additions and 56 deletions

View File

@@ -31,6 +31,7 @@ from io import BytesIO
STYLE_POST_TITLE = "post_title"
STYLE_ORIGINAL_NAME = "original_name"
STYLE_DATE_BASED = "date_based" # For manga date-based sequential naming
STYLE_POST_TITLE_GLOBAL_NUMBERING = "post_title_global_numbering" # For manga post title + global counter
SKIP_SCOPE_FILES = "files"
SKIP_SCOPE_POSTS = "posts"
@@ -276,7 +277,7 @@ def prepare_cookies_for_request(use_cookie_flag, cookie_text_input, selected_coo
return None
def fetch_posts_paginated(api_url_base, headers, offset, logger, cancellation_event=None, pause_event=None, cookies_dict=None):
if cancellation_event and cancellation_event.is_set():
if cancellation_event and cancellation_event.is_set(): # type: ignore
logger(" Fetch cancelled before request.")
raise RuntimeError("Fetch operation cancelled by user.")
@@ -284,7 +285,7 @@ def fetch_posts_paginated(api_url_base, headers, offset, logger, cancellation_ev
logger(" Post fetching paused...")
while pause_event.is_set():
if cancellation_event and cancellation_event.is_set():
logger(" Post fetching cancelled while paused.")
logger(" Post fetching cancelled while paused.") # type: ignore
raise RuntimeError("Fetch operation cancelled by user.")
time.sleep(0.5)
logger(" Post fetching resumed.")
@@ -379,21 +380,37 @@ def download_from_api(api_url_input, logger=print, start_page=None, end_page=Non
page_size = 50
if is_creator_feed_for_manga:
logger(" Manga Mode: Fetching all posts to reverse order (oldest posts processed first)...")
logger(" Manga Mode: Fetching posts to sort by date (oldest processed first)...")
all_posts_for_manga_mode = []
current_offset_manga = 0
# Determine starting page and offset for manga mode
if start_page and start_page > 1:
current_offset_manga = (start_page - 1) * page_size
logger(f" Manga Mode: Starting fetch from page {start_page} (offset {current_offset_manga}).")
elif start_page: # start_page is 1
logger(f" Manga Mode: Starting fetch from page 1 (offset 0).")
if end_page:
logger(f" Manga Mode: Will fetch up to page {end_page}.")
while True:
if pause_event and pause_event.is_set():
logger(" Manga mode post fetching paused...") # type: ignor
logger(" Manga mode post fetching paused...") # type: ignore
while pause_event.is_set():
if cancellation_event and cancellation_event.is_set():
logger(" Manga mode post fetching cancelled while paused.")
logger(" Manga mode post fetching cancelled while paused.") # type: ignore
break
time.sleep(0.5)
if not (cancellation_event and cancellation_event.is_set()): logger(" Manga mode post fetching resumed.")
if cancellation_event and cancellation_event.is_set():
logger(" Manga mode post fetching cancelled.")
break
current_page_num_manga = (current_offset_manga // page_size) + 1
if end_page and current_page_num_manga > end_page:
logger(f" Manga Mode: Reached specified end page ({end_page}). Stopping post fetch.")
break
try:
posts_batch_manga = fetch_posts_paginated(api_base_url, headers, current_offset_manga, logger, cancellation_event, pause_event, cookies_dict=cookies_for_api)
if not isinstance(posts_batch_manga, list):
@@ -401,7 +418,11 @@ def download_from_api(api_url_input, logger=print, start_page=None, end_page=Non
break
if not posts_batch_manga:
logger("✅ Reached end of posts (Manga Mode fetch all).")
break
if start_page and not end_page and current_page_num_manga < start_page: # Started on a page with no posts
logger(f" Manga Mode: No posts found on or after specified start page {start_page}.")
elif end_page and current_page_num_manga <= end_page and not all_posts_for_manga_mode: # Range specified but no posts in it
logger(f" Manga Mode: No posts found within the specified page range ({start_page or 1}-{end_page}).")
break # No more posts from API
all_posts_for_manga_mode.extend(posts_batch_manga)
current_offset_manga += page_size # Increment by page_size for the next API call's 'o' parameter
time.sleep(0.6)
@@ -420,7 +441,7 @@ def download_from_api(api_url_input, logger=print, start_page=None, end_page=Non
if all_posts_for_manga_mode:
logger(f" Manga Mode: Fetched {len(all_posts_for_manga_mode)} total posts. Sorting by publication date (oldest first)...")
# ... (rest of sorting and yielding logic for manga mode remains the same) ...
def sort_key_tuple(post):
published_date_str = post.get('published')
added_date_str = post.get('added')
@@ -584,7 +605,8 @@ class PostProcessorWorker:
selected_cookie_file=None, # Added missing parameter
app_base_dir=None, # New parameter for app's base directory
manga_date_file_counter_ref=None, # New parameter for date-based manga naming
):
manga_global_file_counter_ref=None, # New parameter for global numbering
): # type: ignore
self.post = post_data
self.download_root = download_root
self.known_names = known_names
@@ -630,6 +652,7 @@ class PostProcessorWorker:
self.selected_cookie_file = selected_cookie_file # Store selected cookie file path
self.app_base_dir = app_base_dir # Store app base dir
self.cookie_text = cookie_text # Store cookie text
self.manga_global_file_counter_ref = manga_global_file_counter_ref # Store global counter
self.use_cookie = use_cookie # Store cookie setting
if self.compress_images and Image is None:
@@ -666,7 +689,8 @@ class PostProcessorWorker:
def _download_single_file(self, file_info, target_folder_path, headers, original_post_id_for_log, skip_event, # skip_event is threading.Event
post_title="", file_index_in_post=0, num_files_in_this_post=1,
manga_date_file_counter_ref=None): # Added manga_date_file_counter_ref
was_original_name_kept_flag = False
was_original_name_kept_flag = False
manga_global_file_counter_ref = None # Placeholder, will be passed from process()
final_filename_saved_for_return = ""
def _get_current_character_filters(self):
@@ -677,7 +701,8 @@ class PostProcessorWorker:
def _download_single_file(self, file_info, target_folder_path, headers, original_post_id_for_log, skip_event,
post_title="", file_index_in_post=0, num_files_in_this_post=1, # Added manga_date_file_counter_ref
manga_date_file_counter_ref=None,
forced_filename_override=None): # New for retries
forced_filename_override=None, # New for retries
manga_global_file_counter_ref=None): # New for global numbering
was_original_name_kept_flag = False
final_filename_saved_for_return = ""
retry_later_details = None # For storing info if retryable failure
@@ -739,6 +764,19 @@ class PostProcessorWorker:
self.logger(f"⚠️ Manga Date Mode: Counter ref not provided or malformed for '{api_original_filename}'. Using original. Ref: {manga_date_file_counter_ref}")
filename_to_save_in_main_path = clean_filename(api_original_filename)
self.logger(f"⚠️ Manga mode (Date Based Style Fallback): Using cleaned original filename '{filename_to_save_in_main_path}' for post {original_post_id_for_log}.")
elif self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING:
if manga_global_file_counter_ref is not None and len(manga_global_file_counter_ref) == 2:
counter_val_for_filename = -1
counter_lock = manga_global_file_counter_ref[1]
with counter_lock:
counter_val_for_filename = manga_global_file_counter_ref[0]
manga_global_file_counter_ref[0] += 1
cleaned_post_title_base_for_global = clean_filename(post_title.strip() if post_title and post_title.strip() else "post")
filename_to_save_in_main_path = f"{cleaned_post_title_base_for_global}_{counter_val_for_filename:03d}{original_ext}"
else:
self.logger(f"⚠️ Manga Title+GlobalNum Mode: Counter ref not provided or malformed for '{api_original_filename}'. Using original. Ref: {manga_global_file_counter_ref}")
self.logger(f"⚠️ Manga mode (Date Based Style Fallback): Using cleaned original filename '{filename_to_save_in_main_path}' for post {original_post_id_for_log}.")
else:
self.logger(f"⚠️ Manga mode: Unknown filename style '{self.manga_filename_style}'. Defaulting to original filename for '{api_original_filename}'.")
filename_to_save_in_main_path = clean_filename(api_original_filename)
@@ -1429,6 +1467,14 @@ class PostProcessorWorker:
target_folder_path_for_this_file = current_path_for_file
manga_date_counter_to_pass = None
manga_global_counter_to_pass = None
if self.manga_mode_active:
if self.manga_filename_style == STYLE_DATE_BASED:
manga_date_counter_to_pass = self.manga_date_file_counter_ref
elif self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING:
manga_global_counter_to_pass = self.manga_global_file_counter_ref if self.manga_global_file_counter_ref is not None else self.manga_date_file_counter_ref
futures_list.append(file_pool.submit(
self._download_single_file,
file_info_to_dl,
@@ -1436,8 +1482,9 @@ class PostProcessorWorker:
headers,
post_id,
self.skip_current_file_flag,
post_title=post_title, # Keyword argument
manga_date_file_counter_ref=self.manga_date_file_counter_ref if self.manga_mode_active and self.manga_filename_style == STYLE_DATE_BASED else None,
post_title=post_title,
manga_date_file_counter_ref=manga_date_counter_to_pass,
manga_global_file_counter_ref=manga_global_counter_to_pass,
file_index_in_post=file_idx, # Changed to keyword argument
num_files_in_this_post=num_files_in_this_post_for_naming # Changed to keyword argument
))
@@ -1505,6 +1552,7 @@ class DownloadThread(QThread):
selected_cookie_file=None, # New parameter for selected cookie file
app_base_dir=None, # New parameter
manga_date_file_counter_ref=None, # New parameter
manga_global_file_counter_ref=None, # New parameter for global numbering
use_cookie=False, # Added: Expected by main.py
cookie_text="", # Added: Expected by main.py
):
@@ -1555,6 +1603,7 @@ class DownloadThread(QThread):
self.cookie_text = cookie_text # Store cookie text
self.use_cookie = use_cookie # Store cookie setting
self.manga_date_file_counter_ref = manga_date_file_counter_ref # Store for passing to worker by DownloadThread
self.manga_global_file_counter_ref = manga_global_file_counter_ref # Store for global numbering
if self.compress_images and Image is None:
self.logger("⚠️ Image compression disabled: Pillow library not found (DownloadThread).")
self.compress_images = False
@@ -1607,9 +1656,16 @@ class DownloadThread(QThread):
for filename_to_check in filenames_in_dir:
base_name_no_ext = os.path.splitext(filename_to_check)[0]
match = re.match(r"(\d{3,})", base_name_no_ext)
if match: highest_num = max(highest_num, int(match.group(1)))
if match: highest_num = max(highest_num, int(match.group(1))) # Corrected indentation
current_manga_date_file_counter_ref = [highest_num + 1, threading.Lock()]
self.logger(f" [Thread] Manga Date Mode: Initialized counter at {current_manga_date_file_counter_ref[0]}.")
elif self.manga_mode_active and self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING and not self.extract_links_only and current_manga_date_file_counter_ref is None: # Use current_manga_date_file_counter_ref for STYLE_POST_TITLE_GLOBAL_NUMBERING as well
# For global numbering, we always start from 1 for the session unless a ref is passed.
# If you need to resume global numbering across sessions, similar scanning logic would be needed.
# For now, it starts at 1 per session if no ref is provided.
current_manga_date_file_counter_ref = [1, threading.Lock()] # Start global numbering at 1
self.logger(f" [Thread] Manga Title+GlobalNum Mode: Initialized counter at {current_manga_date_file_counter_ref[0]}.")
worker_signals_obj = PostProcessorSignals()
try:
worker_signals_obj.progress_signal.connect(self.progress_signal)
@@ -1676,6 +1732,7 @@ class DownloadThread(QThread):
selected_cookie_file=self.selected_cookie_file, # Pass selected cookie file
app_base_dir=self.app_base_dir, # Pass app_base_dir
cookie_text=self.cookie_text, # Pass cookie text
manga_global_file_counter_ref=self.manga_global_file_counter_ref, # Pass the ref
use_cookie=self.use_cookie, # Pass cookie setting to worker
manga_date_file_counter_ref=current_manga_date_file_counter_ref, # Pass the calculated or passed-in ref
)

155
main.py
View File

@@ -54,20 +54,33 @@ try:
CHAR_SCOPE_FILES, # Ensure this is imported
CHAR_SCOPE_BOTH,
CHAR_SCOPE_COMMENTS,
FILE_DOWNLOAD_STATUS_FAILED_RETRYABLE_LATER # Import the new status
FILE_DOWNLOAD_STATUS_FAILED_RETRYABLE_LATER, # Import the new status
STYLE_POST_TITLE_GLOBAL_NUMBERING # Import new manga style
)
print("Successfully imported names from downloader_utils.")
except ImportError as e:
print(f"--- IMPORT ERROR ---")
print(f"Failed to import from 'downloader_utils.py': {e}")
print(f"--- Check downloader_utils.py for syntax errors or missing dependencies. ---")
KNOWN_NAMES = []
PostProcessorSignals = QObject
PostProcessorWorker = object
# Create a mock PostProcessorSignals class with the expected signals
class _MockPostProcessorSignals(QObject):
progress_signal = pyqtSignal(str)
file_download_status_signal = pyqtSignal(bool)
external_link_signal = pyqtSignal(str, str, str, str)
file_progress_signal = pyqtSignal(str, object)
missed_character_post_signal = pyqtSignal(str, str)
# Add any other signals that might be expected if the real class is extended
def __init__(self, parent=None):
super().__init__(parent)
print("WARNING: Using MOCK PostProcessorSignals due to import error from downloader_utils.py. Some functionalities might be impaired.")
PostProcessorSignals = _MockPostProcessorSignals # Use the mock class
BackendDownloadThread = QThread
def clean_folder_name(n): return str(n)
def extract_post_info(u): return None, None, None
def download_from_api(*a, **k): yield []
SKIP_SCOPE_FILES = "files"
SKIP_SCOPE_FILES = "files" # type: ignore
SKIP_SCOPE_POSTS = "posts"
SKIP_SCOPE_BOTH = "both"
CHAR_SCOPE_TITLE = "title"
@@ -75,6 +88,7 @@ except ImportError as e:
CHAR_SCOPE_BOTH = "both"
CHAR_SCOPE_COMMENTS = "comments"
FILE_DOWNLOAD_STATUS_FAILED_RETRYABLE_LATER = "failed_retry_later"
STYLE_POST_TITLE_GLOBAL_NUMBERING = "post_title_global_numbering" # Mock for safety
except Exception as e:
print(f"--- UNEXPECTED IMPORT ERROR ---")
@@ -101,6 +115,7 @@ MANGA_FILENAME_STYLE_KEY = "mangaFilenameStyleV1"
STYLE_POST_TITLE = "post_title"
STYLE_ORIGINAL_NAME = "original_name"
STYLE_DATE_BASED = "date_based" # New style for date-based naming
STYLE_POST_TITLE_GLOBAL_NUMBERING = STYLE_POST_TITLE_GLOBAL_NUMBERING # Use imported or mocked
SKIP_WORDS_SCOPE_KEY = "skipWordsScopeV1"
ALLOW_MULTIPART_DOWNLOAD_KEY = "allowMultipartDownloadV1"
@@ -2460,18 +2475,18 @@ class DownloaderApp(QWidget):
if self.custom_folder_input: self.custom_folder_input.clear()
def update_ui_for_subfolders(self, checked):
def update_ui_for_subfolders(self, separate_folders_by_name_title_checked: bool):
is_only_links = self.radio_only_links and self.radio_only_links.isChecked()
is_only_archives = self.radio_only_archives and self.radio_only_archives.isChecked()
can_enable_subfolder_per_post_checkbox = not is_only_links and not is_only_archives
if self.use_subfolder_per_post_checkbox:
can_enable_subfolder_per_post = checked and not is_only_links and not is_only_archives
self.use_subfolder_per_post_checkbox.setEnabled(can_enable_subfolder_per_post)
if not can_enable_subfolder_per_post: # If it's disabled, also uncheck it
self.use_subfolder_per_post_checkbox.setEnabled(can_enable_subfolder_per_post_checkbox)
if not can_enable_subfolder_per_post_checkbox:
self.use_subfolder_per_post_checkbox.setChecked(False)
# Visibility and enabled state of character filter widgets are now primarily handled
# by _handle_filter_mode_change to decouple from the subfolder checkbox.
self.update_custom_folder_visibility()
@@ -2508,12 +2523,12 @@ class DownloaderApp(QWidget):
_, _, post_id = extract_post_info(url_text)
is_creator_feed = not post_id if url_text else False
manga_mode_active = self.manga_mode_checkbox.isChecked() if self.manga_mode_checkbox else False
enable_page_range = is_creator_feed and not manga_mode_active
# Manga mode no longer directly dictates page range enabled state.
# Page range is enabled if it's a creator feed.
enable_page_range = is_creator_feed
for widget in [self.page_range_label, self.start_page_input, self.to_label, self.end_page_input]:
if widget: widget.setEnabled(enable_page_range)
if widget: widget.setEnabled(enable_page_range) # Enable/disable based on whether it's a creator feed
if not enable_page_range:
if self.start_page_input: self.start_page_input.clear()
@@ -2546,6 +2561,18 @@ class DownloaderApp(QWidget):
" Downloads as: \"001.jpg\", \"002.jpg\".\n\n"
"Click to change to: Post Title"
)
elif self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING:
self.manga_rename_toggle_button.setText("Name: Title+G.Num")
self.manga_rename_toggle_button.setToolTip(
"Manga Filename Style: Post Title + Global Numbering\n\n"
"When Manga/Comic Mode is active for a creator feed:\n"
"- All files across all posts in the current download session are named sequentially using the post's title as a prefix.\n"
"- Example: Post 'Chapter 1' (2 files) -> 'Chapter 1_001.jpg', 'Chapter 1_002.png'.\n"
" Next Post 'Chapter 2' (1 file) -> 'Chapter 2_003.jpg'.\n"
"- Multithreading for post processing is automatically disabled for this style.\n\n"
"Click to change to: Post Title"
)
elif self.manga_filename_style == STYLE_DATE_BASED:
self.manga_rename_toggle_button.setText("Name: Date Based")
self.manga_rename_toggle_button.setToolTip(
@@ -2572,8 +2599,10 @@ class DownloaderApp(QWidget):
if current_style == STYLE_POST_TITLE: # Title -> Original
new_style = STYLE_ORIGINAL_NAME
elif current_style == STYLE_ORIGINAL_NAME: # Original -> Date
new_style = STYLE_POST_TITLE_GLOBAL_NUMBERING # Original -> Title+GlobalNum
elif current_style == STYLE_POST_TITLE_GLOBAL_NUMBERING: # Title+GlobalNum -> Date Based
new_style = STYLE_DATE_BASED
elif current_style == STYLE_DATE_BASED: # Date -> Title
elif current_style == STYLE_DATE_BASED: # Date Based -> Title
new_style = STYLE_POST_TITLE
else:
self.log_signal.emit(f"⚠️ Unknown current manga filename style: {current_style}. Resetting to default ('{STYLE_POST_TITLE}').")
@@ -2606,14 +2635,8 @@ class DownloaderApp(QWidget):
if self.manga_rename_toggle_button:
self.manga_rename_toggle_button.setVisible(manga_mode_effectively_on and not (is_only_links_mode or is_only_archives_mode))
if manga_mode_effectively_on:
if self.page_range_label: self.page_range_label.setEnabled(False)
if self.start_page_input: self.start_page_input.setEnabled(False); self.start_page_input.clear()
if self.to_label: self.to_label.setEnabled(False)
if self.end_page_input: self.end_page_input.setEnabled(False); self.end_page_input.clear()
else:
self.update_page_range_enabled_state()
# Always update page range enabled state, as it depends on URL type, not directly manga mode.
self.update_page_range_enabled_state()
file_download_mode_active = not (self.radio_only_links and self.radio_only_links.isChecked())
# Character filter widgets should be enabled if it's a file download mode
@@ -2666,10 +2689,12 @@ class DownloaderApp(QWidget):
if not hasattr(self, 'manga_mode_checkbox') or not hasattr(self, 'use_multithreading_checkbox'):
return # UI elements not ready
manga_on = self.manga_mode_checkbox.isChecked()
is_date_style = (self.manga_filename_style == STYLE_DATE_BASED)
if manga_on and is_date_style:
manga_on = self.manga_mode_checkbox.isChecked() # type: ignore
is_sequential_style_requiring_single_thread = (
self.manga_filename_style == STYLE_DATE_BASED or
self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING
)
if manga_on and is_sequential_style_requiring_single_thread:
if self.use_multithreading_checkbox.isChecked() or self.use_multithreading_checkbox.isEnabled():
if self.use_multithreading_checkbox.isChecked():
self.log_signal.emit(" Manga Date Mode: Multithreading for post processing has been disabled to ensure correct sequential file numbering.")
@@ -2810,16 +2835,42 @@ class DownloaderApp(QWidget):
start_page_str, end_page_str = self.start_page_input.text().strip(), self.end_page_input.text().strip()
start_page, end_page = None, None
is_creator_feed = bool(not post_id_from_url)
if is_creator_feed and not manga_mode:
if is_creator_feed: # Page range is only relevant and parsed for creator feeds
try:
if start_page_str: start_page = int(start_page_str)
if end_page_str: end_page = int(end_page_str)
# Validate parsed page numbers
if start_page is not None and start_page <= 0: raise ValueError("Start page must be positive.")
if end_page is not None and end_page <= 0: raise ValueError("End page must be positive.")
if start_page and end_page and start_page > end_page: raise ValueError("Start page cannot be greater than end page.")
except ValueError as e: QMessageBox.critical(self, "Page Range Error", f"Invalid page range: {e}"); return
elif manga_mode:
start_page, end_page = None, None
# If it's a creator feed, and manga mode is on, and both page fields were filled, show warning
if manga_mode and start_page and end_page:
msg_box = QMessageBox(self)
msg_box.setIcon(QMessageBox.Warning)
msg_box.setWindowTitle("Manga Mode & Page Range Warning")
msg_box.setText(
"You have enabled <b>Manga/Comic Mode</b> and also specified a <b>Page Range</b>.\n\n"
"Manga Mode processes posts from oldest to newest across all available pages by default.\n"
"If you use a page range, you might miss parts of the manga/comic if it starts before your 'Start Page' or continues after your 'End Page'.\n\n"
"However, if you are certain the content you want is entirely within this page range (e.g., a short series, or you know the specific pages for a volume), then proceeding is okay.\n\n"
"Do you want to proceed with this page range in Manga Mode?"
)
proceed_button = msg_box.addButton("Proceed Anyway", QMessageBox.AcceptRole)
cancel_button = msg_box.addButton("Cancel Download", QMessageBox.RejectRole)
msg_box.setDefaultButton(proceed_button)
msg_box.setEscapeButton(cancel_button)
msg_box.exec_()
if msg_box.clickedButton() == cancel_button:
self.log_signal.emit("❌ Download cancelled by user due to Manga Mode & Page Range warning.")
self.set_ui_enabled(True); return # Re-enable UI and stop
except ValueError as e:
QMessageBox.critical(self, "Page Range Error", f"Invalid page range: {e}")
self.set_ui_enabled(True); return # Re-enable UI and stop
# If not a creator_feed, start_page and end_page remain None.
self.external_link_queue.clear(); self.extracted_links_cache = []; self._is_processing_external_link_queue = False; self._current_link_post_title = None
raw_character_filters_text = self.character_input.text().strip() # Get current text
@@ -2954,17 +3005,27 @@ class DownloaderApp(QWidget):
self.retryable_failed_files_info.clear() # Clear previous retryable failures before new session
manga_date_file_counter_ref_for_thread = None
if manga_mode and self.manga_filename_style == STYLE_DATE_BASED and not extract_links_only:
manga_date_file_counter_ref_for_thread = None
# Initialization for STYLE_DATE_BASED (scanning existing files) happens in DownloadThread.run
manga_date_file_counter_ref_for_thread = None # Placeholder, actual init in thread
self.log_signal.emit(f" Manga Date Mode: File counter will be initialized by the download thread.")
manga_global_file_counter_ref_for_thread = None
if manga_mode and self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING and not extract_links_only:
manga_global_file_counter_ref_for_thread = None # Placeholder, actual init in thread
self.log_signal.emit(f" Manga Title+GlobalNum Mode: File counter will be initialized by the download thread (starts at 1).")
effective_num_post_workers = 1
effective_num_file_threads_per_worker = 1 # Default to 1 for all cases initially
if post_id_from_url:
if use_multithreading_enabled_by_checkbox:
effective_num_file_threads_per_worker = max(1, min(num_threads_from_gui, MAX_FILE_THREADS_PER_POST_OR_WORKER))
else:
else: # This is the outer else block
if manga_mode and self.manga_filename_style == STYLE_DATE_BASED:
effective_num_post_workers = 1
elif manga_mode and self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING: # Correctly indented elif
effective_num_post_workers = 1
effective_num_file_threads_per_worker = 1 # Files are sequential for this worker too
elif use_multithreading_enabled_by_checkbox: # Standard creator feed with multithreading enabled
effective_num_post_workers = max(1, min(num_threads_from_gui, MAX_THREADS)) # For posts
@@ -2980,13 +3041,15 @@ class DownloaderApp(QWidget):
log_messages.append(f" Mode: Creator Feed")
log_messages.append(f" Post Processing: {'Multi-threaded (' + str(effective_num_post_workers) + ' workers)' if effective_num_post_workers > 1 else 'Single-threaded (1 worker)'}")
log_messages.append(f" ↳ File Downloads per Worker: Up to {effective_num_file_threads_per_worker} concurrent file(s)")
if is_creator_feed:
if manga_mode: log_messages.append(" Page Range: All (Manga Mode - Oldest Posts Processed First)")
else:
pr_log = "All"
if start_page or end_page:
pr_log = f"{f'From {start_page} ' if start_page else ''}{'to ' if start_page and end_page else ''}{f'{end_page}' if end_page else (f'Up to {end_page}' if end_page else (f'From {start_page}' if start_page else 'Specific Range'))}".strip()
log_messages.append(f" Page Range: {pr_log if pr_log else 'All'}")
# Logging for page range (applies if is_creator_feed is true)
pr_log = "All"
if start_page or end_page: # Construct pr_log if start_page or end_page have values
pr_log = f"{f'From {start_page} ' if start_page else ''}{'to ' if start_page and end_page else ''}{f'{end_page}' if end_page else (f'Up to {end_page}' if end_page else (f'From {start_page}' if start_page else 'Specific Range'))}".strip()
if manga_mode:
log_messages.append(f" Page Range: {pr_log if pr_log else 'All'} (Manga Mode - Oldest Posts Processed First within range)")
else: # Not manga mode, but still a creator feed
log_messages.append(f" Page Range: {pr_log if pr_log else 'All'}")
if not extract_links_only:
@@ -3027,8 +3090,9 @@ class DownloaderApp(QWidget):
elif use_cookie_from_checkbox and selected_cookie_file_path_for_backend:
log_messages.append(f" ↳ Cookie File Selected: {os.path.basename(selected_cookie_file_path_for_backend)}")
should_use_multithreading_for_posts = use_multithreading_enabled_by_checkbox and not post_id_from_url
if manga_mode and self.manga_filename_style == STYLE_DATE_BASED and not post_id_from_url:
log_messages.append(f" Threading: Single-threaded (posts) - Enforced by Manga Date Mode")
if manga_mode and (self.manga_filename_style == STYLE_DATE_BASED or self.manga_filename_style == STYLE_POST_TITLE_GLOBAL_NUMBERING) and not post_id_from_url:
enforced_by_style = "Date Mode" if self.manga_filename_style == STYLE_DATE_BASED else "Title+GlobalNum Mode"
log_messages.append(f" Threading: Single-threaded (posts) - Enforced by Manga {enforced_by_style}")
should_use_multithreading_for_posts = False # Ensure this reflects the forced state
else:
log_messages.append(f" Threading: {'Multi-threaded (posts)' if should_use_multithreading_for_posts else 'Single-threaded (posts)'}")
@@ -3082,6 +3146,7 @@ class DownloaderApp(QWidget):
'allow_multipart_download': allow_multipart,
'cookie_text': cookie_text_from_input, # Pass cookie text
'selected_cookie_file': selected_cookie_file_path_for_backend, # Pass selected cookie file
'manga_global_file_counter_ref': manga_global_file_counter_ref_for_thread, # Pass new counter
'app_base_dir': app_base_dir_for_cookies, # Pass app base dir
'use_cookie': use_cookie_from_checkbox, # Pass cookie setting
}
@@ -3103,7 +3168,8 @@ class DownloaderApp(QWidget):
'skip_words_list', 'skip_words_scope', 'char_filter_scope',
'show_external_links', 'extract_links_only', 'num_file_threads_for_worker',
'start_page', 'end_page', 'target_post_id_from_initial_url',
'manga_date_file_counter_ref', # Ensure this is passed for single thread mode
'manga_date_file_counter_ref',
'manga_global_file_counter_ref', # Pass new counter for single thread mode
'manga_mode_active', 'unwanted_keywords', 'manga_filename_style',
'allow_multipart_download', 'use_cookie', 'cookie_text', 'app_base_dir', 'selected_cookie_file' # Added selected_cookie_file
]
@@ -3300,7 +3366,8 @@ class DownloaderApp(QWidget):
'skip_words_list', 'skip_words_scope', 'char_filter_scope',
'show_external_links', 'extract_links_only', 'allow_multipart_download', 'use_cookie', 'cookie_text', 'app_base_dir', 'selected_cookie_file', # Added selected_cookie_file
'num_file_threads', 'skip_current_file_flag', 'manga_date_file_counter_ref',
'manga_mode_active', 'manga_filename_style'
'manga_mode_active', 'manga_filename_style',
'manga_global_file_counter_ref' # Add new counter here
]
ppw_optional_keys_with_defaults = {
'skip_words_list', 'skip_words_scope', 'char_filter_scope', 'remove_from_filename_words_list',
@@ -3308,6 +3375,8 @@ class DownloaderApp(QWidget):
'num_file_threads', 'skip_current_file_flag', 'manga_mode_active', 'manga_filename_style',
'manga_date_file_counter_ref', 'use_cookie', 'cookie_text', 'app_base_dir', 'selected_cookie_file' # Added selected_cookie_file
}
# Batching is generally for high worker counts.
# If num_post_workers is low (e.g., 1), the num_post_workers > POST_WORKER_BATCH_THRESHOLD condition will prevent batching.
if num_post_workers > POST_WORKER_BATCH_THRESHOLD and self.total_posts_to_process > POST_WORKER_NUM_BATCHES :
self.log_signal.emit(f" High thread count ({num_post_workers}) detected. Batching post submissions into {POST_WORKER_NUM_BATCHES} parts.")