mirror of
https://github.com/Yuvi9587/Kemono-Downloader.git
synced 2025-12-29 16:14:44 +00:00
commit
This commit is contained in:
@@ -410,6 +410,39 @@ class PostProcessorWorker:
|
||||
unique_id_for_part_file = uuid.uuid4().hex[:8]
|
||||
unique_part_file_stem_on_disk = f"{temp_file_base_for_unique_part}_{unique_id_for_part_file}"
|
||||
max_retries = 3
|
||||
if not self.keep_in_post_duplicates:
|
||||
final_save_path_check = os.path.join(target_folder_path, filename_to_save_in_main_path)
|
||||
if os.path.exists(final_save_path_check):
|
||||
try:
|
||||
# Use a HEAD request to get the expected size without downloading the body
|
||||
with requests.head(file_url, headers=headers, timeout=15, cookies=cookies_to_use_for_file, allow_redirects=True) as head_response:
|
||||
head_response.raise_for_status()
|
||||
expected_size = int(head_response.headers.get('Content-Length', -1))
|
||||
|
||||
actual_size = os.path.getsize(final_save_path_check)
|
||||
|
||||
if expected_size != -1 and actual_size == expected_size:
|
||||
self.logger(f" -> Skip (File Exists & Complete): '{filename_to_save_in_main_path}' is already on disk with the correct size.")
|
||||
|
||||
# We still need to add its hash to the session to prevent duplicates in other modes
|
||||
# This is a quick hash calculation for the already existing file
|
||||
try:
|
||||
md5_hasher = hashlib.md5()
|
||||
with open(final_save_path_check, 'rb') as f_verify:
|
||||
for chunk in iter(lambda: f_verify.read(8192), b""):
|
||||
md5_hasher.update(chunk)
|
||||
|
||||
with self.downloaded_hash_counts_lock:
|
||||
self.downloaded_hash_counts[md5_hasher.hexdigest()] += 1
|
||||
except Exception as hash_exc:
|
||||
self.logger(f" ⚠️ Could not hash existing file '{filename_to_save_in_main_path}' for session: {hash_exc}")
|
||||
|
||||
return 0, 1, filename_to_save_in_main_path, was_original_name_kept_flag, FILE_DOWNLOAD_STATUS_SKIPPED, None
|
||||
else:
|
||||
self.logger(f" ⚠️ File '{filename_to_save_in_main_path}' exists but is incomplete (Expected: {expected_size}, Actual: {actual_size}). Re-downloading.")
|
||||
|
||||
except requests.RequestException as e:
|
||||
self.logger(f" ⚠️ Could not verify size of existing file '{filename_to_save_in_main_path}': {e}. Proceeding with download.")
|
||||
retry_delay = 5
|
||||
downloaded_size_bytes = 0
|
||||
calculated_file_hash = None
|
||||
@@ -741,8 +774,11 @@ class PostProcessorWorker:
|
||||
history_data_for_this_post = None
|
||||
|
||||
parsed_api_url = urlparse(self.api_url_input)
|
||||
referer_url = f"https://{parsed_api_url.netloc}/"
|
||||
headers = {'User-Agent': 'Mozilla/5.0', 'Referer': referer_url, 'Accept': '*/*'}
|
||||
post_data = self.post
|
||||
post_id = post_data.get('id', 'unknown_id')
|
||||
|
||||
post_page_url = f"https://{parsed_api_url.netloc}/{self.service}/user/{self.user_id}/post/{post_id}"
|
||||
headers = {'User-Agent': 'Mozilla/5.0', 'Referer': post_page_url, 'Accept': '*/*'}
|
||||
link_pattern = re.compile(r"""<a\s+.*?href=["'](https?://[^"']+)["'][^>]*>(.*?)</a>""", re.IGNORECASE | re.DOTALL)
|
||||
post_data = self.post
|
||||
post_title = post_data.get('title', '') or 'untitled_post'
|
||||
@@ -802,7 +838,7 @@ class PostProcessorWorker:
|
||||
all_files_from_post_api_for_char_check = []
|
||||
api_file_domain_for_char_check = urlparse(self.api_url_input).netloc
|
||||
if not api_file_domain_for_char_check or not any(d in api_file_domain_for_char_check.lower() for d in ['kemono.su', 'kemono.party', 'kemono.cr', 'coomer.su', 'coomer.party', 'coomer.st']):
|
||||
api_file_domain_for_char_check = "kemono.su" if "kemono" in self.service.lower() else "coomer.st"
|
||||
api_file_domain_for_char_check = "kemono.cr" if "kemono" in self.service.lower() else "coomer.st"
|
||||
if post_main_file_info and isinstance(post_main_file_info, dict) and post_main_file_info.get('path'):
|
||||
original_api_name = post_main_file_info.get('name') or os.path.basename(post_main_file_info['path'].lstrip('/'))
|
||||
if original_api_name:
|
||||
@@ -845,9 +881,9 @@ class PostProcessorWorker:
|
||||
try:
|
||||
parsed_input_url_for_comments = urlparse(self.api_url_input)
|
||||
api_domain_for_comments = parsed_input_url_for_comments.netloc
|
||||
if not any(d in api_domain_for_comments.lower() for d in ['kemono.su', 'kemono.party', 'coomer.su', 'coomer.party']):
|
||||
if not any(d in api_domain_for_comments.lower() for d in ['kemono.su', 'kemono.party', 'kemono.cr', 'coomer.su', 'coomer.party', 'coomer.st']):
|
||||
self.logger(f"⚠️ Unrecognized domain '{api_domain_for_comments}' for comment API. Defaulting based on service.")
|
||||
api_domain_for_comments = "kemono.su" if "kemono" in self.service.lower() else "coomer.party"
|
||||
api_domain_for_comments = "kemono.cr" if "kemono" in self.service.lower() else "coomer.st"
|
||||
comments_data = fetch_post_comments(
|
||||
api_domain_for_comments, self.service, self.user_id, post_id,
|
||||
headers, self.logger, self.cancellation_event, self.pause_event,
|
||||
@@ -1332,7 +1368,7 @@ class PostProcessorWorker:
|
||||
all_files_from_post_api = []
|
||||
api_file_domain = urlparse(self.api_url_input).netloc
|
||||
if not api_file_domain or not any(d in api_file_domain.lower() for d in ['kemono.su', 'kemono.party', 'kemono.cr', 'coomer.su', 'coomer.party', 'coomer.st']):
|
||||
api_file_domain = "kemono.su" if "kemono" in self.service.lower() else "coomer.st"
|
||||
api_file_domain = "kemono.cr" if "kemono" in self.service.lower() else "coomer.st"
|
||||
if post_main_file_info and isinstance(post_main_file_info, dict) and post_main_file_info.get('path'):
|
||||
file_path = post_main_file_info['path'].lstrip('/')
|
||||
original_api_name = post_main_file_info.get('name') or os.path.basename(file_path)
|
||||
|
||||
@@ -37,13 +37,13 @@ class FavoriteArtistsDialog (QDialog ):
|
||||
self ._init_ui ()
|
||||
self ._fetch_favorite_artists ()
|
||||
|
||||
def _get_domain_for_service (self ,service_name ):
|
||||
service_lower =service_name .lower ()
|
||||
coomer_primary_services ={'onlyfans','fansly','manyvids','candfans'}
|
||||
if service_lower in coomer_primary_services :
|
||||
return "coomer.su"
|
||||
else :
|
||||
return "kemono.su"
|
||||
def _get_domain_for_service(self, service_name):
|
||||
service_lower = service_name.lower()
|
||||
coomer_primary_services = {'onlyfans', 'fansly', 'manyvids', 'candfans'}
|
||||
if service_lower in coomer_primary_services:
|
||||
return "coomer.st" # Use the new domain
|
||||
else:
|
||||
return "kemono.cr" # Use the new domain
|
||||
|
||||
def _tr (self ,key ,default_text =""):
|
||||
"""Helper to get translation based on current app language."""
|
||||
@@ -128,9 +128,29 @@ class FavoriteArtistsDialog (QDialog ):
|
||||
def _fetch_favorite_artists (self ):
|
||||
|
||||
if self.cookies_config['use_cookie']:
|
||||
# Check if we can load cookies for at least one of the services.
|
||||
kemono_cookies = prepare_cookies_for_request(True, self.cookies_config['cookie_text'], self.cookies_config['selected_cookie_file'], self.cookies_config['app_base_dir'], self._logger, target_domain="kemono.su")
|
||||
coomer_cookies = prepare_cookies_for_request(True, self.cookies_config['cookie_text'], self.cookies_config['selected_cookie_file'], self.cookies_config['app_base_dir'], self._logger, target_domain="coomer.su")
|
||||
# --- Kemono Check with Fallback ---
|
||||
kemono_cookies = prepare_cookies_for_request(
|
||||
True, self.cookies_config['cookie_text'], self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'], self._logger, target_domain="kemono.cr"
|
||||
)
|
||||
if not kemono_cookies:
|
||||
self._logger("No cookies for kemono.cr, trying fallback kemono.su...")
|
||||
kemono_cookies = prepare_cookies_for_request(
|
||||
True, self.cookies_config['cookie_text'], self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'], self._logger, target_domain="kemono.su"
|
||||
)
|
||||
|
||||
# --- Coomer Check with Fallback ---
|
||||
coomer_cookies = prepare_cookies_for_request(
|
||||
True, self.cookies_config['cookie_text'], self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'], self._logger, target_domain="coomer.st"
|
||||
)
|
||||
if not coomer_cookies:
|
||||
self._logger("No cookies for coomer.st, trying fallback coomer.su...")
|
||||
coomer_cookies = prepare_cookies_for_request(
|
||||
True, self.cookies_config['cookie_text'], self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'], self._logger, target_domain="coomer.su"
|
||||
)
|
||||
|
||||
if not kemono_cookies and not coomer_cookies:
|
||||
# If cookies are enabled but none could be loaded, show help and stop.
|
||||
@@ -139,7 +159,7 @@ class FavoriteArtistsDialog (QDialog ):
|
||||
cookie_help_dialog = CookieHelpDialog(self.parent_app, self)
|
||||
cookie_help_dialog.exec_()
|
||||
self.download_button.setEnabled(False)
|
||||
return # Stop further execution
|
||||
return # Stop further execution
|
||||
|
||||
kemono_fav_url ="https://kemono.su/api/v1/account/favorites?type=artist"
|
||||
coomer_fav_url ="https://coomer.su/api/v1/account/favorites?type=artist"
|
||||
@@ -149,9 +169,12 @@ class FavoriteArtistsDialog (QDialog ):
|
||||
errors_occurred =[]
|
||||
any_cookies_loaded_successfully_for_any_source =False
|
||||
|
||||
api_sources =[
|
||||
{"name":"Kemono.su","url":kemono_fav_url ,"domain":"kemono.su"},
|
||||
{"name":"Coomer.su","url":coomer_fav_url ,"domain":"coomer.su"}
|
||||
kemono_cr_fav_url = "https://kemono.cr/api/v1/account/favorites?type=artist"
|
||||
coomer_st_fav_url = "https://coomer.st/api/v1/account/favorites?type=artist"
|
||||
|
||||
api_sources = [
|
||||
{"name": "Kemono.cr", "url": kemono_cr_fav_url, "domain": "kemono.cr"},
|
||||
{"name": "Coomer.st", "url": coomer_st_fav_url, "domain": "coomer.st"}
|
||||
]
|
||||
|
||||
for source in api_sources :
|
||||
@@ -159,20 +182,41 @@ class FavoriteArtistsDialog (QDialog ):
|
||||
self .status_label .setText (self ._tr ("fav_artists_loading_from_source_status","⏳ Loading favorites from {source_name}...").format (source_name =source ['name']))
|
||||
QCoreApplication .processEvents ()
|
||||
|
||||
cookies_dict_for_source =None
|
||||
if self .cookies_config ['use_cookie']:
|
||||
cookies_dict_for_source =prepare_cookies_for_request (
|
||||
True ,
|
||||
self .cookies_config ['cookie_text'],
|
||||
self .cookies_config ['selected_cookie_file'],
|
||||
self .cookies_config ['app_base_dir'],
|
||||
self ._logger ,
|
||||
target_domain =source ['domain']
|
||||
cookies_dict_for_source = None
|
||||
if self.cookies_config['use_cookie']:
|
||||
primary_domain = source['domain']
|
||||
fallback_domain = None
|
||||
if primary_domain == "kemono.cr":
|
||||
fallback_domain = "kemono.su"
|
||||
elif primary_domain == "coomer.st":
|
||||
fallback_domain = "coomer.su"
|
||||
|
||||
# First, try the primary domain
|
||||
cookies_dict_for_source = prepare_cookies_for_request(
|
||||
True,
|
||||
self.cookies_config['cookie_text'],
|
||||
self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'],
|
||||
self._logger,
|
||||
target_domain=primary_domain
|
||||
)
|
||||
if cookies_dict_for_source :
|
||||
any_cookies_loaded_successfully_for_any_source =True
|
||||
else :
|
||||
self ._logger (f"Warning ({source ['name']}): Cookies enabled but could not be loaded for this domain. Fetch might fail if cookies are required.")
|
||||
|
||||
# If no cookies found, try the fallback domain
|
||||
if not cookies_dict_for_source and fallback_domain:
|
||||
self._logger(f"Warning ({source['name']}): No cookies found for '{primary_domain}'. Trying fallback '{fallback_domain}'...")
|
||||
cookies_dict_for_source = prepare_cookies_for_request(
|
||||
True,
|
||||
self.cookies_config['cookie_text'],
|
||||
self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'],
|
||||
self._logger,
|
||||
target_domain=fallback_domain
|
||||
)
|
||||
|
||||
if cookies_dict_for_source:
|
||||
any_cookies_loaded_successfully_for_any_source = True
|
||||
else:
|
||||
self._logger(f"Warning ({source['name']}): Cookies enabled but could not be loaded for this source (including fallbacks). Fetch might fail.")
|
||||
try :
|
||||
headers ={'User-Agent':'Mozilla/5.0'}
|
||||
response =requests .get (source ['url'],headers =headers ,cookies =cookies_dict_for_source ,timeout =20 )
|
||||
@@ -223,7 +267,7 @@ class FavoriteArtistsDialog (QDialog ):
|
||||
if self .cookies_config ['use_cookie']and not any_cookies_loaded_successfully_for_any_source :
|
||||
self .status_label .setText (self ._tr ("fav_artists_cookies_required_status","Error: Cookies enabled but could not be loaded for any source."))
|
||||
self ._logger ("Error: Cookies enabled but no cookies loaded for any source. Showing help dialog.")
|
||||
cookie_help_dialog =CookieHelpDialog (self )
|
||||
cookie_help_dialog = CookieHelpDialog(self.parent_app, self)
|
||||
cookie_help_dialog .exec_ ()
|
||||
self .download_button .setEnabled (False )
|
||||
if not fetched_any_successfully :
|
||||
|
||||
@@ -34,28 +34,30 @@ class FavoritePostsFetcherThread (QThread ):
|
||||
self .target_domain_preference =target_domain_preference
|
||||
self .cancellation_event =threading .Event ()
|
||||
self .error_key_map ={
|
||||
"Kemono.su":"kemono_su",
|
||||
"Coomer.su":"coomer_su"
|
||||
"kemono.cr":"kemono_su",
|
||||
"coomer.st":"coomer_su"
|
||||
}
|
||||
|
||||
def _logger (self ,message ):
|
||||
self .parent_logger_func (f"[FavPostsFetcherThread] {message }")
|
||||
|
||||
def run (self ):
|
||||
kemono_fav_posts_url ="https://kemono.su/api/v1/account/favorites?type=post"
|
||||
coomer_fav_posts_url ="https://coomer.su/api/v1/account/favorites?type=post"
|
||||
def run(self):
|
||||
kemono_su_fav_posts_url = "https://kemono.su/api/v1/account/favorites?type=post"
|
||||
coomer_su_fav_posts_url = "https://coomer.su/api/v1/account/favorites?type=post"
|
||||
kemono_cr_fav_posts_url = "https://kemono.cr/api/v1/account/favorites?type=post"
|
||||
coomer_st_fav_posts_url = "https://coomer.st/api/v1/account/favorites?type=post"
|
||||
|
||||
all_fetched_posts_temp =[]
|
||||
error_messages_for_summary =[]
|
||||
fetched_any_successfully =False
|
||||
any_cookies_loaded_successfully_for_any_source =False
|
||||
all_fetched_posts_temp = []
|
||||
error_messages_for_summary = []
|
||||
fetched_any_successfully = False
|
||||
any_cookies_loaded_successfully_for_any_source = False
|
||||
|
||||
self .status_update .emit ("key_fetching_fav_post_list_init")
|
||||
self .progress_bar_update .emit (0 ,0 )
|
||||
self.status_update.emit("key_fetching_fav_post_list_init")
|
||||
self.progress_bar_update.emit(0, 0)
|
||||
|
||||
api_sources =[
|
||||
{"name":"Kemono.su","url":kemono_fav_posts_url ,"domain":"kemono.su"},
|
||||
{"name":"Coomer.su","url":coomer_fav_posts_url ,"domain":"coomer.su"}
|
||||
api_sources = [
|
||||
{"name": "Kemono.cr", "url": kemono_cr_fav_posts_url, "domain": "kemono.cr"},
|
||||
{"name": "Coomer.st", "url": coomer_st_fav_posts_url, "domain": "coomer.st"}
|
||||
]
|
||||
|
||||
api_sources_to_try =[]
|
||||
@@ -76,20 +78,41 @@ class FavoritePostsFetcherThread (QThread ):
|
||||
if self .cancellation_event .is_set ():
|
||||
self .finished .emit ([],"KEY_FETCH_CANCELLED_DURING")
|
||||
return
|
||||
cookies_dict_for_source =None
|
||||
if self .cookies_config ['use_cookie']:
|
||||
cookies_dict_for_source =prepare_cookies_for_request (
|
||||
True ,
|
||||
self .cookies_config ['cookie_text'],
|
||||
self .cookies_config ['selected_cookie_file'],
|
||||
self .cookies_config ['app_base_dir'],
|
||||
self ._logger ,
|
||||
target_domain =source ['domain']
|
||||
cookies_dict_for_source = None
|
||||
if self.cookies_config['use_cookie']:
|
||||
primary_domain = source['domain']
|
||||
fallback_domain = None
|
||||
if primary_domain == "kemono.cr":
|
||||
fallback_domain = "kemono.su"
|
||||
elif primary_domain == "coomer.st":
|
||||
fallback_domain = "coomer.su"
|
||||
|
||||
# First, try the primary domain
|
||||
cookies_dict_for_source = prepare_cookies_for_request(
|
||||
True,
|
||||
self.cookies_config['cookie_text'],
|
||||
self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'],
|
||||
self._logger,
|
||||
target_domain=primary_domain
|
||||
)
|
||||
if cookies_dict_for_source :
|
||||
any_cookies_loaded_successfully_for_any_source =True
|
||||
else :
|
||||
self ._logger (f"Warning ({source ['name']}): Cookies enabled but could not be loaded for this domain. Fetch might fail if cookies are required.")
|
||||
|
||||
# If no cookies found, try the fallback domain
|
||||
if not cookies_dict_for_source and fallback_domain:
|
||||
self._logger(f"Warning ({source['name']}): No cookies found for '{primary_domain}'. Trying fallback '{fallback_domain}'...")
|
||||
cookies_dict_for_source = prepare_cookies_for_request(
|
||||
True,
|
||||
self.cookies_config['cookie_text'],
|
||||
self.cookies_config['selected_cookie_file'],
|
||||
self.cookies_config['app_base_dir'],
|
||||
self._logger,
|
||||
target_domain=fallback_domain
|
||||
)
|
||||
|
||||
if cookies_dict_for_source:
|
||||
any_cookies_loaded_successfully_for_any_source = True
|
||||
else:
|
||||
self._logger(f"Warning ({source['name']}): Cookies enabled but could not be loaded for this domain. Fetch might fail if cookies are required.")
|
||||
|
||||
self ._logger (f"Attempting to fetch favorite posts from: {source ['name']} ({source ['url']})")
|
||||
source_key_part =self .error_key_map .get (source ['name'],source ['name'].lower ().replace ('.','_'))
|
||||
@@ -409,14 +432,14 @@ class FavoritePostsDialog (QDialog ):
|
||||
if status_key .startswith ("KEY_COOKIES_REQUIRED_BUT_NOT_FOUND_FOR_DOMAIN_")or status_key =="KEY_COOKIES_REQUIRED_BUT_NOT_FOUND_GENERIC":
|
||||
status_label_text_key ="fav_posts_cookies_required_error"
|
||||
self ._logger (f"Cookie error: {status_key }. Showing help dialog.")
|
||||
cookie_help_dialog =CookieHelpDialog (self )
|
||||
cookie_help_dialog = CookieHelpDialog(self.parent_app, self)
|
||||
cookie_help_dialog .exec_ ()
|
||||
elif status_key =="KEY_AUTH_FAILED":
|
||||
status_label_text_key ="fav_posts_auth_failed_title"
|
||||
self ._logger (f"Auth error: {status_key }. Showing help dialog.")
|
||||
QMessageBox .warning (self ,self ._tr ("fav_posts_auth_failed_title","Authorization Failed (Posts)"),
|
||||
self ._tr ("fav_posts_auth_failed_message_generic","...").format (domain_specific_part =specific_domain_msg_part ))
|
||||
cookie_help_dialog =CookieHelpDialog (self )
|
||||
cookie_help_dialog = CookieHelpDialog(self.parent_app, self)
|
||||
cookie_help_dialog .exec_ ()
|
||||
elif status_key =="KEY_NO_FAVORITES_FOUND_ALL_PLATFORMS":
|
||||
status_label_text_key ="fav_posts_no_posts_found_status"
|
||||
|
||||
81
src/ui/dialogs/multipart_dialog.py
Normal file
81
src/ui/dialogs/multipart_dialog.py
Normal file
@@ -0,0 +1,81 @@
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialog, QVBoxLayout, QGroupBox, QCheckBox, QHBoxLayout, QLabel,
|
||||
QLineEdit, QDialogButtonBox, QIntValidator
|
||||
)
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
class MultipartDialog(QDialog):
|
||||
"""
|
||||
A dialog for configuring multipart download settings.
|
||||
"""
|
||||
def __init__(self, current_settings, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("Multipart Download Options")
|
||||
self.setMinimumWidth(350)
|
||||
|
||||
self.settings = current_settings
|
||||
|
||||
# Main layout
|
||||
layout = QVBoxLayout(self)
|
||||
|
||||
# File types group
|
||||
types_group = QGroupBox("Apply to File Types")
|
||||
types_layout = QVBoxLayout()
|
||||
self.videos_checkbox = QCheckBox("Videos")
|
||||
self.archives_checkbox = QCheckBox("Archives")
|
||||
types_layout.addWidget(self.videos_checkbox)
|
||||
types_layout.addWidget(self.archives_checkbox)
|
||||
types_group.setLayout(types_layout)
|
||||
layout.addWidget(types_group)
|
||||
|
||||
# File size group
|
||||
size_group = QGroupBox("Minimum File Size")
|
||||
size_layout = QHBoxLayout()
|
||||
size_layout.addWidget(QLabel("Apply only if file size is over:"))
|
||||
self.min_size_input = QLineEdit()
|
||||
self.min_size_input.setValidator(QIntValidator(0, 99999))
|
||||
self.min_size_input.setFixedWidth(50)
|
||||
size_layout.addWidget(self.min_size_input)
|
||||
size_layout.addWidget(QLabel("MB"))
|
||||
size_layout.addStretch()
|
||||
size_group.setLayout(size_layout)
|
||||
layout.addWidget(size_group)
|
||||
|
||||
# Custom extensions group
|
||||
extensions_group = QGroupBox("Custom File Extensions")
|
||||
extensions_layout = QVBoxLayout()
|
||||
extensions_layout.addWidget(QLabel("Apply to these additional extensions (comma-separated):"))
|
||||
self.extensions_input = QLineEdit()
|
||||
self.extensions_input.setPlaceholderText("e.g., .psd, .blend, .mkv")
|
||||
extensions_layout.addWidget(self.extensions_input)
|
||||
extensions_group.setLayout(extensions_layout)
|
||||
layout.addWidget(extensions_group)
|
||||
|
||||
# Dialog buttons
|
||||
button_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
|
||||
button_box.accepted.connect(self.accept)
|
||||
button_box.rejected.connect(self.reject)
|
||||
layout.addWidget(button_box)
|
||||
|
||||
self.setLayout(layout)
|
||||
self._load_initial_settings()
|
||||
|
||||
def _load_initial_settings(self):
|
||||
"""Populates the dialog with the current settings."""
|
||||
self.videos_checkbox.setChecked(self.settings.get('apply_on_videos', False))
|
||||
self.archives_checkbox.setChecked(self.settings.get('apply_on_archives', False))
|
||||
self.min_size_input.setText(str(self.settings.get('min_size_mb', 200)))
|
||||
self.extensions_input.setText(", ".join(self.settings.get('custom_extensions', [])))
|
||||
|
||||
def get_selected_options(self):
|
||||
"""Returns the configured settings from the dialog."""
|
||||
custom_extensions_raw = self.extensions_input.text().strip().lower()
|
||||
custom_extensions = {ext.strip() for ext in custom_extensions_raw.split(',') if ext.strip().startswith('.')}
|
||||
|
||||
return {
|
||||
"enabled": True, # Implied if dialog is saved
|
||||
"apply_on_videos": self.videos_checkbox.isChecked(),
|
||||
"apply_on_archives": self.archives_checkbox.isChecked(),
|
||||
"min_size_mb": int(self.min_size_input.text()) if self.min_size_input.text().isdigit() else 200,
|
||||
"custom_extensions": sorted(list(custom_extensions))
|
||||
}
|
||||
@@ -4288,13 +4288,13 @@ class DownloaderApp (QWidget ):
|
||||
self.log_signal.emit(" Cancelling active External Link download thread...")
|
||||
self.external_link_download_thread.cancel()
|
||||
|
||||
def _get_domain_for_service (self ,service_name :str )->str :
|
||||
def _get_domain_for_service(self, service_name: str) -> str:
|
||||
"""Determines the base domain for a given service."""
|
||||
if not isinstance (service_name ,str ):
|
||||
return "kemono.cr"
|
||||
service_lower =service_name .lower ()
|
||||
coomer_primary_services ={'onlyfans','fansly','manyvids','candfans','gumroad','patreon','subscribestar','dlsite','discord','fantia','boosty','pixiv','fanbox'}
|
||||
if service_lower in coomer_primary_services and service_lower not in ['patreon','discord','fantia','boosty','pixiv','fanbox']:
|
||||
if not isinstance(service_name, str):
|
||||
return "kemono.cr" # Default fallback
|
||||
service_lower = service_name.lower()
|
||||
coomer_primary_services = {'onlyfans', 'fansly', 'manyvids', 'candfans', 'gumroad', 'subscribestar', 'dlsite'}
|
||||
if service_lower in coomer_primary_services:
|
||||
return "coomer.st"
|
||||
return "kemono.cr"
|
||||
|
||||
@@ -5343,42 +5343,54 @@ class DownloaderApp (QWidget ):
|
||||
|
||||
target_domain_preference_for_fetch =None
|
||||
|
||||
if cookies_config ['use_cookie']:
|
||||
self .log_signal .emit ("Favorite Posts: 'Use Cookie' is checked. Determining target domain...")
|
||||
kemono_cookies =prepare_cookies_for_request (
|
||||
cookies_config ['use_cookie'],
|
||||
cookies_config ['cookie_text'],
|
||||
cookies_config ['selected_cookie_file'],
|
||||
cookies_config ['app_base_dir'],
|
||||
lambda msg :self .log_signal .emit (f"[FavPosts Cookie Check - Kemono] {msg }"),
|
||||
target_domain ="kemono.su"
|
||||
if cookies_config['use_cookie']:
|
||||
self.log_signal.emit("Favorite Posts: 'Use Cookie' is checked. Determining target domain...")
|
||||
|
||||
# --- Kemono Check with Fallback ---
|
||||
kemono_cookies = prepare_cookies_for_request(
|
||||
cookies_config['use_cookie'], cookies_config['cookie_text'], cookies_config['selected_cookie_file'],
|
||||
cookies_config['app_base_dir'], lambda msg: self.log_signal.emit(f"[FavPosts Cookie Check] {msg}"),
|
||||
target_domain="kemono.cr"
|
||||
)
|
||||
coomer_cookies =prepare_cookies_for_request (
|
||||
cookies_config ['use_cookie'],
|
||||
cookies_config ['cookie_text'],
|
||||
cookies_config ['selected_cookie_file'],
|
||||
cookies_config ['app_base_dir'],
|
||||
lambda msg :self .log_signal .emit (f"[FavPosts Cookie Check - Coomer] {msg }"),
|
||||
target_domain ="coomer.su"
|
||||
if not kemono_cookies:
|
||||
self.log_signal.emit(" ↳ No cookies for kemono.cr, trying fallback kemono.su...")
|
||||
kemono_cookies = prepare_cookies_for_request(
|
||||
cookies_config['use_cookie'], cookies_config['cookie_text'], cookies_config['selected_cookie_file'],
|
||||
cookies_config['app_base_dir'], lambda msg: self.log_signal.emit(f"[FavPosts Cookie Check] {msg}"),
|
||||
target_domain="kemono.su"
|
||||
)
|
||||
|
||||
# --- Coomer Check with Fallback ---
|
||||
coomer_cookies = prepare_cookies_for_request(
|
||||
cookies_config['use_cookie'], cookies_config['cookie_text'], cookies_config['selected_cookie_file'],
|
||||
cookies_config['app_base_dir'], lambda msg: self.log_signal.emit(f"[FavPosts Cookie Check] {msg}"),
|
||||
target_domain="coomer.st"
|
||||
)
|
||||
if not coomer_cookies:
|
||||
self.log_signal.emit(" ↳ No cookies for coomer.st, trying fallback coomer.su...")
|
||||
coomer_cookies = prepare_cookies_for_request(
|
||||
cookies_config['use_cookie'], cookies_config['cookie_text'], cookies_config['selected_cookie_file'],
|
||||
cookies_config['app_base_dir'], lambda msg: self.log_signal.emit(f"[FavPosts Cookie Check] {msg}"),
|
||||
target_domain="coomer.su"
|
||||
)
|
||||
|
||||
kemono_ok =bool (kemono_cookies )
|
||||
coomer_ok =bool (coomer_cookies )
|
||||
kemono_ok = bool(kemono_cookies)
|
||||
coomer_ok = bool(coomer_cookies)
|
||||
|
||||
if kemono_ok and not coomer_ok :
|
||||
target_domain_preference_for_fetch ="kemono.su"
|
||||
self .log_signal .emit (" ↳ Only Kemono.su cookies loaded. Will fetch favorites from Kemono.su only.")
|
||||
elif coomer_ok and not kemono_ok :
|
||||
target_domain_preference_for_fetch ="coomer.su"
|
||||
self .log_signal .emit (" ↳ Only Coomer.su cookies loaded. Will fetch favorites from Coomer.su only.")
|
||||
elif kemono_ok and coomer_ok :
|
||||
target_domain_preference_for_fetch =None
|
||||
self .log_signal .emit (" ↳ Cookies for both Kemono.su and Coomer.su loaded. Will attempt to fetch from both.")
|
||||
else :
|
||||
self .log_signal .emit (" ↳ No valid cookies loaded for Kemono.su or Coomer.su.")
|
||||
cookie_help_dialog =CookieHelpDialog (self ,self )
|
||||
cookie_help_dialog .exec_ ()
|
||||
return
|
||||
if kemono_ok and not coomer_ok:
|
||||
target_domain_preference_for_fetch = "kemono.cr"
|
||||
self.log_signal.emit(" ↳ Only Kemono cookies loaded. Will fetch favorites from Kemono.cr only.")
|
||||
elif coomer_ok and not kemono_ok:
|
||||
target_domain_preference_for_fetch = "coomer.st"
|
||||
self.log_signal.emit(" ↳ Only Coomer cookies loaded. Will fetch favorites from Coomer.st only.")
|
||||
elif kemono_ok and coomer_ok:
|
||||
target_domain_preference_for_fetch = None
|
||||
self.log_signal.emit(" ↳ Cookies for both Kemono and Coomer loaded. Will attempt to fetch from both.")
|
||||
else:
|
||||
self.log_signal.emit(" ↳ No valid cookies loaded for Kemono.cr or Coomer.st.")
|
||||
cookie_help_dialog = CookieHelpDialog(self, self)
|
||||
cookie_help_dialog.exec_()
|
||||
return
|
||||
else :
|
||||
self .log_signal .emit ("Favorite Posts: 'Use Cookie' is NOT checked. Cookies are required.")
|
||||
cookie_help_dialog =CookieHelpDialog (self ,self )
|
||||
|
||||
Reference in New Issue
Block a user