This commit is contained in:
Yuvi9587
2025-06-10 17:58:41 +01:00
parent 3fc2cfde99
commit d68bab40d9
5 changed files with 135 additions and 9 deletions

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -323,6 +323,46 @@ Download directly from your favorited artists and posts on Kemono.su.
- **Note:** Files successfully retried or skipped due to hash match during a retry attempt are removed from this error list. - **Note:** Files successfully retried or skipped due to hash match during a retry attempt are removed from this error list.
--- ---
## ⚙️ Application Settings
These settings allow you to customize the application's appearance and language.
- **⚙️ Settings Button (Icon may vary, e.g., a gear ⚙️):**
- **Location:** Typically located in a persistent area of the UI, possibly near other global controls or in a menu.
- **Purpose:** Opens the "Settings" dialog.
- **Tooltip Example:** "Open application settings (Theme, Language, etc.)"
- **"Settings" Dialog:**
- **Title:** "Settings"
- **Purpose:** Provides options to configure application-wide preferences.
- **Sections:**
- **Appearance Group (`Appearance`):**
- **Theme Toggle Buttons/Options:**
- `Switch to Light Mode`
- `Switch to Dark Mode`
- **Purpose:** Allows users to switch between a light and dark visual theme for the application.
- **Tooltips:** Provide guidance on switching themes.
- **Language Settings Group (`Language Settings`):**
- **Language Selection Dropdown/List:**
- **Label:** "Language:"
- **Options:** Includes, but not limited to:
- English (`English`)
- 日本語 (`日本語 (Japanese)`)
- Français (French)
- Español (Spanish)
- Deutsch (German)
- Русский (Russian)
- 한국어 (Korean)
- 简体中文 (Chinese Simplified)
- **Purpose:** Allows users to change the display language of the application interface.
- **Restart Prompt:** After changing the language, a dialog may appear:
- **Title:** "Language Changed"
- **Message:** "The language has been changed. A restart is required for all changes to take full effect."
- **Informative Text:** "Would you like to restart the application now?"
- **Buttons:** "Restart Now", "OK" (or similar to defer restart).
- **"OK" Button:** Saves the changes made in the Settings dialog and closes it.
---
## Other UI Elements ## Other UI Elements
- **Retry Failed Downloads Prompt:** - **Retry Failed Downloads Prompt:**

89
main.py
View File

@@ -119,6 +119,35 @@ except ImportError :
sys .exit (1 ) sys .exit (1 )
_app_icon_cache = None # Module-level cache
def get_app_icon_object():
"""
Loads and caches the application icon.
Returns a QIcon object.
"""
global _app_icon_cache
if _app_icon_cache is not None and not _app_icon_cache.isNull():
return _app_icon_cache
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
base_dir = sys._MEIPASS
else:
base_dir = os.path.dirname(os.path.abspath(__file__))
icon_path = os.path.join(base_dir, 'assets', 'Kemono.ico')
if os.path.exists(icon_path):
_app_icon_cache = QIcon(icon_path)
if _app_icon_cache.isNull():
print(f"Warning: QIcon created from '{icon_path}' is null. Icon might be invalid.")
_app_icon_cache = QIcon() # Store an empty icon to avoid re-processing
else:
print(f"Warning: Application icon 'assets/Kemono.ico' not found at {icon_path} (in get_app_icon_object)")
_app_icon_cache = QIcon() # Store an empty icon
return _app_icon_cache
MAX_THREADS =200 MAX_THREADS =200
RECOMMENDED_MAX_THREADS =50 RECOMMENDED_MAX_THREADS =50
MAX_FILE_THREADS_PER_POST_OR_WORKER =10 MAX_FILE_THREADS_PER_POST_OR_WORKER =10
@@ -168,6 +197,10 @@ class DownloadExtractedLinksDialog (QDialog ):
super ().__init__ (parent ) super ().__init__ (parent )
self .links_data =links_data self .links_data =links_data
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
if parent : if parent :
parent_width =parent .width () parent_width =parent .width ()
@@ -299,6 +332,10 @@ class ConfirmAddAllDialog (QDialog ):
self .user_choice =CONFIRM_ADD_ALL_CANCEL_DOWNLOAD self .user_choice =CONFIRM_ADD_ALL_CANCEL_DOWNLOAD
self .setWindowTitle (self ._tr ("confirm_add_all_dialog_title","Confirm Adding New Names")) self .setWindowTitle (self ._tr ("confirm_add_all_dialog_title","Confirm Adding New Names"))
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
main_layout =QVBoxLayout (self ) main_layout =QVBoxLayout (self )
info_label =QLabel ( info_label =QLabel (
@@ -416,6 +453,10 @@ class ExportOptionsDialog (QDialog ):
self .setModal (True ) self .setModal (True )
self .selected_option =self .EXPORT_MODE_LINK_ONLY self .selected_option =self .EXPORT_MODE_LINK_ONLY
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
layout =QVBoxLayout (self ) layout =QVBoxLayout (self )
self .description_label =QLabel () self .description_label =QLabel ()
@@ -487,6 +528,10 @@ class ErrorFilesDialog (QDialog ):
self .setModal (True ) self .setModal (True )
self .error_files =error_files_info_list self .error_files =error_files_info_list
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
main_layout =QVBoxLayout (self ) main_layout =QVBoxLayout (self )
if not self .error_files : if not self .error_files :
@@ -625,6 +670,10 @@ class FutureSettingsDialog (QDialog ):
self .parent_app =parent_app_ref self .parent_app =parent_app_ref
self .setModal (True ) self .setModal (True )
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
layout =QVBoxLayout (self ) layout =QVBoxLayout (self )
@@ -759,6 +808,10 @@ class EmptyPopupDialog (QDialog ):
self .current_scope_mode =self .SCOPE_CHARACTERS self .current_scope_mode =self .SCOPE_CHARACTERS
self .app_base_dir =app_base_dir self .app_base_dir =app_base_dir
self .all_creators_data =[] self .all_creators_data =[]
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .selected_creators_for_queue =[] self .selected_creators_for_queue =[]
self .globally_selected_creators ={} self .globally_selected_creators ={}
@@ -1122,6 +1175,10 @@ class CookieHelpDialog (QDialog ):
self .parent_app =parent_app self .parent_app =parent_app
self .setModal (True ) self .setModal (True )
self .offer_download_without_option =offer_download_without_option self .offer_download_without_option =offer_download_without_option
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .user_choice =None self .user_choice =None
main_layout =QVBoxLayout (self ) main_layout =QVBoxLayout (self )
@@ -1205,6 +1262,10 @@ class KnownNamesFilterDialog (QDialog ):
self .parent_app =parent_app_ref self .parent_app =parent_app_ref
self .setModal (True ) self .setModal (True )
self .all_known_name_entries =sorted (known_names_list ,key =lambda x :x ['name'].lower ()) self .all_known_name_entries =sorted (known_names_list ,key =lambda x :x ['name'].lower ())
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .selected_entries_to_return =[] self .selected_entries_to_return =[]
main_layout =QVBoxLayout (self ) main_layout =QVBoxLayout (self )
@@ -1307,6 +1368,10 @@ class FavoriteArtistsDialog (QDialog ):
self .parent_app =parent_app self .parent_app =parent_app
self .cookies_config =cookies_config self .cookies_config =cookies_config
self .all_fetched_artists =[] self .all_fetched_artists =[]
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .selected_artist_urls =[] self .selected_artist_urls =[]
self .setModal (True ) self .setModal (True )
@@ -1787,6 +1852,10 @@ class FavoritePostsDialog (QDialog ):
self .displayable_grouped_posts ={} self .displayable_grouped_posts ={}
self .fetcher_thread =None self .fetcher_thread =None
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .setModal (True ) self .setModal (True )
self .setMinimumSize (600 ,600 ) self .setMinimumSize (600 ,600 )
if hasattr (self .parent_app ,'get_dark_theme'): if hasattr (self .parent_app ,'get_dark_theme'):
@@ -2181,6 +2250,10 @@ class HelpGuideDialog (QDialog ):
self .steps_data =steps_data self .steps_data =steps_data
self .parent_app =parent_app self .parent_app =parent_app
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .setModal (True ) self .setModal (True )
self .setFixedSize (650 ,600 ) self .setFixedSize (650 ,600 )
@@ -2350,7 +2423,7 @@ class TourDialog (QDialog ):
CONFIG_ORGANIZATION_NAME ="KemonoDownloader" CONFIG_ORGANIZATION_NAME ="KemonoDownloader"
CONFIG_APP_NAME_TOUR ="ApplicationTour" CONFIG_APP_NAME_TOUR ="ApplicationTour"
TOUR_SHOWN_KEY ="neverShowTourAgainV18" TOUR_SHOWN_KEY ="neverShowTourAgainV19"
def __init__ (self ,parent =None ): def __init__ (self ,parent =None ):
super ().__init__ (parent ) super ().__init__ (parent )
@@ -2358,6 +2431,10 @@ class TourDialog (QDialog ):
self .current_step =0 self .current_step =0
self .parent_app =parent self .parent_app =parent
app_icon = get_app_icon_object()
if not app_icon.isNull():
self.setWindowIcon(app_icon)
self .setModal (True ) self .setModal (True )
self .setFixedSize (600 ,620 ) self .setFixedSize (600 ,620 )
self .setStyleSheet (""" self .setStyleSheet ("""
@@ -2803,12 +2880,12 @@ class DownloaderApp (QWidget ):
else: else:
# Running as a script # Running as a script
base_dir_for_icon = os.path.dirname(os.path.abspath(__file__)) base_dir_for_icon = os.path.dirname(os.path.abspath(__file__))
icon_path_for_window = os.path.join(base_dir_for_icon, 'Kemono.ico') icon_path_for_window = os.path.join(base_dir_for_icon, 'assets', 'Kemono.ico') # <--- This is for QWidget
if os.path.exists(icon_path_for_window): if os.path.exists(icon_path_for_window):
self.setWindowIcon(QIcon(icon_path_for_window)) self.setWindowIcon(QIcon(icon_path_for_window))
else: else:
self.log_signal.emit(f"⚠️ Main window icon 'Kemono.ico' not found at {icon_path_for_window} (tried in DownloaderApp init)") self.log_signal.emit(f"⚠️ Main window icon 'assets/Kemono.ico' not found at {icon_path_for_window} (tried in DownloaderApp init)")
except Exception as e_icon_app: except Exception as e_icon_app:
self.log_signal.emit(f"❌ Error setting main window icon in DownloaderApp init: {e_icon_app}") self.log_signal.emit(f"❌ Error setting main window icon in DownloaderApp init: {e_icon_app}")
@@ -7145,9 +7222,9 @@ if __name__ =='__main__':
qt_app =QApplication (sys .argv ) qt_app =QApplication (sys .argv )
if getattr (sys ,'frozen',False ):base_dir =sys ._MEIPASS if getattr (sys ,'frozen',False ):base_dir =sys ._MEIPASS
else :base_dir =os .path .dirname (os .path .abspath (__file__ )) else :base_dir =os .path .dirname (os .path .abspath (__file__ ))
icon_path =os .path .join (base_dir ,'Kemono.ico') icon_path =os .path .join (base_dir , 'assets', 'Kemono.ico')
if os .path .exists (icon_path ):qt_app .setWindowIcon (QIcon (icon_path )) if os .path .exists (icon_path ):qt_app .setWindowIcon (QIcon (icon_path ))
else :print (f"Warning: Application icon 'Kemono.ico' not found at {icon_path }") else :print (f"Warning: Application icon 'assets/Kemono.ico' not found at {icon_path }")
downloader_app_instance =DownloaderApp () downloader_app_instance =DownloaderApp ()
primary_screen =QApplication .primaryScreen () primary_screen =QApplication .primaryScreen ()

View File

@@ -1,4 +1,4 @@
<h1 align="center">Kemono Downloader v5.1.0</h1> <h1 align="center">Kemono Downloader v5.2.0</h1>
<table align="center"> <table align="center">
<tr> <tr>
@@ -34,7 +34,7 @@ A powerful, feature-rich GUI application for downloading content from **[Kemono.
Built with PyQt5, this tool is designed for users who want deep filtering capabilities, customizable folder structures, efficient downloads, and intelligent automation, all within a modern and user-friendly graphical interface. Built with PyQt5, this tool is designed for users who want deep filtering capabilities, customizable folder structures, efficient downloads, and intelligent automation, all within a modern and user-friendly graphical interface.
*This v5.0.0 release marks a significant feature milestone. Future updates are expected to be less frequent, focusing on maintenance and minor refinements.* *This v5.0.0 release marks a significant feature milestone. Future updates are expected to be less frequent, focusing on maintenance and minor refinements.*
*Update v5.1.0 enhances error handling and UI responsiveness.* *Update v5.2.0 introduces multi-language support, theme selection, and further UI refinements.*
<p align="center"> <p align="center">
<a href="features.md"><strong>📚 Full Feature List</strong></a> • <a href="features.md"><strong>📚 Full Feature List</strong></a> •
<a href="LICENSE"><strong>📝 License</strong></a> <a href="LICENSE"><strong>📝 License</strong></a>
@@ -69,12 +69,14 @@ Kemono Downloader offers a range of features to streamline your content download
- **⭐ Favorite Mode:** - **⭐ Favorite Mode:**
- Directly download from your favorited artists and posts on Kemono.su. - Directly download from your favorited artists and posts on Kemono.su.
- Requires a valid cookie and adapts the UI for easy selection from your favorites. - Requires a valid cookie and adapts the UI for easy selection from your favorites.
- Supports downloading into a single location or artist-specific subfolders. - Supports downloading into a single location or artist-specific subfolders.
- **Performance & Advanced Options:** - **Performance & Advanced Options:**
- **Cookie Support:** Use cookies (paste string or load from `cookies.txt`) to access restricted content. - **Cookie Support:** Use cookies (paste string or load from `cookies.txt`) to access restricted content.
- **Multithreading:** Configure the number of simultaneous downloads/post processing threads for improved speed. - **Multithreading:** Configure the number of simultaneous downloads/post processing threads for improved speed.
- **Logging:** - **Logging:**
- A detailed progress log displays download activity, errors, and summaries. - A detailed progress log displays download activity, errors, and summaries.
- **Multi-language Interface:** Choose from several languages for the UI (English, Japanese, French, Spanish, German, Russian, Korean, Chinese Simplified).
- **Theme Customization:** Selectable Light and Dark themes for user comfort.
--- ---
@@ -84,6 +86,13 @@ Kemono Downloader offers a range of features to streamline your content download
- A new **"Export URLs to .txt"** button, allowing users to save links of failed downloads either as "URL only" or "URL with details" (including post title, ID, and original filename). - A new **"Export URLs to .txt"** button, allowing users to save links of failed downloads either as "URL only" or "URL with details" (including post title, ID, and original filename).
- Fixed a bug where files skipped during retry (due to existing hash match) were not correctly removed from the error list. - Fixed a bug where files skipped during retry (due to existing hash match) were not correctly removed from the error list.
- **Improved UI Stability**: Addressed issues with UI state management to more accurately reflect ongoing download activities (including retries and external link downloads). This prevents the "Cancel" button from becoming inactive prematurely while operations are still running. - **Improved UI Stability**: Addressed issues with UI state management to more accurately reflect ongoing download activities (including retries and external link downloads). This prevents the "Cancel" button from becoming inactive prematurely while operations are still running.
## ✨ What's New in v5.2.0
- **Multi-language Support:** The interface now supports multiple languages: English, Japanese, French, Spanish, German, Russian, Korean, and Chinese (Simplified). Select your preferred language in the new Settings dialog.
- **Theme Selection:** Choose between Light and Dark application themes via the Settings dialog for a personalized viewing experience.
- **Centralized Settings:** A new Settings dialog (accessible via a settings button, often with a gear icon) provides a dedicated space for language and appearance customizations.
- **Internal Localization:** Introduced `languages.py` for managing UI translations, streamlining the addition of new languages by contributors.
--- ---
## Installation ## Installation