Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de936e8d96 | ||
|
|
9d0f0dda23 | ||
|
|
222ec769db | ||
|
|
6771ede722 | ||
|
|
8199b79dc7 | ||
|
|
dfca265380 |
BIN
Read/Read.png
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 82 KiB |
BIN
Read/Read1.png
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 84 KiB |
BIN
Read/Read2.png
|
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 85 KiB |
BIN
Read/Read3.png
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 90 KiB |
354
languages.py
@@ -121,6 +121,25 @@ translations ={
|
|||||||
"creator_popup_search_placeholder":"Search by name, service, or paste creator URL...",
|
"creator_popup_search_placeholder":"Search by name, service, or paste creator URL...",
|
||||||
"creator_popup_add_selected_button":"Add Selected",
|
"creator_popup_add_selected_button":"Add Selected",
|
||||||
"creator_popup_scope_characters_button":"Scope: Characters",
|
"creator_popup_scope_characters_button":"Scope: Characters",
|
||||||
|
"creator_popup_posts_area_title": "Fetched Posts",
|
||||||
|
"creator_popup_posts_search_placeholder": "Search fetched posts by title...",
|
||||||
|
"no_posts_fetched_yet_status": "No posts fetched yet.",
|
||||||
|
"fetched_posts_count_label": "Fetched {count} post(s). Select to add to queue.",
|
||||||
|
"no_posts_found_for_selection": "No posts found for selected creator(s).",
|
||||||
|
"fetched_posts_count_label_filtered": "Displaying {count} post(s) matching filter.",
|
||||||
|
"no_posts_match_search_filter": "No posts match your search filter.",
|
||||||
|
"fetch_error_for_creator_label": "Error fetching for {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Post fetching cancelled.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Failed to fetch posts or no posts found.",
|
||||||
|
"select_posts_to_queue_message": "Please select at least one post to add to the queue.",
|
||||||
|
"items_in_queue_placeholder": "{count} items in queue from popup.",
|
||||||
|
"post_fetch_finished_status": "Finished fetching posts for selected creators.",
|
||||||
|
"fetch_posts_button_text": "Fetch Posts",
|
||||||
|
"creator_popup_add_posts_to_queue_button": "Add Selected Posts to Queue",
|
||||||
|
"posts_for_creator_header": "Posts for",
|
||||||
|
"untitled_post_placeholder": "Untitled Post",
|
||||||
|
"no_creators_to_fetch_status": "No creators selected to fetch posts for.",
|
||||||
|
"post_fetch_cancelled_status": "Post fetching cancellation requested...",
|
||||||
"creator_popup_scope_creators_button":"Scope: Creators",
|
"creator_popup_scope_creators_button":"Scope: Creators",
|
||||||
"favorite_artists_button_text":"🖼️ Favorite Artists",
|
"favorite_artists_button_text":"🖼️ Favorite Artists",
|
||||||
"favorite_artists_button_tooltip":"Browse and download from your favorite artists on Kemono.su/Coomer.su.",
|
"favorite_artists_button_tooltip":"Browse and download from your favorite artists on Kemono.su/Coomer.su.",
|
||||||
@@ -137,6 +156,18 @@ translations ={
|
|||||||
"manga_style_date_based_text":"Name: Date Based",
|
"manga_style_date_based_text":"Name: Date Based",
|
||||||
"manga_style_title_global_num_text":"Name: Title+G.Num",
|
"manga_style_title_global_num_text":"Name: Title+G.Num",
|
||||||
"manga_style_unknown_text":"Name: Unknown Style",
|
"manga_style_unknown_text":"Name: Unknown Style",
|
||||||
|
"manga_style_post_title_tooltip":"""Files are named based on the post's title.
|
||||||
|
- The first file in a post is named using the cleaned post title (e.g., 'My Chapter 1.jpg').
|
||||||
|
- If the post has multiple files, subsequent files are also named using the post title, but with a numeric suffix like '_1', '_2', etc. (e.g., 'My Chapter 1_1.png', 'My Chapter 1_2.gif'). The counter for the suffix starts from 1 for the second file.
|
||||||
|
- If a post has only one file, it's named after the post title without a suffix.
|
||||||
|
|
||||||
|
Example: Post 'Chapter One' (3 files: originalA.jpg, originalB.png, originalC.gif)
|
||||||
|
Output: 'Chapter One.jpg', 'Chapter One_1.png', 'Chapter One_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"Files attempt to keep their original filenames.\n\n- An optional prefix can be entered in the input field that appears next to this button.\n\nExample (with prefix 'MySeries'): 'MySeries_OriginalFile.jpg'.\nExample (no prefix): 'OriginalFile.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"Files are named sequentially (e.g., 001.ext, 002.ext) based on post publication order.\n\n- An optional prefix can be entered in the input field that appears next to this button.\n- Multithreading for post processing is disabled for this style to ensure correct numbering.\n\nExample (with prefix 'MyComic'): 'MyComic_001.jpg', 'MyComic_002.png'.\nExample (no prefix): '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"Files are named with the post's title and a global sequential number across all posts.\n\n- Format: '[Cleaned Post Title]_[Global Counter].[ext]'\n- The counter (e.g., _001, _002) increments for every file downloaded in the current session.\n- Multithreading for post processing is disabled for this style to ensure correct numbering.\n\nExample: Post 'Chapter 1' (2 files) -> 'Chapter 1_001.jpg', 'Chapter 1_002.png'.\nNext post 'Chapter 2' (1 file) -> 'Chapter 2_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"The manga filename style is currently unknown. This is unexpected. Please cycle to a valid style.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"Click to cycle to the next style.",
|
||||||
"fav_artists_dialog_title":"Favorite Artists",
|
"fav_artists_dialog_title":"Favorite Artists",
|
||||||
"fav_artists_loading_status":"Loading favorite artists...",
|
"fav_artists_loading_status":"Loading favorite artists...",
|
||||||
"fav_artists_search_placeholder":"Search artists...",
|
"fav_artists_search_placeholder":"Search artists...",
|
||||||
@@ -175,6 +206,7 @@ translations ={
|
|||||||
"key_fetching_from_source_kemono_su":"Fetching favorites from Kemono.su...",
|
"key_fetching_from_source_kemono_su":"Fetching favorites from Kemono.su...",
|
||||||
"key_fetching_from_source_coomer_su":"Fetching favorites from Coomer.su...",
|
"key_fetching_from_source_coomer_su":"Fetching favorites from Coomer.su...",
|
||||||
"fav_posts_fetch_cancelled_status":"Favorite post fetch cancelled.",
|
"fav_posts_fetch_cancelled_status":"Favorite post fetch cancelled.",
|
||||||
|
"fetching_posts_for_creator_status_all_pages": "Fetching all posts for {creator_name} ({service})... This may take a while.",
|
||||||
|
|
||||||
"known_names_filter_dialog_title":"Add Known Names to Filter",
|
"known_names_filter_dialog_title":"Add Known Names to Filter",
|
||||||
"known_names_filter_search_placeholder":"Search names...",
|
"known_names_filter_search_placeholder":"Search names...",
|
||||||
@@ -613,6 +645,18 @@ translations ={
|
|||||||
"creator_popup_add_selected_button":"選択項目を追加",
|
"creator_popup_add_selected_button":"選択項目を追加",
|
||||||
"creator_popup_scope_characters_button":"スコープ: キャラクター",
|
"creator_popup_scope_characters_button":"スコープ: キャラクター",
|
||||||
"creator_popup_scope_creators_button":"スコープ: クリエイター",
|
"creator_popup_scope_creators_button":"スコープ: クリエイター",
|
||||||
|
"creator_popup_title_fetching": "クリエイター投稿",
|
||||||
|
"creator_popup_posts_area_title": "取得済み投稿",
|
||||||
|
"creator_popup_posts_search_placeholder": "タイトルで取得済み投稿を検索...",
|
||||||
|
"no_posts_fetched_yet_status": "まだ投稿が取得されていません。",
|
||||||
|
"fetched_posts_count_label": "{count}件の投稿を取得しました。キューに追加するものを選択してください。",
|
||||||
|
"no_posts_found_for_selection": "選択したクリエイターの投稿が見つかりませんでした。",
|
||||||
|
"fetched_posts_count_label_filtered": "フィルターに一致する{count}件の投稿を表示中。",
|
||||||
|
"no_posts_match_search_filter": "検索フィルターに一致する投稿がありません。",
|
||||||
|
"fetch_error_for_creator_label": "{creator_name}の取得エラー",
|
||||||
|
"post_fetch_cancelled_status_done": "投稿の取得がキャンセルされました。",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "投稿の取得に失敗したか、投稿が見つかりませんでした。",
|
||||||
|
"select_posts_to_queue_message": "キューに追加する投稿を少なくとも1つ選択してください。",
|
||||||
"favorite_artists_button_text":"🖼️ お気に入りアーティスト",
|
"favorite_artists_button_text":"🖼️ お気に入りアーティスト",
|
||||||
"favorite_artists_button_tooltip":"Kemono.su/Coomer.suでお気に入りのアーティストを閲覧してダウンロードします。",
|
"favorite_artists_button_tooltip":"Kemono.su/Coomer.suでお気に入りのアーティストを閲覧してダウンロードします。",
|
||||||
"favorite_posts_button_text":"📄 お気に入り投稿",
|
"favorite_posts_button_text":"📄 お気に入り投稿",
|
||||||
@@ -628,6 +672,18 @@ translations ={
|
|||||||
"manga_style_date_based_text":"名前: 日付順",
|
"manga_style_date_based_text":"名前: 日付順",
|
||||||
"manga_style_title_global_num_text":"名前: タイトル+通し番号",
|
"manga_style_title_global_num_text":"名前: タイトル+通し番号",
|
||||||
"manga_style_unknown_text":"名前: 不明なスタイル",
|
"manga_style_unknown_text":"名前: 不明なスタイル",
|
||||||
|
"manga_style_post_title_tooltip":"""ファイルは投稿のタイトルに基づいて名前が付けられます。
|
||||||
|
- 投稿の最初のファイルは、投稿のクリーンなタイトルを取得します(例:「私の第1章.jpg」)。
|
||||||
|
- 投稿に複数のファイルがある場合、後続のファイルも投稿タイトルを使用して名前が付けられますが、「_1」、「_2」などの数字の接尾辞が付きます(例:「私の第1章_1.png」、「私の第1章_2.gif」)。接尾辞のカウンターは2番目のファイルから1で始まります。
|
||||||
|
- 投稿にファイルが1つしかない場合は、接尾辞なしで投稿のタイトルに基づいて名前が付けられます。
|
||||||
|
|
||||||
|
例:投稿「第一章」(3ファイル:originala.jpg、originalb.png、originalc.gif)
|
||||||
|
出力:「第一章.jpg」、「第一章_1.png」、「第一章_2.gif」。""",
|
||||||
|
"manga_style_original_file_tooltip":"ファイルは元のファイル名を保持しようとします。\n\n- このスタイルボタンの隣に表示される入力フィールドにオプションのプレフィックスを入力できます。\n\n例(プレフィックス「私のシリーズ」):「私のシリーズ_元のファイル.jpg」。\n例(プレフィックスなし):「元のファイル.jpg」。",
|
||||||
|
"manga_style_date_based_tooltip":"ファイルは投稿の公開順に基づいて順番に名前が付けられます(例:001.ext、002.ext)。\n\n- このスタイルボタンの隣に表示される入力フィールドにオプションのプレフィックスを入力できます。\n- このスタイルでは、正しい番号付けを保証するために、投稿処理のマルチスレッドは自動的に無効になります。\n\n例(プレフィックス「私の漫画」):「私の漫画_001.jpg」、「私の漫画_002.png」。\n例(プレフィックスなし):「001.jpg」、「002.png」。",
|
||||||
|
"manga_style_title_global_num_tooltip":"ファイルは投稿のタイトルと、すべての投稿にわたるグローバルな連番で名前が付けられます。\n\n- 形式:「[クリーンな投稿タイトル]_[グローバルカウンター].[ext]」\n- カウンター(例:_001、_002)は、現在のセッションでダウンロードされたすべてのファイルに対してインクリメントされます。\n- このスタイルでは、正しい番号付けを保証するために、投稿処理のマルチスレッドは自動的に無効になります。\n\n例:投稿「第1章」(2ファイル)->「第1章_001.jpg」、「第1章_002.png」。\n次の投稿「第2章」(1ファイル)->「第2章_003.jpg」。",
|
||||||
|
"manga_style_unknown_tooltip":"漫画のファイル名スタイルは現在不明です。これは予期しないことです。有効なスタイルに切り替えてください。",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"クリックして次のスタイルに切り替えます。",
|
||||||
"fav_artists_dialog_title":"お気に入りアーティスト",
|
"fav_artists_dialog_title":"お気に入りアーティスト",
|
||||||
"fav_artists_loading_status":"お気に入りアーティストを読み込み中...",
|
"fav_artists_loading_status":"お気に入りアーティストを読み込み中...",
|
||||||
"fav_artists_search_placeholder":"アーティストを検索...",
|
"fav_artists_search_placeholder":"アーティストを検索...",
|
||||||
@@ -667,6 +723,8 @@ translations ={
|
|||||||
"key_fetching_from_source_kemono_su":"Kemono.suからお気に入りを取得中...",
|
"key_fetching_from_source_kemono_su":"Kemono.suからお気に入りを取得中...",
|
||||||
"key_fetching_from_source_coomer_su":"Coomer.suからお気に入りを取得中...",
|
"key_fetching_from_source_coomer_su":"Coomer.suからお気に入りを取得中...",
|
||||||
"fav_posts_fetch_cancelled_status":"お気に入り投稿の取得がキャンセルされました。",
|
"fav_posts_fetch_cancelled_status":"お気に入り投稿の取得がキャンセルされました。",
|
||||||
|
"items_in_queue_placeholder": "ポップアップからキューに{count}件のアイテムがあります。",
|
||||||
|
"post_fetch_finished_status": "選択したクリエイターの投稿の取得が完了しました。",
|
||||||
|
|
||||||
"known_names_filter_dialog_title":"既知の名前をフィルターに追加",
|
"known_names_filter_dialog_title":"既知の名前をフィルターに追加",
|
||||||
"known_names_filter_search_placeholder":"名前を検索...",
|
"known_names_filter_search_placeholder":"名前を検索...",
|
||||||
@@ -1095,6 +1153,18 @@ translations ["fr"]={
|
|||||||
"creator_popup_add_selected_button":"Ajouter la sélection",
|
"creator_popup_add_selected_button":"Ajouter la sélection",
|
||||||
"creator_popup_scope_characters_button":"Portée : Personnages",
|
"creator_popup_scope_characters_button":"Portée : Personnages",
|
||||||
"creator_popup_scope_creators_button":"Portée : Créateurs",
|
"creator_popup_scope_creators_button":"Portée : Créateurs",
|
||||||
|
"creator_popup_title_fetching": "Publications du créateur",
|
||||||
|
"creator_popup_posts_area_title": "Publications récupérées",
|
||||||
|
"creator_popup_posts_search_placeholder": "Rechercher les publications récupérées par titre...",
|
||||||
|
"no_posts_fetched_yet_status": "Aucune publication récupérée pour le moment.",
|
||||||
|
"fetched_posts_count_label": "{count} publication(s) récupérée(s). Sélectionnez pour ajouter à la file d'attente.",
|
||||||
|
"no_posts_found_for_selection": "Aucune publication trouvée pour le(s) créateur(s) sélectionné(s).",
|
||||||
|
"fetched_posts_count_label_filtered": "Affichage de {count} publication(s) correspondant au filtre.",
|
||||||
|
"no_posts_match_search_filter": "Aucune publication ne correspond à votre filtre de recherche.",
|
||||||
|
"fetch_error_for_creator_label": "Erreur de récupération pour {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Récupération des publications annulée.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Échec de la récupération des publications ou aucune publication trouvée.",
|
||||||
|
"select_posts_to_queue_message": "Veuillez sélectionner au moins une publication à ajouter à la file d'attente.",
|
||||||
"favorite_artists_button_text":"🖼️ Artistes favoris",
|
"favorite_artists_button_text":"🖼️ Artistes favoris",
|
||||||
"favorite_artists_button_tooltip":"Parcourez et téléchargez depuis vos artistes favoris sur Kemono.su/Coomer.su.",
|
"favorite_artists_button_tooltip":"Parcourez et téléchargez depuis vos artistes favoris sur Kemono.su/Coomer.su.",
|
||||||
"favorite_posts_button_text":"📄 Publications favorites",
|
"favorite_posts_button_text":"📄 Publications favorites",
|
||||||
@@ -1110,6 +1180,18 @@ translations ["fr"]={
|
|||||||
"manga_style_date_based_text":"Nom : Basé sur la date",
|
"manga_style_date_based_text":"Nom : Basé sur la date",
|
||||||
"manga_style_title_global_num_text":"Nom : Titre+Num.G",
|
"manga_style_title_global_num_text":"Nom : Titre+Num.G",
|
||||||
"manga_style_unknown_text":"Nom : Style inconnu",
|
"manga_style_unknown_text":"Nom : Style inconnu",
|
||||||
|
"manga_style_post_title_tooltip":"""Les fichiers sont nommés en fonction du titre de la publication.
|
||||||
|
- Le premier fichier d'une publication est nommé en utilisant le titre nettoyé de la publication (par ex., 'Mon Chapitre 1.jpg').
|
||||||
|
- Si la publication contient plusieurs fichiers, les fichiers suivants sont également nommés en utilisant le titre de la publication, mais avec un suffixe numérique comme '_1', '_2', etc. (par ex., 'Mon Chapitre 1_1.png', 'Mon Chapitre 1_2.gif'). Le compteur pour le suffixe commence à 1 pour le deuxième fichier.
|
||||||
|
- Si une publication ne contient qu'un seul fichier, il est nommé d'après le titre de la publication sans suffixe.
|
||||||
|
|
||||||
|
Exemple : Publication 'Chapitre Un' (3 fichiers : originalA.jpg, originalB.png, originalC.gif)
|
||||||
|
Sortie : 'Chapitre Un.jpg', 'Chapitre Un_1.png', 'Chapitre Un_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"Les fichiers tentent de conserver leurs noms de fichiers originaux.\n\n- Un préfixe optionnel peut être saisi dans le champ de saisie qui apparaît à côté de ce bouton de style.\n\nExemple (avec préfixe 'MaSerie') : 'MaSerie_FichierOriginal.jpg'.\nExemple (sans préfixe) : 'FichierOriginal.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"Les fichiers sont nommés séquentiellement (par ex., 001.ext, 002.ext) en fonction de l'ordre de publication des publications.\n\n- Un préfixe optionnel peut être saisi dans le champ de saisie qui apparaît à côté de ce bouton de style.\n- Le multithreading pour le traitement des publications est automatiquement désactivé pour ce style afin d'assurer une numérotation correcte.\n\nExemple (avec préfixe 'MaBD') : 'MaBD_001.jpg', 'MaBD_002.png'.\nExemple (sans préfixe) : '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"Les fichiers sont nommés avec le titre de la publication et un numéro séquentiel global pour toutes les publications.\n\n- Format : '[Titre de la publication nettoyé]_[Compteur global].[ext]'\n- Le compteur (par ex., _001, _002) s'incrémente pour chaque fichier téléchargé dans la session actuelle.\n- Le multithreading pour le traitement des publications est automatiquement désactivé pour ce style afin d'assurer une numérotation correcte.\n\nExemple : Publication 'Chapitre 1' (2 fichiers) -> 'Chapitre 1_001.jpg', 'Chapitre 1_002.png'.\nPublication suivante 'Chapitre 2' (1 fichier) -> 'Chapitre 2_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"Le style de nom de fichier manga est actuellement inconnu. C'est inattendu. Veuillez passer à un style valide.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"Cliquez pour passer au style suivant.",
|
||||||
"fav_artists_dialog_title":"Artistes favoris",
|
"fav_artists_dialog_title":"Artistes favoris",
|
||||||
"fav_artists_loading_status":"Chargement des artistes favoris...",
|
"fav_artists_loading_status":"Chargement des artistes favoris...",
|
||||||
"fav_artists_search_placeholder":"Rechercher des artistes...",
|
"fav_artists_search_placeholder":"Rechercher des artistes...",
|
||||||
@@ -1147,6 +1229,9 @@ translations ["fr"]={
|
|||||||
"key_fetching_from_source_kemono_su":"Récupération des favoris de Kemono.su...",
|
"key_fetching_from_source_kemono_su":"Récupération des favoris de Kemono.su...",
|
||||||
"key_fetching_from_source_coomer_su":"Récupération des favoris de Coomer.su...",
|
"key_fetching_from_source_coomer_su":"Récupération des favoris de Coomer.su...",
|
||||||
"fav_posts_fetch_cancelled_status":"Récupération des publications favorites annulée.",
|
"fav_posts_fetch_cancelled_status":"Récupération des publications favorites annulée.",
|
||||||
|
"items_in_queue_placeholder": "{count} éléments en file d'attente depuis la popup.",
|
||||||
|
"post_fetch_finished_status": "Récupération des publications terminée pour les créateurs sélectionnés.",
|
||||||
|
|
||||||
"known_names_filter_dialog_title":"Ajouter des noms connus au filtre",
|
"known_names_filter_dialog_title":"Ajouter des noms connus au filtre",
|
||||||
"known_names_filter_search_placeholder":"Rechercher des noms...",
|
"known_names_filter_search_placeholder":"Rechercher des noms...",
|
||||||
"known_names_filter_select_all_button":"Tout sélectionner",
|
"known_names_filter_select_all_button":"Tout sélectionner",
|
||||||
@@ -1281,6 +1366,18 @@ translations ["fr"]={
|
|||||||
"help_guide_github_tooltip":"Visiter la page GitHub du projet (S'ouvre dans le navigateur)",
|
"help_guide_github_tooltip":"Visiter la page GitHub du projet (S'ouvre dans le navigateur)",
|
||||||
"help_guide_instagram_tooltip":"Visiter notre page Instagram (S'ouvre dans le navigateur)",
|
"help_guide_instagram_tooltip":"Visiter notre page Instagram (S'ouvre dans le navigateur)",
|
||||||
"help_guide_discord_tooltip":"Rejoindre notre communauté Discord (S'ouvre dans le navigateur)",
|
"help_guide_discord_tooltip":"Rejoindre notre communauté Discord (S'ouvre dans le navigateur)",
|
||||||
|
"creator_popup_posts_search_placeholder": "Rechercher les publications récupérées par titre...",
|
||||||
|
"no_posts_fetched_yet_status": "Aucune publication récupérée pour le moment.",
|
||||||
|
"fetched_posts_count_label": "{count} publication(s) récupérée(s). Sélectionnez pour ajouter à la file d'attente.",
|
||||||
|
"no_posts_found_for_selection": "Aucune publication trouvée pour le(s) créateur(s) sélectionné(s).",
|
||||||
|
"fetched_posts_count_label_filtered": "Affichage de {count} publication(s) correspondant au filtre.",
|
||||||
|
"no_posts_match_search_filter": "Aucune publication ne correspond à votre filtre de recherche.",
|
||||||
|
"fetch_error_for_creator_label": "Erreur de récupération pour {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Récupération des publications annulée.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Échec de la récupération des publications ou aucune publication trouvée.",
|
||||||
|
"select_posts_to_queue_message": "Veuillez sélectionner au moins une publication à ajouter à la file d'attente.",
|
||||||
|
"items_in_queue_placeholder": "{count} éléments en file d'attente depuis la popup.",
|
||||||
|
"post_fetch_finished_status": "Récupération des publications terminée pour les créateurs sélectionnés.",
|
||||||
"help_guide_step1_title":"① Introduction & Entrées principales",
|
"help_guide_step1_title":"① Introduction & Entrées principales",
|
||||||
"help_guide_step1_content":"<html><head/><body>\n<p>Ce guide offre un aperçu des fonctionnalités, des champs et des boutons de Kemono Downloader.</p>\n<h3>Zone de saisie principale (en haut à gauche)</h3>\n<ul>\n<li><b>🔗 URL Créateur/Post Kemono :</b>\n<ul>\n<li>Saisissez l'adresse web complète de la page d'un créateur (par ex., <i>https://kemono.su/patreon/user/12345</i>) ou d'une publication spécifique (par ex., <i>.../post/98765</i>).</li>\n<li>Prend en charge les URL de Kemono (kemono.su, kemono.party) et Coomer (coomer.su, coomer.party).</li>\n</ul>\n</li>\n<li><b>Plage de pages (Début à Fin) :</b>\n<ul>\n<li>Pour les URL de créateurs : Spécifiez une plage de pages à récupérer (par ex., pages 2 à 5). Laissez vide pour toutes les pages.</li>\n<li>Désactivé pour les URL de publications uniques ou lorsque le <b>Mode Manga/BD</b> est actif.</li>\n</ul>\n</li>\n<li><b>📁 Emplacement de téléchargement :</b>\n<ul>\n<li>Cliquez sur <b>'Parcourir...'</b> pour choisir un dossier principal sur votre ordinateur où tous les fichiers téléchargés seront enregistrés.</li>\n<li>Ce champ est requis sauf si vous utilisez le mode <b>'🔗 Liens Uniquement'</b>.</li>\n</ul>\n</li>\n<li><b>🎨 Bouton de sélection du créateur (à côté de la saisie de l'URL) :</b>\n<ul>\n<li>Cliquez sur l'icône de la palette (🎨) pour ouvrir la boîte de dialogue 'Sélection du créateur'.</li>\n<li>Cette boîte de dialogue charge les créateurs depuis votre fichier <code>creators.json</code> (qui doit se trouver dans le répertoire de l'application).</li>\n<li><b>À l'intérieur de la boîte de dialogue :</b>\n<ul>\n<li><b>Barre de recherche :</b> Tapez pour filtrer la liste des créateurs par nom ou service.</li>\n<li><b>Liste des créateurs :</b> Affiche les créateurs de votre <code>creators.json</code>. Les créateurs que vous avez mis en 'favoris' (dans les données JSON) apparaissent en haut.</li>\n<li><b>Cases à cocher :</b> Sélectionnez un ou plusieurs créateurs en cochant la case à côté de leur nom.</li>\n<li><b>Bouton 'Portée' (par ex., 'Portée : Personnages') :</b> Ce bouton bascule l'organisation du téléchargement lors de l'initiation des téléchargements à partir de cette popup :\n<ul><li><i>Portée : Personnages :</i> Les téléchargements seront organisés dans des dossiers nommés d'après les personnages directement dans votre 'Emplacement de téléchargement' principal. Les œuvres de différents créateurs pour le même personnage seront regroupées.</li>\n<li><i>Portée : Créateurs :</i> Les téléchargements créeront d'abord un dossier nommé d'après le créateur dans votre 'Emplacement de téléchargement' principal. Les sous-dossiers nommés d'après les personnages seront ensuite créés à l'intérieur du dossier de chaque créateur.</li></ul>\n</li>\n<li><b>Bouton 'Ajouter la sélection' :</b> Cliquer sur ce bouton prendra les noms de tous les créateurs cochés et les ajoutera au champ de saisie principal '🔗 URL Créateur/Post Kemono', séparés par des virgules. La boîte de dialogue se fermera alors.</li>\n</ul>\n</li>\n<li>Cette fonctionnalité offre un moyen rapide de remplir le champ URL pour plusieurs créateurs sans avoir à taper ou coller manuellement chaque URL.</li>\n</ul>\n</li>\n</ul></body></html>",
|
"help_guide_step1_content":"<html><head/><body>\n<p>Ce guide offre un aperçu des fonctionnalités, des champs et des boutons de Kemono Downloader.</p>\n<h3>Zone de saisie principale (en haut à gauche)</h3>\n<ul>\n<li><b>🔗 URL Créateur/Post Kemono :</b>\n<ul>\n<li>Saisissez l'adresse web complète de la page d'un créateur (par ex., <i>https://kemono.su/patreon/user/12345</i>) ou d'une publication spécifique (par ex., <i>.../post/98765</i>).</li>\n<li>Prend en charge les URL de Kemono (kemono.su, kemono.party) et Coomer (coomer.su, coomer.party).</li>\n</ul>\n</li>\n<li><b>Plage de pages (Début à Fin) :</b>\n<ul>\n<li>Pour les URL de créateurs : Spécifiez une plage de pages à récupérer (par ex., pages 2 à 5). Laissez vide pour toutes les pages.</li>\n<li>Désactivé pour les URL de publications uniques ou lorsque le <b>Mode Manga/BD</b> est actif.</li>\n</ul>\n</li>\n<li><b>📁 Emplacement de téléchargement :</b>\n<ul>\n<li>Cliquez sur <b>'Parcourir...'</b> pour choisir un dossier principal sur votre ordinateur où tous les fichiers téléchargés seront enregistrés.</li>\n<li>Ce champ est requis sauf si vous utilisez le mode <b>'🔗 Liens Uniquement'</b>.</li>\n</ul>\n</li>\n<li><b>🎨 Bouton de sélection du créateur (à côté de la saisie de l'URL) :</b>\n<ul>\n<li>Cliquez sur l'icône de la palette (🎨) pour ouvrir la boîte de dialogue 'Sélection du créateur'.</li>\n<li>Cette boîte de dialogue charge les créateurs depuis votre fichier <code>creators.json</code> (qui doit se trouver dans le répertoire de l'application).</li>\n<li><b>À l'intérieur de la boîte de dialogue :</b>\n<ul>\n<li><b>Barre de recherche :</b> Tapez pour filtrer la liste des créateurs par nom ou service.</li>\n<li><b>Liste des créateurs :</b> Affiche les créateurs de votre <code>creators.json</code>. Les créateurs que vous avez mis en 'favoris' (dans les données JSON) apparaissent en haut.</li>\n<li><b>Cases à cocher :</b> Sélectionnez un ou plusieurs créateurs en cochant la case à côté de leur nom.</li>\n<li><b>Bouton 'Portée' (par ex., 'Portée : Personnages') :</b> Ce bouton bascule l'organisation du téléchargement lors de l'initiation des téléchargements à partir de cette popup :\n<ul><li><i>Portée : Personnages :</i> Les téléchargements seront organisés dans des dossiers nommés d'après les personnages directement dans votre 'Emplacement de téléchargement' principal. Les œuvres de différents créateurs pour le même personnage seront regroupées.</li>\n<li><i>Portée : Créateurs :</i> Les téléchargements créeront d'abord un dossier nommé d'après le créateur dans votre 'Emplacement de téléchargement' principal. Les sous-dossiers nommés d'après les personnages seront ensuite créés à l'intérieur du dossier de chaque créateur.</li></ul>\n</li>\n<li><b>Bouton 'Ajouter la sélection' :</b> Cliquer sur ce bouton prendra les noms de tous les créateurs cochés et les ajoutera au champ de saisie principal '🔗 URL Créateur/Post Kemono', séparés par des virgules. La boîte de dialogue se fermera alors.</li>\n</ul>\n</li>\n<li>Cette fonctionnalité offre un moyen rapide de remplir le champ URL pour plusieurs créateurs sans avoir à taper ou coller manuellement chaque URL.</li>\n</ul>\n</li>\n</ul></body></html>",
|
||||||
"help_guide_step2_title":"② Filtrage des téléchargements",
|
"help_guide_step2_title":"② Filtrage des téléchargements",
|
||||||
@@ -1302,11 +1399,41 @@ translations ["fr"]={
|
|||||||
}
|
}
|
||||||
|
|
||||||
translations ["en"].update ({
|
translations ["en"].update ({
|
||||||
|
|
||||||
|
"creator_popup_title":"Creator Selection",
|
||||||
|
"creator_popup_title_fetching": "Creator Posts", # New key
|
||||||
|
"creator_popup_search_placeholder":"Search by name, service, or paste creator URL...",
|
||||||
|
"creator_popup_add_selected_button": "Add Selected", # Already exists, but good to confirm
|
||||||
|
"fetch_posts_button_text": "Fetch Posts",
|
||||||
|
"creator_popup_scope_characters_button": "Scope: Characters",
|
||||||
|
"creator_popup_title_fetching": "Beiträge des Erstellers",
|
||||||
|
"creator_popup_posts_area_title": "Abgerufene Beiträge",
|
||||||
|
"creator_popup_posts_search_placeholder": "Abgerufene Beiträge nach Titel suchen...",
|
||||||
|
"no_posts_fetched_yet_status": "Noch keine Beiträge abgerufen.",
|
||||||
|
"fetched_posts_count_label": "{count} Beitrag/Beiträge abgerufen. Zum Hinzufügen zur Warteschlange auswählen.",
|
||||||
|
"no_posts_found_for_selection": "Keine Beiträge für ausgewählte(n) Ersteller gefunden.",
|
||||||
|
"fetched_posts_count_label_filtered": "Zeige {count} Beitrag/Beiträge an, die dem Filter entsprechen.",
|
||||||
|
"no_posts_match_search_filter": "Keine Beiträge entsprechen Ihrem Suchfilter.",
|
||||||
|
"fetch_error_for_creator_label": "Fehler beim Abrufen für {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Abruf von Beiträgen abgebrochen.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Fehler beim Abrufen von Beiträgen oder keine Beiträge gefunden.",
|
||||||
|
"select_posts_to_queue_message": "Bitte wählen Sie mindestens einen Beitrag zum Hinzufügen zur Warteschlange aus.",
|
||||||
"help_guide_dialog_title":"Kemono Downloader - Feature Guide",
|
"help_guide_dialog_title":"Kemono Downloader - Feature Guide",
|
||||||
"help_guide_github_tooltip":"Visit project's GitHub page (Opens in browser)",
|
"help_guide_github_tooltip":"Visit project's GitHub page (Opens in browser)",
|
||||||
"help_guide_instagram_tooltip":"Visit our Instagram page (Opens in browser)",
|
"help_guide_instagram_tooltip":"Visit our Instagram page (Opens in browser)",
|
||||||
"help_guide_discord_tooltip":"Visit our Discord community (Opens in browser)",
|
"help_guide_discord_tooltip":"Visit our Discord community (Opens in browser)",
|
||||||
"help_guide_step1_title":"① Introduction & Main Inputs",
|
"help_guide_step1_title":"① Introduction & Main Inputs",
|
||||||
|
"creator_popup_posts_area_title": "Fetched Posts", # Already exists
|
||||||
|
"creator_popup_posts_search_placeholder": "Search fetched posts by title...", # New key
|
||||||
|
"no_posts_fetched_yet_status": "No posts fetched yet.", # New key
|
||||||
|
"fetched_posts_count_label": "Fetched {count} post(s). Select to add to queue.", # New key
|
||||||
|
"no_posts_found_for_selection": "No posts found for selected creator(s).", # New key
|
||||||
|
"fetched_posts_count_label_filtered": "Displaying {count} post(s) matching filter.", # New key
|
||||||
|
"no_posts_match_search_filter": "No posts match your search filter.", # New key
|
||||||
|
"fetch_error_for_creator_label": "Error fetching for {creator_name}", # New key
|
||||||
|
"post_fetch_cancelled_status_done": "Post fetching cancelled.", # New key
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Failed to fetch posts or no posts found.", # New key
|
||||||
|
"select_posts_to_queue_message": "Please select at least one post to add to the queue.", # New key
|
||||||
"help_guide_step1_content":"""<html><head/><body>
|
"help_guide_step1_content":"""<html><head/><body>
|
||||||
<p>This guide provides an overview of the Kemono Downloader's features, fields, and buttons.</p>
|
<p>This guide provides an overview of the Kemono Downloader's features, fields, and buttons.</p>
|
||||||
<h3>Main Input Area (Top Left)</h3>
|
<h3>Main Input Area (Top Left)</h3>
|
||||||
@@ -1539,6 +1666,7 @@ translations ["en"].update ({
|
|||||||
"help_guide_step8_title":"⑧ Favorite Mode & Future Features",
|
"help_guide_step8_title":"⑧ Favorite Mode & Future Features",
|
||||||
"help_guide_step8_content":"""<html><head/><body>
|
"help_guide_step8_content":"""<html><head/><body>
|
||||||
<h3>Favorite Mode (Downloading from Your Kemono.su Favorites)</h3>
|
<h3>Favorite Mode (Downloading from Your Kemono.su Favorites)</h3>
|
||||||
|
<p>This mode allows you to download content directly from artists you've favorited on Kemono.su/Coomer.su.</p> <!-- Added Coomer -->
|
||||||
<p>This mode allows you to download content directly from artists you've favorited on Kemono.su.</p>
|
<p>This mode allows you to download content directly from artists you've favorited on Kemono.su.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>⭐ How to Enable:</b>
|
<li><b>⭐ How to Enable:</b>
|
||||||
@@ -1560,7 +1688,7 @@ translations ["en"].update ({
|
|||||||
</li>
|
</li>
|
||||||
<li><b>🖼️ Favorite Artists Button:</b>
|
<li><b>🖼️ Favorite Artists Button:</b>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Clicking this opens a dialog that lists all artists you have favorited on Kemono.su.</li>
|
<li>Clicking this opens a dialog that lists all artists you have favorited on Kemono.su and Coomer.su.</li> <!-- Added Coomer -->
|
||||||
<li>You can select one or more artists from this list to download their content.</li>
|
<li>You can select one or more artists from this list to download their content.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -1590,6 +1718,8 @@ translations ["en"].update ({
|
|||||||
"help_guide_step9_title":"⑨ Key Files & Tour",
|
"help_guide_step9_title":"⑨ Key Files & Tour",
|
||||||
"help_guide_step9_content":"""<html><head/><body>
|
"help_guide_step9_content":"""<html><head/><body>
|
||||||
<h3>Key Files Used by the Application</h3>
|
<h3>Key Files Used by the Application</h3>
|
||||||
|
<p><b><code>creators.json</code> (Optional but Recommended):</b></p> <!-- Added creators.json -->
|
||||||
|
<ul><li>Located in the application's directory. This file stores a list of creators (name, service, ID, and an optional 'favorited' flag). The 'Creator Selection' popup (🎨 icon) uses this file to let you quickly select creators to add to the URL input. You can manually edit this JSON file to add or manage your list of creators.</li></ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><b><code>Known.txt</code>:</b>
|
<li><b><code>Known.txt</code>:</b>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -1611,7 +1741,7 @@ translations ["en"].update ({
|
|||||||
<li><b>Format:</b> Must be in Netscape cookie file format.</li>
|
<li><b>Format:</b> Must be in Netscape cookie file format.</li>
|
||||||
<li><b>Usage:</b> Allows the downloader to use your browser's login session for accessing content that might be behind a login on Kemono/Coomer.</li>
|
<li><b>Usage:</b> Allows the downloader to use your browser's login session for accessing content that might be behind a login on Kemono/Coomer.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>,
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>First-Time User Tour</h3>
|
<h3>First-Time User Tour</h3>
|
||||||
@@ -1619,7 +1749,9 @@ translations ["en"].update ({
|
|||||||
<li>On the first launch (or if reset), a welcome tour dialog appears, guiding you through the main features. You can skip it or choose to "Never show this tour again."</li>
|
<li>On the first launch (or if reset), a welcome tour dialog appears, guiding you through the main features. You can skip it or choose to "Never show this tour again."</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Many UI elements also have tooltips that appear when you hover your mouse over them, providing quick hints.</em></p>
|
<p><em>Many UI elements also have tooltips that appear when you hover your mouse over them, providing quick hints.</em></p>
|
||||||
</body></html>"""
|
</body></html>""",
|
||||||
|
"items_in_queue_placeholder": "{count} items in queue from popup.", # New key
|
||||||
|
"post_fetch_finished_status": "Finished fetching posts for selected creators." # New key
|
||||||
})
|
})
|
||||||
|
|
||||||
translations ["ja"].update ({
|
translations ["ja"].update ({
|
||||||
@@ -1627,6 +1759,18 @@ translations ["ja"].update ({
|
|||||||
"help_guide_github_tooltip":"プロジェクトのGitHubページにアクセス (ブラウザで開きます)",
|
"help_guide_github_tooltip":"プロジェクトのGitHubページにアクセス (ブラウザで開きます)",
|
||||||
"help_guide_instagram_tooltip":"Instagramページにアクセス (ブラウザで開きます)",
|
"help_guide_instagram_tooltip":"Instagramページにアクセス (ブラウザで開きます)",
|
||||||
"help_guide_discord_tooltip":"Discordコミュニティにアクセス (ブラウザで開きます)",
|
"help_guide_discord_tooltip":"Discordコミュニティにアクセス (ブラウザで開きます)",
|
||||||
|
"creator_popup_posts_search_placeholder": "タイトルで取得済み投稿を検索...",
|
||||||
|
"no_posts_fetched_yet_status": "まだ投稿が取得されていません。",
|
||||||
|
"fetched_posts_count_label": "{count}件の投稿を取得しました。キューに追加するものを選択してください。",
|
||||||
|
"no_posts_found_for_selection": "選択したクリエイターの投稿が見つかりませんでした。",
|
||||||
|
"fetched_posts_count_label_filtered": "フィルターに一致する{count}件の投稿を表示中。",
|
||||||
|
"no_posts_match_search_filter": "検索フィルターに一致する投稿がありません。",
|
||||||
|
"fetch_error_for_creator_label": "{creator_name}の取得エラー",
|
||||||
|
"post_fetch_cancelled_status_done": "投稿の取得がキャンセルされました。",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "投稿の取得に失敗したか、投稿が見つかりませんでした。",
|
||||||
|
"select_posts_to_queue_message": "キューに追加する投稿を少なくとも1つ選択してください。",
|
||||||
|
"items_in_queue_placeholder": "ポップアップからキューに{count}件のアイテムがあります。",
|
||||||
|
"post_fetch_finished_status": "選択したクリエイターの投稿の取得が完了しました。",
|
||||||
"help_guide_step1_title":"① 概要と主な入力",
|
"help_guide_step1_title":"① 概要と主な入力",
|
||||||
"help_guide_step1_content":"""<html><head/><body>
|
"help_guide_step1_content":"""<html><head/><body>
|
||||||
<p>このガイドでは、Kemonoダウンローダーの機能、フィールド、ボタンの概要を説明します。</p>
|
<p>このガイドでは、Kemonoダウンローダーの機能、フィールド、ボタンの概要を説明します。</p>
|
||||||
@@ -2061,6 +2205,18 @@ translations ["zh_CN"].update ({
|
|||||||
"creator_popup_add_selected_button":"添加所选",
|
"creator_popup_add_selected_button":"添加所选",
|
||||||
"creator_popup_scope_characters_button":"范围:角色",
|
"creator_popup_scope_characters_button":"范围:角色",
|
||||||
"creator_popup_scope_creators_button":"范围:创作者",
|
"creator_popup_scope_creators_button":"范围:创作者",
|
||||||
|
"creator_popup_title_fetching": "创作者帖子",
|
||||||
|
"creator_popup_posts_area_title": "已获取的帖子",
|
||||||
|
"creator_popup_posts_search_placeholder": "按标题搜索已获取的帖子...",
|
||||||
|
"no_posts_fetched_yet_status": "尚未获取任何帖子。",
|
||||||
|
"fetched_posts_count_label": "已获取 {count} 个帖子。选择以添加到队列。",
|
||||||
|
"no_posts_found_for_selection": "未找到所选创作者的帖子。",
|
||||||
|
"fetched_posts_count_label_filtered": "正在显示 {count} 个符合筛选条件的帖子。",
|
||||||
|
"no_posts_match_search_filter": "没有帖子符合您的搜索筛选条件。",
|
||||||
|
"fetch_error_for_creator_label": "获取 {creator_name} 时出错",
|
||||||
|
"post_fetch_cancelled_status_done": "帖子获取已取消。",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "获取帖子失败或未找到帖子。",
|
||||||
|
"select_posts_to_queue_message": "请至少选择一个帖子以添加到队列。",
|
||||||
"favorite_artists_button_text":"🖼️ 收藏的艺术家",
|
"favorite_artists_button_text":"🖼️ 收藏的艺术家",
|
||||||
"favorite_artists_button_tooltip":"浏览并从您在 Kemono.su/Coomer.su 上收藏的艺术家那里下载。",
|
"favorite_artists_button_tooltip":"浏览并从您在 Kemono.su/Coomer.su 上收藏的艺术家那里下载。",
|
||||||
"favorite_posts_button_text":"📄 收藏的帖子",
|
"favorite_posts_button_text":"📄 收藏的帖子",
|
||||||
@@ -2113,6 +2269,8 @@ translations ["zh_CN"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"正在从 Kemono.su 获取收藏...",
|
"key_fetching_from_source_kemono_su":"正在从 Kemono.su 获取收藏...",
|
||||||
"key_fetching_from_source_coomer_su":"正在从 Coomer.su 获取收藏...",
|
"key_fetching_from_source_coomer_su":"正在从 Coomer.su 获取收藏...",
|
||||||
"fav_posts_fetch_cancelled_status":"收藏帖子获取已取消。",
|
"fav_posts_fetch_cancelled_status":"收藏帖子获取已取消。",
|
||||||
|
"items_in_queue_placeholder": "来自弹出窗口的队列中有 {count} 个项目。",
|
||||||
|
"post_fetch_finished_status": "已完成获取所选创作者的帖子。",
|
||||||
"known_names_filter_dialog_title":"将已知名称添加到筛选器",
|
"known_names_filter_dialog_title":"将已知名称添加到筛选器",
|
||||||
"known_names_filter_search_placeholder":"搜索名称...",
|
"known_names_filter_search_placeholder":"搜索名称...",
|
||||||
"known_names_filter_select_all_button":"全选",
|
"known_names_filter_select_all_button":"全选",
|
||||||
@@ -2265,6 +2423,7 @@ translations ["zh_CN"].update ({
|
|||||||
"help_guide_step8_content":"<html><head/><body>\n<h3>收藏模式(从您的 Kemono.su 收藏中下载)</h3>\n<p>此模式允许您直接从您在 Kemono.su 上收藏的艺术家那里下载内容。</p>\n<ul>\n<li><b>⭐ 如何启用:</b>\n<ul>\n<li>选中位于“🔗 仅链接”单选按钮旁边的<b>“⭐ 收藏模式”</b>复选框。</li>\n</ul>\n</li>\n<li><b>收藏模式下的界面更改:</b>\n<ul>\n<li>“🔗 Kemono 创建者/帖子 URL”输入区域被一条消息替换,指示收藏模式已激活。</li>\n<li>标准的“开始下载”、“暂停”、“取消”按钮被替换为:\n<ul>\n<li><b>“🖼️ 收藏的艺术家”</b>按钮</li>\n<li><b>“📄 收藏的帖子”</b>按钮</li>\n</ul>\n</li>\n<li>“🍪 使用 Cookie”选项被自动启用并锁定,因为获取您的收藏需要 cookie。</li>\n</ul>\n</li>\n<li><b>🖼️ 收藏的艺术家按钮:</b>\n<ul>\n<li>单击此按钮将打开一个对话框,其中列出了您在 Kemono.su 上收藏的所有艺术家。</li>\n<li>您可以从此列表中选择一个或多个艺术家以下载其内容。</li>\n</ul>\n</li>\n<li><b>📄 收藏的帖子按钮(未来功能):</b>\n<ul>\n<li>下载特定的收藏<i>帖子</i>(尤其是在它们是系列的一部分时,以类似漫画的顺序)是目前正在开发的功能。</li>\n<li>处理收藏帖子的最佳方式,特别是对于像漫画这样的顺序阅读,仍在探索中。</li>\n<li>如果您对如何下载和组织收藏帖子有具体的想法或用例(例如,从收藏中“漫画风格”),请考虑在项目的 GitHub 页面上提出问题或加入讨论。您的意见非常宝贵!</li>\n</ul>\n</li>\n<li><b>收藏下载范围(按钮):</b>\n<ul>\n<li>此按钮(在“收藏的帖子”旁边)控制从所选收藏艺术家那里下载内容的位置:\n<ul>\n<li><b><i>范围:所选位置:</i></b>所有选定的艺术家都下载到您在界面中设置的主“下载位置”。筛选器全局应用于所有内容。</li>\n<li><b><i>范围:艺术家文件夹:</i></b>对于每个选定的艺术家,将在您的主“下载位置”内自动创建一个子文件夹(以艺术家命名)。该艺术家的内容将进入其特定的子文件夹。筛选器在每个艺术家的专用文件夹内应用。</li>\n</ul>\n</li>\n</ul>\n</li>\n<li><b>收藏模式下的筛选器:</b>\n<ul>\n<li>您在界面中设置的“🎯 按角色筛选”、“🚫 使用关键词跳过”和“筛选文件”选项仍将适用于从您选定的收藏艺术家那里下载的内容。</li>\n</ul>\n</li>\n</ul></body></html>",
|
"help_guide_step8_content":"<html><head/><body>\n<h3>收藏模式(从您的 Kemono.su 收藏中下载)</h3>\n<p>此模式允许您直接从您在 Kemono.su 上收藏的艺术家那里下载内容。</p>\n<ul>\n<li><b>⭐ 如何启用:</b>\n<ul>\n<li>选中位于“🔗 仅链接”单选按钮旁边的<b>“⭐ 收藏模式”</b>复选框。</li>\n</ul>\n</li>\n<li><b>收藏模式下的界面更改:</b>\n<ul>\n<li>“🔗 Kemono 创建者/帖子 URL”输入区域被一条消息替换,指示收藏模式已激活。</li>\n<li>标准的“开始下载”、“暂停”、“取消”按钮被替换为:\n<ul>\n<li><b>“🖼️ 收藏的艺术家”</b>按钮</li>\n<li><b>“📄 收藏的帖子”</b>按钮</li>\n</ul>\n</li>\n<li>“🍪 使用 Cookie”选项被自动启用并锁定,因为获取您的收藏需要 cookie。</li>\n</ul>\n</li>\n<li><b>🖼️ 收藏的艺术家按钮:</b>\n<ul>\n<li>单击此按钮将打开一个对话框,其中列出了您在 Kemono.su 上收藏的所有艺术家。</li>\n<li>您可以从此列表中选择一个或多个艺术家以下载其内容。</li>\n</ul>\n</li>\n<li><b>📄 收藏的帖子按钮(未来功能):</b>\n<ul>\n<li>下载特定的收藏<i>帖子</i>(尤其是在它们是系列的一部分时,以类似漫画的顺序)是目前正在开发的功能。</li>\n<li>处理收藏帖子的最佳方式,特别是对于像漫画这样的顺序阅读,仍在探索中。</li>\n<li>如果您对如何下载和组织收藏帖子有具体的想法或用例(例如,从收藏中“漫画风格”),请考虑在项目的 GitHub 页面上提出问题或加入讨论。您的意见非常宝贵!</li>\n</ul>\n</li>\n<li><b>收藏下载范围(按钮):</b>\n<ul>\n<li>此按钮(在“收藏的帖子”旁边)控制从所选收藏艺术家那里下载内容的位置:\n<ul>\n<li><b><i>范围:所选位置:</i></b>所有选定的艺术家都下载到您在界面中设置的主“下载位置”。筛选器全局应用于所有内容。</li>\n<li><b><i>范围:艺术家文件夹:</i></b>对于每个选定的艺术家,将在您的主“下载位置”内自动创建一个子文件夹(以艺术家命名)。该艺术家的内容将进入其特定的子文件夹。筛选器在每个艺术家的专用文件夹内应用。</li>\n</ul>\n</li>\n</ul>\n</li>\n<li><b>收藏模式下的筛选器:</b>\n<ul>\n<li>您在界面中设置的“🎯 按角色筛选”、“🚫 使用关键词跳过”和“筛选文件”选项仍将适用于从您选定的收藏艺术家那里下载的内容。</li>\n</ul>\n</li>\n</ul></body></html>",
|
||||||
"help_guide_step9_title":"⑨ 关键文件与导览",
|
"help_guide_step9_title":"⑨ 关键文件与导览",
|
||||||
"help_guide_step9_content":"<html><head/><body>\n<h3>应用程序使用的关键文件</h3>\n<ul>\n<li><b><code>Known.txt</code>:</b>\n<ul>\n<li>位于应用程序的目录中(<code>.exe</code> 或 <code>main.py</code> 所在的位置)。</li>\n<li>在启用“按名称/标题分文件夹”时,存储您已知的系列、角色或系列标题列表,用于自动文件夹组织。</li>\n<li><b>格式:</b>\n<ul>\n<li>每一行都是一个条目。</li>\n<li><b>简单名称:</b>例如,<code>我的精彩系列</code>。匹配此内容的内容将进入名为“我的精彩系列”的文件夹。</li>\n<li><b>分组别名:</b>例如,<code>(角色 A, 角色 A, 备用名 A)</code>。匹配“角色 A”、“角色 A”或“备用名 A”的内容将全部进入一个名为“角色 A 角色 A 备用名 A”的文件夹(清理后)。括号中的所有术语都成为该文件夹的别名。</li>\n</ul>\n</li>\n<li><b>用法:</b>如果帖子与您的活动“按角色筛选”输入不匹配,则用作文件夹命名的后备。您可以通过界面管理简单的条目,或直接编辑文件以获取复杂的别名。应用程序会在启动或下次使用时重新加载它。</li>\n</ul>\n</li>\n<li><b><code>cookies.txt</code>(可选):</b>\n<ul>\n<li>如果您使用“使用 Cookie”功能并且不提供直接的 cookie 字符串或浏览到特定文件,应用程序将在其目录中查找名为 <code>cookies.txt</code> 的文件。</li>\n<li><b>格式:</b>必须是 Netscape cookie 文件格式。</li>\n<li><b>用法:</b>允许下载器使用您的浏览器的登录会话来访问可能需要在 Kemono/Coomer 上登录的内容。</li>\n</ul>\n</li>\n</ul>\n<h3>首次用户导览</h3>\n<ul>\n<li>在首次启动时(或如果重置),会出现一个欢迎导览对话框,引导您了解主要功能。您可以跳过它或选择“不再显示此导览”。</li>\n</ul>\n<p><em>许多界面元素还具有工具提示,当您将鼠标悬停在它们上面时会出现,提供快速提示。</em></p>\n</body></html>"
|
"help_guide_step9_content":"<html><head/><body>\n<h3>应用程序使用的关键文件</h3>\n<ul>\n<li><b><code>Known.txt</code>:</b>\n<ul>\n<li>位于应用程序的目录中(<code>.exe</code> 或 <code>main.py</code> 所在的位置)。</li>\n<li>在启用“按名称/标题分文件夹”时,存储您已知的系列、角色或系列标题列表,用于自动文件夹组织。</li>\n<li><b>格式:</b>\n<ul>\n<li>每一行都是一个条目。</li>\n<li><b>简单名称:</b>例如,<code>我的精彩系列</code>。匹配此内容的内容将进入名为“我的精彩系列”的文件夹。</li>\n<li><b>分组别名:</b>例如,<code>(角色 A, 角色 A, 备用名 A)</code>。匹配“角色 A”、“角色 A”或“备用名 A”的内容将全部进入一个名为“角色 A 角色 A 备用名 A”的文件夹(清理后)。括号中的所有术语都成为该文件夹的别名。</li>\n</ul>\n</li>\n<li><b>用法:</b>如果帖子与您的活动“按角色筛选”输入不匹配,则用作文件夹命名的后备。您可以通过界面管理简单的条目,或直接编辑文件以获取复杂的别名。应用程序会在启动或下次使用时重新加载它。</li>\n</ul>\n</li>\n<li><b><code>cookies.txt</code>(可选):</b>\n<ul>\n<li>如果您使用“使用 Cookie”功能并且不提供直接的 cookie 字符串或浏览到特定文件,应用程序将在其目录中查找名为 <code>cookies.txt</code> 的文件。</li>\n<li><b>格式:</b>必须是 Netscape cookie 文件格式。</li>\n<li><b>用法:</b>允许下载器使用您的浏览器的登录会话来访问可能需要在 Kemono/Coomer 上登录的内容。</li>\n</ul>\n</li>\n</ul>\n<h3>首次用户导览</h3>\n<ul>\n<li>在首次启动时(或如果重置),会出现一个欢迎导览对话框,引导您了解主要功能。您可以跳过它或选择“不再显示此导览”。</li>\n</ul>\n<p><em>许多界面元素还具有工具提示,当您将鼠标悬停在它们上面时会出现,提供快速提示。</em></p>\n</body></html>"
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
translations ["ru"]={}
|
translations ["ru"]={}
|
||||||
@@ -2385,6 +2544,18 @@ translations ["ru"].update ({
|
|||||||
"creator_popup_add_selected_button":"Добавить выбранные",
|
"creator_popup_add_selected_button":"Добавить выбранные",
|
||||||
"creator_popup_scope_characters_button":"Область: Персонажи",
|
"creator_popup_scope_characters_button":"Область: Персонажи",
|
||||||
"creator_popup_scope_creators_button":"Область: Авторы",
|
"creator_popup_scope_creators_button":"Область: Авторы",
|
||||||
|
"creator_popup_title_fetching": "Посты автора",
|
||||||
|
"creator_popup_posts_area_title": "Загруженные посты",
|
||||||
|
"creator_popup_posts_search_placeholder": "Искать загруженные посты по названию...",
|
||||||
|
"no_posts_fetched_yet_status": "Посты еще не загружены.",
|
||||||
|
"fetched_posts_count_label": "Загружено {count} постов. Выберите для добавления в очередь.",
|
||||||
|
"no_posts_found_for_selection": "Для выбранных авторов посты не найдены.",
|
||||||
|
"fetched_posts_count_label_filtered": "Отображается {count} постов, соответствующих фильтру.",
|
||||||
|
"no_posts_match_search_filter": "Нет постов, соответствующих вашему поисковому фильтру.",
|
||||||
|
"fetch_error_for_creator_label": "Ошибка загрузки для {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Загрузка постов отменена.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Не удалось загрузить посты или посты не найдены.",
|
||||||
|
"select_posts_to_queue_message": "Пожалуйста, выберите хотя бы один пост для добавления в очередь.",
|
||||||
"favorite_artists_button_text":"🖼️ Избранные художники",
|
"favorite_artists_button_text":"🖼️ Избранные художники",
|
||||||
"favorite_artists_button_tooltip":"Просматривайте и скачивайте работы ваших любимых художников на Kemono.su/Coomer.su.",
|
"favorite_artists_button_tooltip":"Просматривайте и скачивайте работы ваших любимых художников на Kemono.su/Coomer.su.",
|
||||||
"favorite_posts_button_text":"📄 Избранные посты",
|
"favorite_posts_button_text":"📄 Избранные посты",
|
||||||
@@ -2400,6 +2571,18 @@ translations ["ru"].update ({
|
|||||||
"manga_style_date_based_text":"Название: На основе даты",
|
"manga_style_date_based_text":"Название: На основе даты",
|
||||||
"manga_style_title_global_num_text":"Название: Заголовок+Г.ном.",
|
"manga_style_title_global_num_text":"Название: Заголовок+Г.ном.",
|
||||||
"manga_style_unknown_text":"Название: Неизвестный стиль",
|
"manga_style_unknown_text":"Название: Неизвестный стиль",
|
||||||
|
"manga_style_post_title_tooltip":"""Файлы именуются на основе заголовка поста.
|
||||||
|
- Первый файл в посте именуется с использованием очищенного заголовка поста (например, 'Моя глава 1.jpg').
|
||||||
|
- Если в посте несколько файлов, последующие файлы также именуются с использованием заголовка поста, но с числовым суффиксом, таким как '_1', '_2' и т. д. (например, 'Моя глава 1_1.png', 'Моя глава 1_2.gif'). Счетчик для суффикса начинается с 1 для второго файла.
|
||||||
|
- Если в посте только один файл, он именуется по заголовку поста без суффикса.
|
||||||
|
|
||||||
|
Пример: Пост 'Глава первая' (3 файла: originalA.jpg, originalB.png, originalC.gif)
|
||||||
|
Вывод: 'Глава первая.jpg', 'Глава первая_1.png', 'Глава первая_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"Файлы пытаются сохранить свои исходные имена файлов.\n\n- Необязательный префикс можно ввести в поле ввода, которое появляется рядом с этой кнопкой стиля.\n\nПример (с префиксом 'МояСерия'): 'МояСерия_ИсходныйФайл.jpg'.\nПример (без префикса): 'ИсходныйФайл.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"Файлы именуются последовательно (например, 001.ext, 002.ext) на основе порядка публикации постов.\n\n- Необязательный префикс можно ввести в поле ввода, которое появляется рядом с этой кнопкой стиля.\n- Многопоточность для обработки постов автоматически отключается для этого стиля, чтобы обеспечить правильную нумерацию.\n\nПример (с префиксом 'МойКомикс'): 'МойКомикс_001.jpg', 'МойКомикс_002.png'.\nПример (без префикса): '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"Файлы именуются с заголовком поста и глобальным порядковым номером по всем постам.\n\n- Формат: '[Очищенный заголовок поста]_[Глобальный счетчик].[ext]'\n- Счетчик (например, _001, _002) увеличивается для каждого скачанного файла в текущей сессии.\n- Многопоточность для обработки постов автоматически отключается для этого стиля, чтобы обеспечить правильную нумерацию.\n\nПример: Пост 'Глава 1' (2 файла) -> 'Глава 1_001.jpg', 'Глава 1_002.png'.\nСледующий пост 'Глава 2' (1 файл) -> 'Глава 2_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"Стиль именования файлов манги в настоящее время неизвестен. Это неожиданно. Пожалуйста, переключитесь на допустимый стиль.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"Нажмите, чтобы переключиться на следующий стиль.",
|
||||||
"fav_artists_dialog_title":"Избранные художники",
|
"fav_artists_dialog_title":"Избранные художники",
|
||||||
"fav_artists_loading_status":"Загрузка избранных художников...",
|
"fav_artists_loading_status":"Загрузка избранных художников...",
|
||||||
"fav_artists_search_placeholder":"Поиск художников...",
|
"fav_artists_search_placeholder":"Поиск художников...",
|
||||||
@@ -2437,6 +2620,8 @@ translations ["ru"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"Загрузка избранного с Kemono.su...",
|
"key_fetching_from_source_kemono_su":"Загрузка избранного с Kemono.su...",
|
||||||
"key_fetching_from_source_coomer_su":"Загрузка избранного с Coomer.su...",
|
"key_fetching_from_source_coomer_su":"Загрузка избранного с Coomer.su...",
|
||||||
"fav_posts_fetch_cancelled_status":"Загрузка избранных постов отменена.",
|
"fav_posts_fetch_cancelled_status":"Загрузка избранных постов отменена.",
|
||||||
|
"items_in_queue_placeholder": "{count} элементов в очереди из всплывающего окна.",
|
||||||
|
"post_fetch_finished_status": "Загрузка постов для выбранных авторов завершена.",
|
||||||
"known_names_filter_dialog_title":"Добавить известные имена в фильтр",
|
"known_names_filter_dialog_title":"Добавить известные имена в фильтр",
|
||||||
"known_names_filter_search_placeholder":"Поиск имен...",
|
"known_names_filter_search_placeholder":"Поиск имен...",
|
||||||
"known_names_filter_select_all_button":"Выбрать все",
|
"known_names_filter_select_all_button":"Выбрать все",
|
||||||
@@ -2709,6 +2894,18 @@ translations ["ko"].update ({
|
|||||||
"creator_popup_add_selected_button":"선택 항목 추가",
|
"creator_popup_add_selected_button":"선택 항목 추가",
|
||||||
"creator_popup_scope_characters_button":"범위: 캐릭터",
|
"creator_popup_scope_characters_button":"범위: 캐릭터",
|
||||||
"creator_popup_scope_creators_button":"범위: 작성자",
|
"creator_popup_scope_creators_button":"범위: 작성자",
|
||||||
|
"creator_popup_title_fetching": "작성자 게시물",
|
||||||
|
"creator_popup_posts_area_title": "가져온 게시물",
|
||||||
|
"creator_popup_posts_search_placeholder": "제목으로 가져온 게시물 검색...",
|
||||||
|
"no_posts_fetched_yet_status": "아직 가져온 게시물이 없습니다.",
|
||||||
|
"fetched_posts_count_label": "{count}개의 게시물을 가져왔습니다. 대기열에 추가할 항목을 선택하십시오.",
|
||||||
|
"no_posts_found_for_selection": "선택한 작성자에 대한 게시물을 찾을 수 없습니다.",
|
||||||
|
"fetched_posts_count_label_filtered": "필터와 일치하는 {count}개의 게시물을 표시합니다.",
|
||||||
|
"no_posts_match_search_filter": "검색 필터와 일치하는 게시물이 없습니다.",
|
||||||
|
"fetch_error_for_creator_label": "{creator_name} 가져오기 오류",
|
||||||
|
"post_fetch_cancelled_status_done": "게시물 가져오기가 취소되었습니다.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "게시물을 가져오지 못했거나 게시물을 찾을 수 없습니다.",
|
||||||
|
"select_posts_to_queue_message": "대기열에 추가할 게시물을 하나 이상 선택하십시오.",
|
||||||
"favorite_artists_button_text":"🖼️ 즐겨찾는 아티스트",
|
"favorite_artists_button_text":"🖼️ 즐겨찾는 아티스트",
|
||||||
"favorite_artists_button_tooltip":"Kemono.su/Coomer.su에서 즐겨찾는 아티스트를 탐색하고 다운로드하십시오.",
|
"favorite_artists_button_tooltip":"Kemono.su/Coomer.su에서 즐겨찾는 아티스트를 탐색하고 다운로드하십시오.",
|
||||||
"favorite_posts_button_text":"📄 즐겨찾는 게시물",
|
"favorite_posts_button_text":"📄 즐겨찾는 게시물",
|
||||||
@@ -2724,6 +2921,18 @@ translations ["ko"].update ({
|
|||||||
"manga_style_date_based_text":"이름: 날짜 기반",
|
"manga_style_date_based_text":"이름: 날짜 기반",
|
||||||
"manga_style_title_global_num_text":"이름: 제목+전역 번호",
|
"manga_style_title_global_num_text":"이름: 제목+전역 번호",
|
||||||
"manga_style_unknown_text":"이름: 알 수 없는 스타일",
|
"manga_style_unknown_text":"이름: 알 수 없는 스타일",
|
||||||
|
"manga_style_post_title_tooltip":"""파일은 게시물 제목을 기준으로 이름이 지정됩니다.
|
||||||
|
- 게시물의 첫 번째 파일은 정리된 게시물 제목을 사용하여 이름이 지정됩니다(예: '내 1장.jpg').
|
||||||
|
- 게시물에 여러 파일이 있는 경우 후속 파일도 게시물 제목을 사용하여 이름이 지정되지만 '_1', '_2' 등과 같은 숫자 접미사가 붙습니다(예: '내 1장_1.png', '내 1장_2.gif'). 접미사 카운터는 두 번째 파일부터 1로 시작합니다.
|
||||||
|
- 게시물에 파일이 하나만 있는 경우 접미사 없이 게시물 제목을 기준으로 이름이 지정됩니다.
|
||||||
|
|
||||||
|
예: 게시물 '1장'(파일 3개: originala.jpg, originalb.png, originalc.gif)
|
||||||
|
출력: '1장.jpg', '1장_1.png', '1장_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"파일은 원래 파일 이름을 유지하려고 시도합니다.\n\n- 이 스타일 버튼 옆에 나타나는 입력 필드에 선택적 접두사를 입력할 수 있습니다.\n\n예(접두사 '내 시리즈'): '내 시리즈_원본 파일.jpg'.\n예(접두사 없음): '원본 파일.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"파일은 게시물 게시 순서에 따라 순차적으로 이름이 지정됩니다(예: 001.ext, 002.ext).\n\n- 이 스타일 버튼 옆에 나타나는 입력 필드에 선택적 접두사를 입력할 수 있습니다.\n- 이 스타일의 경우 올바른 번호 매기기를 보장하기 위해 게시물 처리 멀티스레딩이 자동으로 비활성화됩니다.\n\n예(접두사 '내 만화'): '내 만화_001.jpg', '내 만화_002.png'.\n예(접두사 없음): '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"파일은 게시물 제목과 모든 게시물에 걸친 전역 순차 번호로 이름이 지정됩니다.\n\n- 형식: '[정리된 게시물 제목]_[전역 카운터].[ext]'\n- 카운터(예: _001, _002)는 현재 세션에서 다운로드된 모든 파일에 대해 증가합니다.\n- 이 스타일의 경우 올바른 번호 매기기를 보장하기 위해 게시물 처리 멀티스레딩이 자동으로 비활성화됩니다.\n\n예: 게시물 '1장'(파일 2개) -> '1장_001.jpg', '1장_002.png'.\n다음 게시물 '2장'(파일 1개) -> '2장_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"만화 파일 이름 스타일이 현재 알 수 없습니다. 이는 예기치 않은 상황입니다. 유효한 스타일로 전환하십시오.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"클릭하여 다음 스타일로 전환합니다.",
|
||||||
"fav_artists_dialog_title":"즐겨찾는 아티스트",
|
"fav_artists_dialog_title":"즐겨찾는 아티스트",
|
||||||
"fav_artists_loading_status":"즐겨찾는 아티스트 로드 중...",
|
"fav_artists_loading_status":"즐겨찾는 아티스트 로드 중...",
|
||||||
"fav_artists_search_placeholder":"아티스트 검색...",
|
"fav_artists_search_placeholder":"아티스트 검색...",
|
||||||
@@ -2761,6 +2970,8 @@ translations ["ko"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"Kemono.su에서 즐겨찾기 가져오는 중...",
|
"key_fetching_from_source_kemono_su":"Kemono.su에서 즐겨찾기 가져오는 중...",
|
||||||
"key_fetching_from_source_coomer_su":"Coomer.su에서 즐겨찾기 가져오는 중...",
|
"key_fetching_from_source_coomer_su":"Coomer.su에서 즐겨찾기 가져오는 중...",
|
||||||
"fav_posts_fetch_cancelled_status":"즐겨찾는 게시물 가져오기가 취소되었습니다.",
|
"fav_posts_fetch_cancelled_status":"즐겨찾는 게시물 가져오기가 취소되었습니다.",
|
||||||
|
"items_in_queue_placeholder": "팝업에서 대기열에 {count}개의 항목이 있습니다.",
|
||||||
|
"post_fetch_finished_status": "선택한 작성자에 대한 게시물 가져오기가 완료되었습니다.",
|
||||||
"known_names_filter_dialog_title":"필터에 알려진 이름 추가",
|
"known_names_filter_dialog_title":"필터에 알려진 이름 추가",
|
||||||
"known_names_filter_search_placeholder":"이름 검색...",
|
"known_names_filter_search_placeholder":"이름 검색...",
|
||||||
"known_names_filter_select_all_button":"모두 선택",
|
"known_names_filter_select_all_button":"모두 선택",
|
||||||
@@ -3033,6 +3244,18 @@ translations ["es"].update ({
|
|||||||
"creator_popup_add_selected_button":"Añadir seleccionados",
|
"creator_popup_add_selected_button":"Añadir seleccionados",
|
||||||
"creator_popup_scope_characters_button":"Ámbito: Personajes",
|
"creator_popup_scope_characters_button":"Ámbito: Personajes",
|
||||||
"creator_popup_scope_creators_button":"Ámbito: Creadores",
|
"creator_popup_scope_creators_button":"Ámbito: Creadores",
|
||||||
|
"creator_popup_title_fetching": "Publicaciones del creador",
|
||||||
|
"creator_popup_posts_area_title": "Publicaciones obtenidas",
|
||||||
|
"creator_popup_posts_search_placeholder": "Buscar publicaciones obtenidas por título...",
|
||||||
|
"no_posts_fetched_yet_status": "Aún no se han obtenido publicaciones.",
|
||||||
|
"fetched_posts_count_label": "{count} publicación(es) obtenida(s). Seleccione para añadir a la cola.",
|
||||||
|
"no_posts_found_for_selection": "No se encontraron publicaciones para el/los creador(es) seleccionado(s).",
|
||||||
|
"fetched_posts_count_label_filtered": "Mostrando {count} publicación(es) que coinciden con el filtro.",
|
||||||
|
"no_posts_match_search_filter": "Ninguna publicación coincide con su filtro de búsqueda.",
|
||||||
|
"fetch_error_for_creator_label": "Error al obtener para {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Obtención de publicaciones cancelada.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Error al obtener publicaciones o no se encontraron publicaciones.",
|
||||||
|
"select_posts_to_queue_message": "Por favor, seleccione al menos una publicación para añadir a la cola.",
|
||||||
"favorite_artists_button_text":"🖼️ Artistas favoritos",
|
"favorite_artists_button_text":"🖼️ Artistas favoritos",
|
||||||
"favorite_artists_button_tooltip":"Explore y descargue de sus artistas favoritos en Kemono.su/Coomer.su.",
|
"favorite_artists_button_tooltip":"Explore y descargue de sus artistas favoritos en Kemono.su/Coomer.su.",
|
||||||
"favorite_posts_button_text":"📄 Publicaciones favoritas",
|
"favorite_posts_button_text":"📄 Publicaciones favoritas",
|
||||||
@@ -3048,6 +3271,18 @@ translations ["es"].update ({
|
|||||||
"manga_style_date_based_text":"Nombre: Basado en la fecha",
|
"manga_style_date_based_text":"Nombre: Basado en la fecha",
|
||||||
"manga_style_title_global_num_text":"Nombre: Título+Núm.G.",
|
"manga_style_title_global_num_text":"Nombre: Título+Núm.G.",
|
||||||
"manga_style_unknown_text":"Nombre: Estilo desconocido",
|
"manga_style_unknown_text":"Nombre: Estilo desconocido",
|
||||||
|
"manga_style_post_title_tooltip":"""Los archivos se nombran según el título de la publicación.
|
||||||
|
- El primer archivo de una publicación se nombra usando el título limpio de la publicación (p. ej., 'Mi Capítulo 1.jpg').
|
||||||
|
- Si la publicación contiene varios archivos, los archivos posteriores también se nombran usando el título de la publicación, pero con un sufijo numérico como '_1', '_2', etc. (p. ej., 'Mi Capítulo 1_1.png', 'Mi Capítulo 1_2.gif'). El contador para el sufijo comienza en 1 para el segundo archivo.
|
||||||
|
- Si una publicación solo tiene un archivo, se nombra según el título de la publicación sin sufijo.
|
||||||
|
|
||||||
|
Ejemplo: Publicación 'Capítulo Uno' (3 archivos: originalA.jpg, originalB.png, originalC.gif)
|
||||||
|
Salida: 'Capítulo Uno.jpg', 'Capítulo Uno_1.png', 'Capítulo Uno_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"Los archivos intentan conservar sus nombres de archivo originales.\n\n- Se puede introducir un prefijo opcional en el campo de entrada que aparece junto a este botón de estilo.\n\nEjemplo (con prefijo 'MiSerie'): 'MiSerie_ArchivoOriginal.jpg'.\nEjemplo (sin prefijo): 'ArchivoOriginal.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"Los archivos se nombran secuencialmente (p. ej., 001.ext, 002.ext) según el orden de publicación de los posts.\n\n- Se puede introducir un prefijo opcional en el campo de entrada que aparece junto a este botón de estilo.\n- El multihilo para el procesamiento de publicaciones se desactiva automáticamente para este estilo para garantizar una numeración correcta.\n\nEjemplo (con prefijo 'MiComic'): 'MiComic_001.jpg', 'MiComic_002.png'.\nEjemplo (sin prefijo): '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"Los archivos se nombran con el título de la publicación y un número secuencial global para todas las publicaciones.\n\n- Formato: '[Título de la publicación limpio]_[Contador global].[ext]'\n- El contador (p. ej., _001, _002) se incrementa por cada archivo descargado en la sesión actual.\n- El multihilo para el procesamiento de publicaciones se desactiva automáticamente para este estilo para garantizar una numeración correcta.\n\nEjemplo: Publicación 'Capítulo 1' (2 archivos) -> 'Capítulo 1_001.jpg', 'Capítulo 1_002.png'.\nSiguiente publicación 'Capítulo 2' (1 archivo) -> 'Capítulo 2_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"El estilo de nombre de archivo de manga es actualmente desconocido. Esto es inesperado. Por favor, cambie a un estilo válido.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"Haga clic para cambiar al siguiente estilo.",
|
||||||
"fav_artists_dialog_title":"Artistas favoritos",
|
"fav_artists_dialog_title":"Artistas favoritos",
|
||||||
"fav_artists_loading_status":"Cargando artistas favoritos...",
|
"fav_artists_loading_status":"Cargando artistas favoritos...",
|
||||||
"fav_artists_search_placeholder":"Buscar artistas...",
|
"fav_artists_search_placeholder":"Buscar artistas...",
|
||||||
@@ -3085,6 +3320,8 @@ translations ["es"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"Obteniendo los favoritos de Kemono.su...",
|
"key_fetching_from_source_kemono_su":"Obteniendo los favoritos de Kemono.su...",
|
||||||
"key_fetching_from_source_coomer_su":"Obteniendo los favoritos de Coomer.su...",
|
"key_fetching_from_source_coomer_su":"Obteniendo los favoritos de Coomer.su...",
|
||||||
"fav_posts_fetch_cancelled_status":"Obtención de publicaciones favoritas cancelada.",
|
"fav_posts_fetch_cancelled_status":"Obtención de publicaciones favoritas cancelada.",
|
||||||
|
"items_in_queue_placeholder": "{count} elementos en cola desde la ventana emergente.",
|
||||||
|
"post_fetch_finished_status": "Obtención de publicaciones finalizada para los creadores seleccionados.",
|
||||||
"known_names_filter_dialog_title":"Añadir nombres conocidos al filtro",
|
"known_names_filter_dialog_title":"Añadir nombres conocidos al filtro",
|
||||||
"known_names_filter_search_placeholder":"Buscar nombres...",
|
"known_names_filter_search_placeholder":"Buscar nombres...",
|
||||||
"known_names_filter_select_all_button":"Seleccionar todo",
|
"known_names_filter_select_all_button":"Seleccionar todo",
|
||||||
@@ -3219,6 +3456,18 @@ translations ["es"].update ({
|
|||||||
"help_guide_github_tooltip":"Visitar la página de GitHub del proyecto (se abre en el navegador)",
|
"help_guide_github_tooltip":"Visitar la página de GitHub del proyecto (se abre en el navegador)",
|
||||||
"help_guide_instagram_tooltip":"Visitar nuestra página de Instagram (se abre en el navegador)",
|
"help_guide_instagram_tooltip":"Visitar nuestra página de Instagram (se abre en el navegador)",
|
||||||
"help_guide_discord_tooltip":"Visitar nuestra comunidad de Discord (se abre en el navegador)",
|
"help_guide_discord_tooltip":"Visitar nuestra comunidad de Discord (se abre en el navegador)",
|
||||||
|
"creator_popup_posts_search_placeholder": "Buscar publicaciones obtenidas por título...",
|
||||||
|
"no_posts_fetched_yet_status": "Aún no se han obtenido publicaciones.",
|
||||||
|
"fetched_posts_count_label": "{count} publicación(es) obtenida(s). Seleccione para añadir a la cola.",
|
||||||
|
"no_posts_found_for_selection": "No se encontraron publicaciones para el/los creador(es) seleccionado(s).",
|
||||||
|
"fetched_posts_count_label_filtered": "Mostrando {count} publicación(es) que coinciden con el filtro.",
|
||||||
|
"no_posts_match_search_filter": "Ninguna publicación coincide con su filtro de búsqueda.",
|
||||||
|
"fetch_error_for_creator_label": "Error al obtener para {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Obtención de publicaciones cancelada.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Error al obtener publicaciones o no se encontraron publicaciones.",
|
||||||
|
"select_posts_to_queue_message": "Por favor, seleccione al menos una publicación para añadir a la cola.",
|
||||||
|
"items_in_queue_placeholder": "{count} elementos en cola desde la ventana emergente.",
|
||||||
|
"post_fetch_finished_status": "Obtención de publicaciones finalizada para los creadores seleccionados.",
|
||||||
"help_guide_step1_title":"① Introducción y entradas principales",
|
"help_guide_step1_title":"① Introducción y entradas principales",
|
||||||
"help_guide_step1_content":"<html><head/><body>\n<p>Esta guía ofrece una descripción general de las funciones, campos y botones de Kemono Downloader.</p>\n<h3>Área de entrada principal (arriba a la izquierda)</h3>\n<ul>\n<li><b>🔗 URL del creador/publicación de Kemono:</b>\n<ul>\n<li>Introduzca la dirección web completa de la página de un creador (p. ej., <i>https://kemono.su/patreon/user/12345</i>) o de una publicación específica (p. ej., <i>.../post/98765</i>).</li>\n<li>Admite URL de Kemono (kemono.su, kemono.party) y Coomer (coomer.su, coomer.party).</li>\n</ul>\n</li>\n<li><b>Rango de páginas (de inicio a fin):</b>\n<ul>\n<li>Para URL de creadores: Especifique un rango de páginas para obtener (p. ej., de la 2 a la 5). Déjelo en blanco para todas las páginas.</li>\n<li>Desactivado para URL de publicaciones únicas o cuando el <b>Modo Manga/Cómic</b> está activo.</li>\n</ul>\n</li>\n<li><b>📁 Ubicación de descarga:</b>\n<ul>\n<li>Haga clic en <b>'Explorar...'</b> para elegir una carpeta principal en su ordenador donde se guardarán todos los archivos descargados.</li>\n<li>Este campo es obligatorio a menos que esté usando el modo <b>'🔗 Solo enlaces'</b>.</li>\n</ul>\n</li>\n<li><b>🎨 Botón de selección de creador (junto a la entrada de URL):</b>\n<ul>\n<li>Haga clic en el icono de la paleta (🎨) para abrir el cuadro de diálogo 'Selección de creador'.</li>\n<li>Este cuadro de diálogo carga creadores desde su archivo <code>creators.json</code> (que debe estar en el directorio de la aplicación).</li>\n<li><b>Dentro del cuadro de diálogo:</b>\n<ul>\n<li><b>Barra de búsqueda:</b> Escriba para filtrar la lista de creadores por nombre o servicio.</li>\n<li><b>Lista de creadores:</b> Muestra los creadores de su <code>creators.json</code>. Los creadores que ha añadido a 'favoritos' (en los datos JSON) aparecen en la parte superior.</li>\n<li><b>Casillas de verificación:</b> Seleccione uno o más creadores marcando la casilla junto a su nombre.</li>\n<li><b>Botón 'Ámbito' (p. ej., 'Ámbito: Personajes'):</b> Este botón alterna la organización de la descarga al iniciar descargas desde esta ventana emergente:\n<ul><li><i>Ámbito: Personajes:</i> Las descargas se organizarán en carpetas con nombres de personajes directamente dentro de su 'Ubicación de descarga' principal. Las obras de diferentes creadores para el mismo personaje se agruparán.</li>\n<li><i>Ámbito: Creadores:</i> Las descargas crearán primero una carpeta con el nombre del creador dentro de su 'Ubicación de descarga' principal. Luego, se crearán subcarpetas con nombres de personajes dentro de la carpeta de cada creador.</li></ul>\n</li>\n<li><b>Botón 'Añadir seleccionados':</b> Al hacer clic aquí, se tomarán los nombres de todos los creadores marcados y se añadirán al campo de entrada principal '🔗 URL del creador/publicación de Kemono', separados por comas. El cuadro de diálogo se cerrará.</li>\n</ul>\n</li>\n<li>Esta función proporciona una forma rápida de rellenar el campo de URL para múltiples creadores sin tener que escribir o pegar manualmente cada URL.</li>\n</ul>\n</li>\n</ul></body></html>",
|
"help_guide_step1_content":"<html><head/><body>\n<p>Esta guía ofrece una descripción general de las funciones, campos y botones de Kemono Downloader.</p>\n<h3>Área de entrada principal (arriba a la izquierda)</h3>\n<ul>\n<li><b>🔗 URL del creador/publicación de Kemono:</b>\n<ul>\n<li>Introduzca la dirección web completa de la página de un creador (p. ej., <i>https://kemono.su/patreon/user/12345</i>) o de una publicación específica (p. ej., <i>.../post/98765</i>).</li>\n<li>Admite URL de Kemono (kemono.su, kemono.party) y Coomer (coomer.su, coomer.party).</li>\n</ul>\n</li>\n<li><b>Rango de páginas (de inicio a fin):</b>\n<ul>\n<li>Para URL de creadores: Especifique un rango de páginas para obtener (p. ej., de la 2 a la 5). Déjelo en blanco para todas las páginas.</li>\n<li>Desactivado para URL de publicaciones únicas o cuando el <b>Modo Manga/Cómic</b> está activo.</li>\n</ul>\n</li>\n<li><b>📁 Ubicación de descarga:</b>\n<ul>\n<li>Haga clic en <b>'Explorar...'</b> para elegir una carpeta principal en su ordenador donde se guardarán todos los archivos descargados.</li>\n<li>Este campo es obligatorio a menos que esté usando el modo <b>'🔗 Solo enlaces'</b>.</li>\n</ul>\n</li>\n<li><b>🎨 Botón de selección de creador (junto a la entrada de URL):</b>\n<ul>\n<li>Haga clic en el icono de la paleta (🎨) para abrir el cuadro de diálogo 'Selección de creador'.</li>\n<li>Este cuadro de diálogo carga creadores desde su archivo <code>creators.json</code> (que debe estar en el directorio de la aplicación).</li>\n<li><b>Dentro del cuadro de diálogo:</b>\n<ul>\n<li><b>Barra de búsqueda:</b> Escriba para filtrar la lista de creadores por nombre o servicio.</li>\n<li><b>Lista de creadores:</b> Muestra los creadores de su <code>creators.json</code>. Los creadores que ha añadido a 'favoritos' (en los datos JSON) aparecen en la parte superior.</li>\n<li><b>Casillas de verificación:</b> Seleccione uno o más creadores marcando la casilla junto a su nombre.</li>\n<li><b>Botón 'Ámbito' (p. ej., 'Ámbito: Personajes'):</b> Este botón alterna la organización de la descarga al iniciar descargas desde esta ventana emergente:\n<ul><li><i>Ámbito: Personajes:</i> Las descargas se organizarán en carpetas con nombres de personajes directamente dentro de su 'Ubicación de descarga' principal. Las obras de diferentes creadores para el mismo personaje se agruparán.</li>\n<li><i>Ámbito: Creadores:</i> Las descargas crearán primero una carpeta con el nombre del creador dentro de su 'Ubicación de descarga' principal. Luego, se crearán subcarpetas con nombres de personajes dentro de la carpeta de cada creador.</li></ul>\n</li>\n<li><b>Botón 'Añadir seleccionados':</b> Al hacer clic aquí, se tomarán los nombres de todos los creadores marcados y se añadirán al campo de entrada principal '🔗 URL del creador/publicación de Kemono', separados por comas. El cuadro de diálogo se cerrará.</li>\n</ul>\n</li>\n<li>Esta función proporciona una forma rápida de rellenar el campo de URL para múltiples creadores sin tener que escribir o pegar manualmente cada URL.</li>\n</ul>\n</li>\n</ul></body></html>",
|
||||||
"help_guide_step2_title":"② Filtrado de descargas",
|
"help_guide_step2_title":"② Filtrado de descargas",
|
||||||
@@ -3357,6 +3606,18 @@ translations ["de"].update ({
|
|||||||
"creator_popup_add_selected_button":"Ausgewählte hinzufügen",
|
"creator_popup_add_selected_button":"Ausgewählte hinzufügen",
|
||||||
"creator_popup_scope_characters_button":"Bereich: Charaktere",
|
"creator_popup_scope_characters_button":"Bereich: Charaktere",
|
||||||
"creator_popup_scope_creators_button":"Bereich: Ersteller",
|
"creator_popup_scope_creators_button":"Bereich: Ersteller",
|
||||||
|
"creator_popup_title_fetching": "Beiträge des Erstellers",
|
||||||
|
"creator_popup_posts_area_title": "Abgerufene Beiträge",
|
||||||
|
"creator_popup_posts_search_placeholder": "Abgerufene Beiträge nach Titel suchen...",
|
||||||
|
"no_posts_fetched_yet_status": "Noch keine Beiträge abgerufen.",
|
||||||
|
"fetched_posts_count_label": "{count} Beitrag/Beiträge abgerufen. Zum Hinzufügen zur Warteschlange auswählen.",
|
||||||
|
"no_posts_found_for_selection": "Keine Beiträge für ausgewählte(n) Ersteller gefunden.",
|
||||||
|
"fetched_posts_count_label_filtered": "Zeige {count} Beitrag/Beiträge an, die dem Filter entsprechen.",
|
||||||
|
"no_posts_match_search_filter": "Keine Beiträge entsprechen Ihrem Suchfilter.",
|
||||||
|
"fetch_error_for_creator_label": "Fehler beim Abrufen für {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Abruf von Beiträgen abgebrochen.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Fehler beim Abrufen von Beiträgen oder keine Beiträge gefunden.",
|
||||||
|
"select_posts_to_queue_message": "Bitte wählen Sie mindestens einen Beitrag zum Hinzufügen zur Warteschlange aus.",
|
||||||
"favorite_artists_button_text":"🖼️ Lieblingskünstler",
|
"favorite_artists_button_text":"🖼️ Lieblingskünstler",
|
||||||
"favorite_artists_button_tooltip":"Durchsuchen und herunterladen von Ihren Lieblingskünstlern auf Kemono.su/Coomer.su.",
|
"favorite_artists_button_tooltip":"Durchsuchen und herunterladen von Ihren Lieblingskünstlern auf Kemono.su/Coomer.su.",
|
||||||
"favorite_posts_button_text":"📄 Lieblingsbeiträge",
|
"favorite_posts_button_text":"📄 Lieblingsbeiträge",
|
||||||
@@ -3372,6 +3633,18 @@ translations ["de"].update ({
|
|||||||
"manga_style_date_based_text":"Name: Datumsbasiert",
|
"manga_style_date_based_text":"Name: Datumsbasiert",
|
||||||
"manga_style_title_global_num_text":"Name: Titel+G.Nr.",
|
"manga_style_title_global_num_text":"Name: Titel+G.Nr.",
|
||||||
"manga_style_unknown_text":"Name: Unbekannter Stil",
|
"manga_style_unknown_text":"Name: Unbekannter Stil",
|
||||||
|
"manga_style_post_title_tooltip":"""Dateien werden basierend auf dem Beitragstitel benannt.
|
||||||
|
- Die erste Datei in einem Beitrag wird unter Verwendung des bereinigten Beitragstitels benannt (z. B. 'Mein Kapitel 1.jpg').
|
||||||
|
- Wenn der Beitrag mehrere Dateien enthält, werden nachfolgende Dateien ebenfalls unter Verwendung des Beitragstitels benannt, jedoch mit einem numerischen Suffix wie '_1', '_2' usw. (z. B. 'Mein Kapitel 1_1.png', 'Mein Kapitel 1_2.gif'). Der Zähler für das Suffix beginnt bei 1 für die zweite Datei.
|
||||||
|
- Wenn ein Beitrag nur eine Datei hat, wird sie nach dem Beitragstitel ohne Suffix benannt.
|
||||||
|
|
||||||
|
Beispiel: Beitrag 'Kapitel Eins' (3 Dateien: originalA.jpg, originalB.png, originalC.gif)
|
||||||
|
Ausgabe: 'Kapitel Eins.jpg', 'Kapitel Eins_1.png', 'Kapitel Eins_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"Dateien versuchen, ihre ursprünglichen Dateinamen beizubehalten.\n\n- Ein optionales Präfix kann in das Eingabefeld eingegeben werden, das neben dieser Stil-Schaltfläche erscheint.\n\nBeispiel (mit Präfix 'MeineSerie'): 'MeineSerie_Originaldatei.jpg'.\nBeispiel (ohne Präfix): 'Originaldatei.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"Dateien werden sequentiell (z. B. 001.ext, 002.ext) basierend auf der Veröffentlichungsreihenfolge der Beiträge benannt.\n\n- Ein optionales Präfix kann in das Eingabefeld eingegeben werden, das neben dieser Stil-Schaltfläche erscheint.\n- Multithreading für die Beitragsverarbeitung wird für diesen Stil automatisch deaktiviert, um eine korrekte Nummerierung zu gewährleisten.\n\nBeispiel (mit Präfix 'MeinComic'): 'MeinComic_001.jpg', 'MeinComic_002.png'.\nBeispiel (ohne Präfix): '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"Dateien werden mit dem Beitragstitel und einer globalen fortlaufenden Nummer über alle Beiträge hinweg benannt.\n\n- Format: '[Bereinigter Beitragstitel]_[Globaler Zähler].[ext]'\n- Der Zähler (z. B. _001, _002) erhöht sich für jede in der aktuellen Sitzung heruntergeladene Datei.\n- Multithreading für die Beitragsverarbeitung wird für diesen Stil automatisch deaktiviert, um eine korrekte Nummerierung zu gewährleisten.\n\nBeispiel: Beitrag 'Kapitel 1' (2 Dateien) -> 'Kapitel 1_001.jpg', 'Kapitel 1_002.png'.\nNächster Beitrag 'Kapitel 2' (1 Datei) -> 'Kapitel 2_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"Der Manga-Dateinamenstil ist derzeit unbekannt. Dies ist unerwartet. Bitte wechseln Sie zu einem gültigen Stil.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"Klicken, um zum nächsten Stil zu wechseln.",
|
||||||
"fav_artists_dialog_title":"Lieblingskünstler",
|
"fav_artists_dialog_title":"Lieblingskünstler",
|
||||||
"fav_artists_loading_status":"Lade Lieblingskünstler...",
|
"fav_artists_loading_status":"Lade Lieblingskünstler...",
|
||||||
"fav_artists_search_placeholder":"Künstler suchen...",
|
"fav_artists_search_placeholder":"Künstler suchen...",
|
||||||
@@ -3409,6 +3682,8 @@ translations ["de"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"Rufe Favoriten von Kemono.su ab...",
|
"key_fetching_from_source_kemono_su":"Rufe Favoriten von Kemono.su ab...",
|
||||||
"key_fetching_from_source_coomer_su":"Rufe Favoriten von Coomer.su ab...",
|
"key_fetching_from_source_coomer_su":"Rufe Favoriten von Coomer.su ab...",
|
||||||
"fav_posts_fetch_cancelled_status":"Abruf von Lieblingsbeiträgen abgebrochen.",
|
"fav_posts_fetch_cancelled_status":"Abruf von Lieblingsbeiträgen abgebrochen.",
|
||||||
|
"items_in_queue_placeholder": "{count} Elemente in der Warteschlange aus dem Popup.",
|
||||||
|
"post_fetch_finished_status": "Abruf von Beiträgen für ausgewählte Ersteller abgeschlossen.",
|
||||||
"known_names_filter_dialog_title":"Bekannte Namen zum Filter hinzufügen",
|
"known_names_filter_dialog_title":"Bekannte Namen zum Filter hinzufügen",
|
||||||
"known_names_filter_search_placeholder":"Namen suchen...",
|
"known_names_filter_search_placeholder":"Namen suchen...",
|
||||||
"known_names_filter_select_all_button":"Alle auswählen",
|
"known_names_filter_select_all_button":"Alle auswählen",
|
||||||
@@ -3543,6 +3818,18 @@ translations ["de"].update ({
|
|||||||
"help_guide_github_tooltip":"Besuchen Sie die GitHub-Seite des Projekts (öffnet sich im Browser)",
|
"help_guide_github_tooltip":"Besuchen Sie die GitHub-Seite des Projekts (öffnet sich im Browser)",
|
||||||
"help_guide_instagram_tooltip":"Besuchen Sie unsere Instagram-Seite (öffnet sich im Browser)",
|
"help_guide_instagram_tooltip":"Besuchen Sie unsere Instagram-Seite (öffnet sich im Browser)",
|
||||||
"help_guide_discord_tooltip":"Besuchen Sie unsere Discord-Community (öffnet sich im Browser)",
|
"help_guide_discord_tooltip":"Besuchen Sie unsere Discord-Community (öffnet sich im Browser)",
|
||||||
|
"creator_popup_posts_search_placeholder": "Abgerufene Beiträge nach Titel suchen...",
|
||||||
|
"no_posts_fetched_yet_status": "Noch keine Beiträge abgerufen.",
|
||||||
|
"fetched_posts_count_label": "{count} Beitrag/Beiträge abgerufen. Zum Hinzufügen zur Warteschlange auswählen.",
|
||||||
|
"no_posts_found_for_selection": "Keine Beiträge für ausgewählte(n) Ersteller gefunden.",
|
||||||
|
"fetched_posts_count_label_filtered": "Zeige {count} Beitrag/Beiträge an, die dem Filter entsprechen.",
|
||||||
|
"no_posts_match_search_filter": "Keine Beiträge entsprechen Ihrem Suchfilter.",
|
||||||
|
"fetch_error_for_creator_label": "Fehler beim Abrufen für {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Abruf von Beiträgen abgebrochen.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Fehler beim Abrufen von Beiträgen oder keine Beiträge gefunden.",
|
||||||
|
"select_posts_to_queue_message": "Bitte wählen Sie mindestens einen Beitrag zum Hinzufügen zur Warteschlange aus.",
|
||||||
|
"items_in_queue_placeholder": "{count} Elemente in der Warteschlange aus dem Popup.",
|
||||||
|
"post_fetch_finished_status": "Abruf von Beiträgen für ausgewählte Ersteller abgeschlossen.",
|
||||||
"help_guide_step1_title":"① Einführung & Haupteingaben",
|
"help_guide_step1_title":"① Einführung & Haupteingaben",
|
||||||
"help_guide_step1_content":"<html><head/><body>\n<p>Dieses Handbuch bietet einen Überblick über die Funktionen, Felder und Schaltflächen des Kemono Downloaders.</p>\n<h3>Haupteingabebereich (oben links)</h3>\n<ul>\n<li><b>🔗 Kemono Ersteller/Beitrags-URL:</b>\n<ul>\n<li>Geben Sie die vollständige Webadresse einer Erstellerseite (z. B. <i>https://kemono.su/patreon/user/12345</i>) oder eines bestimmten Beitrags (z. B. <i>.../post/98765</i>) ein.</li>\n<li>Unterstützt Kemono- (kemono.su, kemono.party) und Coomer-URLs (coomer.su, coomer.party).</li>\n</ul>\n</li>\n<li><b>Seitenbereich (Start bis Ende):</b>\n<ul>\n<li>Für Ersteller-URLs: Geben Sie einen Seitenbereich zum Abrufen an (z. B. Seiten 2 bis 5). Lassen Sie das Feld für alle Seiten leer.</li>\n<li>Deaktiviert für einzelne Beitrags-URLs oder wenn der <b>Manga/Comic-Modus</b> aktiv ist.</li>\n</ul>\n</li>\n<li><b>📁 Download-Speicherort:</b>\n<ul>\n<li>Klicken Sie auf <b>'Durchsuchen...'</b>, um einen Hauptordner auf Ihrem Computer auszuwählen, in dem alle heruntergeladenen Dateien gespeichert werden.</li>\n<li>Dieses Feld ist erforderlich, es sei denn, Sie verwenden den Modus <b>'🔗 Nur Links'</b>.</li>\n</ul>\n</li>\n<li><b>🎨 Erstellerauswahl-Schaltfläche (neben der URL-Eingabe):</b>\n<ul>\n<li>Klicken Sie auf das Palettensymbol (🎨), um das Dialogfeld 'Erstellerauswahl' zu öffnen.</li>\n<li>Dieses Dialogfeld lädt Ersteller aus Ihrer <code>creators.json</code>-Datei (die sich im Anwendungsverzeichnis befinden sollte).</li>\n<li><b>Innerhalb des Dialogfelds:</b>\n<ul>\n<li><b>Suchleiste:</b> Geben Sie Text ein, um die Liste der Ersteller nach Name oder Dienst zu filtern.</li>\n<li><b>Erstellerliste:</b> Zeigt Ersteller aus Ihrer <code>creators.json</code> an. Ersteller, die Sie als 'Favoriten' markiert haben (in den JSON-Daten), werden oben angezeigt.</li>\n<li><b>Kontrollkästchen:</b> Wählen Sie einen oder mehrere Ersteller aus, indem Sie das Kästchen neben ihrem Namen aktivieren.</li>\n<li><b>Schaltfläche 'Bereich' (z. B. 'Bereich: Charaktere'):</b> Diese Schaltfläche schaltet die Download-Organisation um, wenn Downloads aus diesem Popup gestartet werden:\n<ul><li><i>Bereich: Charaktere:</i> Downloads werden direkt in Ihrem Haupt-'Download-Speicherort' in nach Charakteren benannte Ordner organisiert. Arbeiten verschiedener Ersteller für denselben Charakter werden zusammengefasst.</li>\n<li><i>Bereich: Ersteller:</i> Downloads erstellen zuerst einen nach dem Ersteller benannten Ordner in Ihrem Haupt-'Download-Speicherort'. Dann werden in jedem Erstellerordner nach Charakteren benannte Unterordner erstellt.</li></ul>\n</li>\n<li><b>Schaltfläche 'Ausgewählte hinzufügen':</b> Wenn Sie hier klicken, werden die Namen aller aktivierten Ersteller übernommen und durch Kommas getrennt in das Haupteingabefeld '🔗 Kemono Ersteller/Beitrags-URL' eingefügt. Das Dialogfeld wird dann geschlossen.</li>\n</ul>\n</li>\n<li>Diese Funktion bietet eine schnelle Möglichkeit, das URL-Feld für mehrere Ersteller zu füllen, ohne jede URL manuell eingeben oder einfügen zu müssen.</li>\n</ul>\n</li>\n</ul></body></html>",
|
"help_guide_step1_content":"<html><head/><body>\n<p>Dieses Handbuch bietet einen Überblick über die Funktionen, Felder und Schaltflächen des Kemono Downloaders.</p>\n<h3>Haupteingabebereich (oben links)</h3>\n<ul>\n<li><b>🔗 Kemono Ersteller/Beitrags-URL:</b>\n<ul>\n<li>Geben Sie die vollständige Webadresse einer Erstellerseite (z. B. <i>https://kemono.su/patreon/user/12345</i>) oder eines bestimmten Beitrags (z. B. <i>.../post/98765</i>) ein.</li>\n<li>Unterstützt Kemono- (kemono.su, kemono.party) und Coomer-URLs (coomer.su, coomer.party).</li>\n</ul>\n</li>\n<li><b>Seitenbereich (Start bis Ende):</b>\n<ul>\n<li>Für Ersteller-URLs: Geben Sie einen Seitenbereich zum Abrufen an (z. B. Seiten 2 bis 5). Lassen Sie das Feld für alle Seiten leer.</li>\n<li>Deaktiviert für einzelne Beitrags-URLs oder wenn der <b>Manga/Comic-Modus</b> aktiv ist.</li>\n</ul>\n</li>\n<li><b>📁 Download-Speicherort:</b>\n<ul>\n<li>Klicken Sie auf <b>'Durchsuchen...'</b>, um einen Hauptordner auf Ihrem Computer auszuwählen, in dem alle heruntergeladenen Dateien gespeichert werden.</li>\n<li>Dieses Feld ist erforderlich, es sei denn, Sie verwenden den Modus <b>'🔗 Nur Links'</b>.</li>\n</ul>\n</li>\n<li><b>🎨 Erstellerauswahl-Schaltfläche (neben der URL-Eingabe):</b>\n<ul>\n<li>Klicken Sie auf das Palettensymbol (🎨), um das Dialogfeld 'Erstellerauswahl' zu öffnen.</li>\n<li>Dieses Dialogfeld lädt Ersteller aus Ihrer <code>creators.json</code>-Datei (die sich im Anwendungsverzeichnis befinden sollte).</li>\n<li><b>Innerhalb des Dialogfelds:</b>\n<ul>\n<li><b>Suchleiste:</b> Geben Sie Text ein, um die Liste der Ersteller nach Name oder Dienst zu filtern.</li>\n<li><b>Erstellerliste:</b> Zeigt Ersteller aus Ihrer <code>creators.json</code> an. Ersteller, die Sie als 'Favoriten' markiert haben (in den JSON-Daten), werden oben angezeigt.</li>\n<li><b>Kontrollkästchen:</b> Wählen Sie einen oder mehrere Ersteller aus, indem Sie das Kästchen neben ihrem Namen aktivieren.</li>\n<li><b>Schaltfläche 'Bereich' (z. B. 'Bereich: Charaktere'):</b> Diese Schaltfläche schaltet die Download-Organisation um, wenn Downloads aus diesem Popup gestartet werden:\n<ul><li><i>Bereich: Charaktere:</i> Downloads werden direkt in Ihrem Haupt-'Download-Speicherort' in nach Charakteren benannte Ordner organisiert. Arbeiten verschiedener Ersteller für denselben Charakter werden zusammengefasst.</li>\n<li><i>Bereich: Ersteller:</i> Downloads erstellen zuerst einen nach dem Ersteller benannten Ordner in Ihrem Haupt-'Download-Speicherort'. Dann werden in jedem Erstellerordner nach Charakteren benannte Unterordner erstellt.</li></ul>\n</li>\n<li><b>Schaltfläche 'Ausgewählte hinzufügen':</b> Wenn Sie hier klicken, werden die Namen aller aktivierten Ersteller übernommen und durch Kommas getrennt in das Haupteingabefeld '🔗 Kemono Ersteller/Beitrags-URL' eingefügt. Das Dialogfeld wird dann geschlossen.</li>\n</ul>\n</li>\n<li>Diese Funktion bietet eine schnelle Möglichkeit, das URL-Feld für mehrere Ersteller zu füllen, ohne jede URL manuell eingeben oder einfügen zu müssen.</li>\n</ul>\n</li>\n</ul></body></html>",
|
||||||
"help_guide_step2_title":"② Downloads filtern",
|
"help_guide_step2_title":"② Downloads filtern",
|
||||||
@@ -3681,6 +3968,18 @@ translations ["pt"].update ({
|
|||||||
"creator_popup_add_selected_button":"Adicionar Selecionados",
|
"creator_popup_add_selected_button":"Adicionar Selecionados",
|
||||||
"creator_popup_scope_characters_button":"Escopo: Personagens",
|
"creator_popup_scope_characters_button":"Escopo: Personagens",
|
||||||
"creator_popup_scope_creators_button":"Escopo: Criadores",
|
"creator_popup_scope_creators_button":"Escopo: Criadores",
|
||||||
|
"creator_popup_title_fetching": "Publicações do Criador",
|
||||||
|
"creator_popup_posts_area_title": "Publicações Buscadas",
|
||||||
|
"creator_popup_posts_search_placeholder": "Pesquisar publicações buscadas por título...",
|
||||||
|
"no_posts_fetched_yet_status": "Nenhuma publicação buscada ainda.",
|
||||||
|
"fetched_posts_count_label": "{count} publicação(ões) buscada(s). Selecione para adicionar à fila.",
|
||||||
|
"no_posts_found_for_selection": "Nenhuma publicação encontrada para o(s) criador(es) selecionado(s).",
|
||||||
|
"fetched_posts_count_label_filtered": "Exibindo {count} publicação(ões) que correspondem ao filtro.",
|
||||||
|
"no_posts_match_search_filter": "Nenhuma publicação corresponde ao seu filtro de pesquisa.",
|
||||||
|
"fetch_error_for_creator_label": "Erro ao buscar para {creator_name}",
|
||||||
|
"post_fetch_cancelled_status_done": "Busca de publicações cancelada.",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "Falha ao buscar publicações ou nenhuma publicação encontrada.",
|
||||||
|
"select_posts_to_queue_message": "Por favor, selecione pelo menos uma publicação para adicionar à fila.",
|
||||||
"favorite_artists_button_text":"🖼️ Artistas Favoritos",
|
"favorite_artists_button_text":"🖼️ Artistas Favoritos",
|
||||||
"favorite_artists_button_tooltip":"Navegue e baixe de seus artistas favoritos no Kemono.su/Coomer.su.",
|
"favorite_artists_button_tooltip":"Navegue e baixe de seus artistas favoritos no Kemono.su/Coomer.su.",
|
||||||
"favorite_posts_button_text":"📄 Publicações Favoritas",
|
"favorite_posts_button_text":"📄 Publicações Favoritas",
|
||||||
@@ -3696,6 +3995,18 @@ translations ["pt"].update ({
|
|||||||
"manga_style_date_based_text":"Nome: Baseado na Data",
|
"manga_style_date_based_text":"Nome: Baseado na Data",
|
||||||
"manga_style_title_global_num_text":"Nome: Título+Núm. Global",
|
"manga_style_title_global_num_text":"Nome: Título+Núm. Global",
|
||||||
"manga_style_unknown_text":"Nome: Estilo Desconhecido",
|
"manga_style_unknown_text":"Nome: Estilo Desconhecido",
|
||||||
|
"manga_style_post_title_tooltip":"""Os arquivos são nomeados com base no título da publicação.
|
||||||
|
- O primeiro arquivo em uma publicação é nomeado usando o título limpo da publicação (ex: 'Meu Capítulo 1.jpg').
|
||||||
|
- Se a publicação contiver vários arquivos, os arquivos subsequentes também são nomeados usando o título da publicação, mas com um sufixo numérico como '_1', '_2', etc. (ex: 'Meu Capítulo 1_1.png', 'Meu Capítulo 1_2.gif'). O contador para o sufixo começa em 1 para o segundo arquivo.
|
||||||
|
- Se uma publicação tiver apenas um arquivo, ele será nomeado com base no título da publicação sem sufixo.
|
||||||
|
|
||||||
|
Exemplo: Publicação 'Capítulo Um' (3 arquivos: originalA.jpg, originalB.png, originalC.gif)
|
||||||
|
Saída: 'Capítulo Um.jpg', 'Capítulo Um_1.png', 'Capítulo Um_2.gif'.""",
|
||||||
|
"manga_style_original_file_tooltip":"Os arquivos tentam manter seus nomes de arquivo originais.\n\n- Um prefixo opcional pode ser inserido no campo de entrada que aparece ao lado deste botão de estilo.\n\nExemplo (com prefixo 'MinhaSérie'): 'MinhaSérie_ArquivoOriginal.jpg'.\nExemplo (sem prefixo): 'ArquivoOriginal.jpg'.",
|
||||||
|
"manga_style_date_based_tooltip":"Os arquivos são nomeados sequencialmente (ex: 001.ext, 002.ext) com base na ordem de publicação dos posts.\n\n- Um prefixo opcional pode ser inserido no campo de entrada que aparece ao lado deste botão de estilo.\n- O multithreading para processamento de publicações é desativado automaticamente para este estilo para garantir a numeração correta.\n\nExemplo (com prefixo 'MeuQuadrinho'): 'MeuQuadrinho_001.jpg', 'MeuQuadrinho_002.png'.\nExemplo (sem prefixo): '001.jpg', '002.png'.",
|
||||||
|
"manga_style_title_global_num_tooltip":"Os arquivos são nomeados com o título da publicação e um número sequencial global para todas as publicações.\n\n- Formato: '[Título da Publicação Limpo]_[Contador Global].[ext]'\n- O contador (ex: _001, _002) incrementa para cada arquivo baixado na sessão atual.\n- O multithreading para processamento de publicações é desativado automaticamente para este estilo para garantir a numeração correta.\n\nExemplo: Publicação 'Capítulo 1' (2 arquivos) -> 'Capítulo 1_001.jpg', 'Capítulo 1_002.png'.\nPróxima publicação 'Capítulo 2' (1 arquivo) -> 'Capítulo 2_003.jpg'.",
|
||||||
|
"manga_style_unknown_tooltip":"O estilo de nome de arquivo de mangá é atualmente desconhecido. Isso é inesperado. Por favor, alterne para um estilo válido.",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"Clique para alternar para o próximo estilo.",
|
||||||
"fav_artists_dialog_title":"Artistas Favoritos",
|
"fav_artists_dialog_title":"Artistas Favoritos",
|
||||||
"fav_artists_loading_status":"Carregando artistas favoritos...",
|
"fav_artists_loading_status":"Carregando artistas favoritos...",
|
||||||
"fav_artists_search_placeholder":"Pesquisar artistas...",
|
"fav_artists_search_placeholder":"Pesquisar artistas...",
|
||||||
@@ -3733,6 +4044,8 @@ translations ["pt"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"Buscando favoritos do Kemono.su...",
|
"key_fetching_from_source_kemono_su":"Buscando favoritos do Kemono.su...",
|
||||||
"key_fetching_from_source_coomer_su":"Buscando favoritos do Coomer.su...",
|
"key_fetching_from_source_coomer_su":"Buscando favoritos do Coomer.su...",
|
||||||
"fav_posts_fetch_cancelled_status":"Busca de publicações favoritas cancelada.",
|
"fav_posts_fetch_cancelled_status":"Busca de publicações favoritas cancelada.",
|
||||||
|
"items_in_queue_placeholder": "{count} itens na fila do pop-up.",
|
||||||
|
"post_fetch_finished_status": "Busca de publicações concluída para os criadores selecionados.",
|
||||||
"known_names_filter_dialog_title":"Adicionar Nomes Conhecidos ao Filtro",
|
"known_names_filter_dialog_title":"Adicionar Nomes Conhecidos ao Filtro",
|
||||||
"known_names_filter_search_placeholder":"Pesquisar nomes...",
|
"known_names_filter_search_placeholder":"Pesquisar nomes...",
|
||||||
"known_names_filter_select_all_button":"Selecionar Todos",
|
"known_names_filter_select_all_button":"Selecionar Todos",
|
||||||
@@ -4005,6 +4318,18 @@ translations ["zh_TW"].update ({
|
|||||||
"creator_popup_add_selected_button":"新增所選",
|
"creator_popup_add_selected_button":"新增所選",
|
||||||
"creator_popup_scope_characters_button":"範圍:角色",
|
"creator_popup_scope_characters_button":"範圍:角色",
|
||||||
"creator_popup_scope_creators_button":"範圍:創作者",
|
"creator_popup_scope_creators_button":"範圍:創作者",
|
||||||
|
"creator_popup_title_fetching": "創作者貼文",
|
||||||
|
"creator_popup_posts_area_title": "已擷取的貼文",
|
||||||
|
"creator_popup_posts_search_placeholder": "按標題搜尋已擷取的貼文...",
|
||||||
|
"no_posts_fetched_yet_status": "尚未擷取任何貼文。",
|
||||||
|
"fetched_posts_count_label": "已擷取 {count} 篇貼文。選取以新增至佇列。",
|
||||||
|
"no_posts_found_for_selection": "找不到所選創作者的貼文。",
|
||||||
|
"fetched_posts_count_label_filtered": "正在顯示 {count} 篇符合篩選條件的貼文。",
|
||||||
|
"no_posts_match_search_filter": "沒有貼文符合您的搜尋篩選條件。",
|
||||||
|
"fetch_error_for_creator_label": "擷取 {creator_name} 時發生錯誤",
|
||||||
|
"post_fetch_cancelled_status_done": "貼文擷取已取消。",
|
||||||
|
"failed_to_fetch_or_no_posts_label": "擷取貼文失敗或找不到貼文。",
|
||||||
|
"select_posts_to_queue_message": "請至少選擇一篇貼文以新增至佇列。",
|
||||||
"favorite_artists_button_text":"🖼️ 最愛的藝術家",
|
"favorite_artists_button_text":"🖼️ 最愛的藝術家",
|
||||||
"favorite_artists_button_tooltip":"瀏覽並從您在 Kemono.su/Coomer.su 上最愛的藝術家下載。",
|
"favorite_artists_button_tooltip":"瀏覽並從您在 Kemono.su/Coomer.su 上最愛的藝術家下載。",
|
||||||
"favorite_posts_button_text":"📄 最愛的貼文",
|
"favorite_posts_button_text":"📄 最愛的貼文",
|
||||||
@@ -4020,6 +4345,18 @@ translations ["zh_TW"].update ({
|
|||||||
"manga_style_date_based_text":"命名:基於日期",
|
"manga_style_date_based_text":"命名:基於日期",
|
||||||
"manga_style_title_global_num_text":"命名:標題+全域編號",
|
"manga_style_title_global_num_text":"命名:標題+全域編號",
|
||||||
"manga_style_unknown_text":"命名:未知樣式",
|
"manga_style_unknown_text":"命名:未知樣式",
|
||||||
|
"manga_style_post_title_tooltip":"""檔案根據貼文標題命名。
|
||||||
|
- 貼文中的第一個檔案使用清理後的貼文標題命名(例如:「我的第一章.jpg」)。
|
||||||
|
- 如果貼文包含多個檔案,後續檔案也使用貼文標題命名,但會加上數字後綴,例如「_1」、「_2」等(例如:「我的第一章_1.png」、「我的第一章_2.gif」)。後綴的計數器從第二個檔案的 1 開始。
|
||||||
|
- 如果貼文只有一個檔案,則根據貼文標題命名,不加後綴。
|
||||||
|
|
||||||
|
範例:貼文「第一章」(3 個檔案:originala.jpg、originalb.png、originalc.gif)
|
||||||
|
輸出:「第一章.jpg」、「第一章_1.png」、「第一章_2.gif」。""",
|
||||||
|
"manga_style_original_file_tooltip":"檔案會嘗試保留其原始檔名。\n\n- 可在此樣式按鈕旁邊出現的輸入欄位中輸入可選的前綴。\n\n範例(前綴「我的系列」):「我的系列_原始檔案.jpg」。\n範例(無前綴):「原始檔案.jpg」。",
|
||||||
|
"manga_style_date_based_tooltip":"檔案根據貼文的發布順序按順序命名(例如:001.ext、002.ext)。\n\n- 可在此樣式按鈕旁邊出現的輸入欄位中輸入可選的前綴。\n- 此樣式會自動停用貼文處理的多執行緒,以確保正確的編號。\n\n範例(前綴「我的漫畫」):「我的漫畫_001.jpg」、「我的漫畫_002.png」。\n範例(無前綴):「001.jpg」、「002.png」。",
|
||||||
|
"manga_style_title_global_num_tooltip":"檔案使用貼文標題和所有貼文的全域序號命名。\n\n- 格式:「[清理後的貼文標題]_[全域計數器].[ext]」\n- 計數器(例如:_001、_002)會針對目前工作階段中下載的每個檔案遞增。\n- 此樣式會自動停用貼文處理的多執行緒,以確保正確的編號。\n\n範例:貼文「第一章」(2 個檔案)-> 「第一章_001.jpg」、「第一章_002.png」。\n下一個貼文「第二章」(1 個檔案)-> 「第二章_003.jpg」。",
|
||||||
|
"manga_style_unknown_tooltip":"漫畫檔名樣式目前未知。這是不預期的。請切換到有效的樣式。",
|
||||||
|
"manga_style_cycle_tooltip_suffix":"點擊以切換到下一個樣式。",
|
||||||
"fav_artists_dialog_title":"最愛的藝術家",
|
"fav_artists_dialog_title":"最愛的藝術家",
|
||||||
"fav_artists_loading_status":"正在載入最愛的藝術家...",
|
"fav_artists_loading_status":"正在載入最愛的藝術家...",
|
||||||
"fav_artists_search_placeholder":"搜尋藝術家...",
|
"fav_artists_search_placeholder":"搜尋藝術家...",
|
||||||
@@ -4057,6 +4394,10 @@ translations ["zh_TW"].update ({
|
|||||||
"key_fetching_from_source_kemono_su":"正在從 Kemono.su 擷取最愛...",
|
"key_fetching_from_source_kemono_su":"正在從 Kemono.su 擷取最愛...",
|
||||||
"key_fetching_from_source_coomer_su":"正在從 Coomer.su 擷取最愛...",
|
"key_fetching_from_source_coomer_su":"正在從 Coomer.su 擷取最愛...",
|
||||||
"fav_posts_fetch_cancelled_status":"最愛的貼文擷取已取消。",
|
"fav_posts_fetch_cancelled_status":"最愛的貼文擷取已取消。",
|
||||||
|
"items_in_queue_placeholder": "彈出視窗中有 {count} 個項目在佇列中。",
|
||||||
|
"post_fetch_finished_status": "已完成擷取所選創作者的貼文。",
|
||||||
|
"items_in_queue_placeholder": "彈出視窗中有 {count} 個項目在佇列中。",
|
||||||
|
"post_fetch_finished_status": "已完成擷取所選創作者的貼文。",
|
||||||
"known_names_filter_dialog_title":"將已知名稱新增至篩選器",
|
"known_names_filter_dialog_title":"將已知名稱新增至篩選器",
|
||||||
"known_names_filter_search_placeholder":"搜尋名稱...",
|
"known_names_filter_search_placeholder":"搜尋名稱...",
|
||||||
"known_names_filter_select_all_button":"全選",
|
"known_names_filter_select_all_button":"全選",
|
||||||
@@ -4208,7 +4549,12 @@ translations ["zh_TW"].update ({
|
|||||||
"help_guide_step8_title":"⑧ 最愛模式與未來功能",
|
"help_guide_step8_title":"⑧ 最愛模式與未來功能",
|
||||||
"help_guide_step8_content":"<html><head/><body>\n<h3>最愛模式(從您在 Kemono.su 上的最愛下載)</h3>\n<p>此模式可讓您直接從您在 Kemono.su 上收藏的藝術家下載內容。</p>\n<ul>\n<li><b>⭐ 如何啟用:</b>\n<ul>\n<li>勾選<b>「⭐ 最愛模式」</b>核取方塊,位於「🔗 僅限連結」選項按鈕旁邊。</li>\n</ul>\n</li>\n<li><b>最愛模式中的 UI 變更:</b>\n<ul>\n<li>「🔗 Kemono 創作者/貼文網址」輸入區域會被一條訊息取代,表示最愛模式已啟用。</li>\n<li>標準的「開始下載」、「暫停」、「取消」按鈕會被以下按鈕取代:\n<ul>\n<li><b>「🖼️ 最愛的藝術家」</b>按鈕</li>\n<li><b>「📄 最愛的貼文」</b>按鈕</li>\n</ul>\n</li>\n<li>「🍪 使用 Cookie」選項會自動啟用並鎖定,因為需要 cookie 來擷取您的最愛。</li>\n</ul>\n</li>\n<li><b>🖼️ 最愛的藝術家按鈕:</b>\n<ul>\n<li>點擊此處可開啟一個對話方塊,列出您在 Kemono.su 上收藏的所有藝術家。</li>\n<li>您可以從此列表中選擇一位或多位藝術家來下載他們的內容。</li>\n</ul>\n</li>\n<li><b>📄 最愛的貼文按鈕(未來功能):</b>\n<ul>\n<li>下載特定收藏的<i>貼文</i>(特別是如果它們是系列的一部分,則以類似漫畫的循序順序)是一項目前正在開發的功能。</li>\n<li>處理收藏貼文的最佳方式,特別是對於像漫畫這樣的循序閱讀,仍在探索中。</li>\n<li>如果您對於如何下載和組織收藏貼文有任何想法或特定用例(例如:從最愛中進行「漫畫風格」),請考慮在專案的 GitHub 頁面上提出問題或加入討論。您的意見非常寶貴!</li>\n</ul>\n</li>\n<li><b>最愛下載範圍(按鈕):</b>\n<ul>\n<li>此按鈕(位於「最愛的貼文」旁邊)控制所選最愛藝術家內容的下載位置:\n<ul>\n<li><b><i>範圍:所選位置:</i></b>所有選定的藝術家都將下載到您在 UI 中設定的主要「下載位置」。篩選器會全域應用於所有內容。</li>\n<li><b><i>範圍:藝術家資料夾:</i></b>對於每位選定的藝術家,將在您的主要「下載位置」內自動建立一個子資料夾(以藝術家姓名命名)。該藝術家的內容將進入其特定資料夾。篩選器會在每個藝術家的專用資料夾內應用。</li>\n</ul>\n</li>\n</ul>\n</li>\n<li><b>最愛模式中的篩選器:</b>\n<ul>\n<li>您在 UI 中設定的「🎯 按角色篩選」、「🚫 使用關鍵字跳過」和「篩選檔案」選項仍將適用於從您選定的最愛藝術家下載的內容。</li>\n</ul>\n</li>\n</ul></body></html>",
|
"help_guide_step8_content":"<html><head/><body>\n<h3>最愛模式(從您在 Kemono.su 上的最愛下載)</h3>\n<p>此模式可讓您直接從您在 Kemono.su 上收藏的藝術家下載內容。</p>\n<ul>\n<li><b>⭐ 如何啟用:</b>\n<ul>\n<li>勾選<b>「⭐ 最愛模式」</b>核取方塊,位於「🔗 僅限連結」選項按鈕旁邊。</li>\n</ul>\n</li>\n<li><b>最愛模式中的 UI 變更:</b>\n<ul>\n<li>「🔗 Kemono 創作者/貼文網址」輸入區域會被一條訊息取代,表示最愛模式已啟用。</li>\n<li>標準的「開始下載」、「暫停」、「取消」按鈕會被以下按鈕取代:\n<ul>\n<li><b>「🖼️ 最愛的藝術家」</b>按鈕</li>\n<li><b>「📄 最愛的貼文」</b>按鈕</li>\n</ul>\n</li>\n<li>「🍪 使用 Cookie」選項會自動啟用並鎖定,因為需要 cookie 來擷取您的最愛。</li>\n</ul>\n</li>\n<li><b>🖼️ 最愛的藝術家按鈕:</b>\n<ul>\n<li>點擊此處可開啟一個對話方塊,列出您在 Kemono.su 上收藏的所有藝術家。</li>\n<li>您可以從此列表中選擇一位或多位藝術家來下載他們的內容。</li>\n</ul>\n</li>\n<li><b>📄 最愛的貼文按鈕(未來功能):</b>\n<ul>\n<li>下載特定收藏的<i>貼文</i>(特別是如果它們是系列的一部分,則以類似漫畫的循序順序)是一項目前正在開發的功能。</li>\n<li>處理收藏貼文的最佳方式,特別是對於像漫畫這樣的循序閱讀,仍在探索中。</li>\n<li>如果您對於如何下載和組織收藏貼文有任何想法或特定用例(例如:從最愛中進行「漫畫風格」),請考慮在專案的 GitHub 頁面上提出問題或加入討論。您的意見非常寶貴!</li>\n</ul>\n</li>\n<li><b>最愛下載範圍(按鈕):</b>\n<ul>\n<li>此按鈕(位於「最愛的貼文」旁邊)控制所選最愛藝術家內容的下載位置:\n<ul>\n<li><b><i>範圍:所選位置:</i></b>所有選定的藝術家都將下載到您在 UI 中設定的主要「下載位置」。篩選器會全域應用於所有內容。</li>\n<li><b><i>範圍:藝術家資料夾:</i></b>對於每位選定的藝術家,將在您的主要「下載位置」內自動建立一個子資料夾(以藝術家姓名命名)。該藝術家的內容將進入其特定資料夾。篩選器會在每個藝術家的專用資料夾內應用。</li>\n</ul>\n</li>\n</ul>\n</li>\n<li><b>最愛模式中的篩選器:</b>\n<ul>\n<li>您在 UI 中設定的「🎯 按角色篩選」、「🚫 使用關鍵字跳過」和「篩選檔案」選項仍將適用於從您選定的最愛藝術家下載的內容。</li>\n</ul>\n</li>\n</ul></body></html>",
|
||||||
"help_guide_step9_title":"⑨ 關鍵檔案與導覽",
|
"help_guide_step9_title":"⑨ 關鍵檔案與導覽",
|
||||||
"help_guide_step9_content":"<html><head/><body>\n<h3>應用程式使用的關鍵檔案</h3>\n<ul>\n<li><b><code>Known.txt</code>:</b>\n<ul>\n<li>位於應用程式目錄中(<code>.exe</code> 或 <code>main.py</code> 所在的位置)。</li>\n<li>在啟用「按名稱/標題分開資料夾」時,儲存您已知的節目、角色或系列標題列表,以便自動組織資料夾。</li>\n<li><b>格式:</b>\n<ul>\n<li>每一行都是一個條目。</li>\n<li><b>單一名稱:</b>例如:<code>我的精彩系列</code>。符合此內容的內容將進入名為「我的精彩系列」的資料夾。</li>\n<li><b>群組別名:</b>例如:<code>(角色 A, 角 A, 備用名稱 A)</code>。符合「角色 A」、「角 A」或「備用名稱 A」的內容都將進入一個名為「角色 A 角 A 備用名稱 A」的資料夾(經過清理)。括號中的所有術語都成為該資料夾的別名。</li>\n</ul>\n</li>\n<li><b>用途:</b>如果貼文不符合您目前作用中的「按角色篩選」輸入,則作為資料夾命名的備用選項。您可以透過 UI 管理簡單條目,或直接編輯檔案以處理複雜的別名。應用程式會在啟動或下次使用時重新載入它。</li>\n</ul>\n</li>\n<li><b><code>cookies.txt</code>(可選):</b>\n<ul>\n<li>如果您使用「使用 Cookie」功能,且未提供直接的 cookie 字串或瀏覽特定檔案,應用程式將在其目錄中尋找名為 <code>cookies.txt</code> 的檔案。</li>\n<li><b>格式:</b>必須是 Netscape cookie 檔案格式。</li>\n<li><b>用途:</b>允許下載器使用您的瀏覽器登入工作階段來存取可能需要登入 Kemono/Coomer 的內容。</li>\n</ul>\n</li>\n</ul>\n<h3>首次使用者導覽</h3>\n<ul>\n<li>首次啟動時(或如果重設),會出現一個歡迎導覽對話方塊,引導您了解主要功能。您可以跳過它或選擇「不再顯示此導覽」。</li>\n</ul>\n<p><em>許多 UI 元素也有工具提示,當您將滑鼠懸停在它們上方時會出現,提供快速提示。</em></p>\n</body></html>"
|
"help_guide_step9_content":"<html><head/><body>\n<h3>應用程式使用的關鍵檔案</h3>\n<ul>\n<li><b><code>Known.txt</code>:</b>\n<ul>\n<li>位於應用程式目錄中(<code>.exe</code> 或 <code>main.py</code> 所在的位置)。</li>\n<li>在啟用「按名稱/標題分開資料夾」時,儲存您已知的節目、角色或系列標題列表,以便自動組織資料夾。</li>\n<li><b>格式:</b>\n<ul>\n<li>每一行都是一個條目。</li>\n<li><b>單一名稱:</b>例如:<code>我的精彩系列</code>。符合此內容的內容將進入名為「我的精彩系列」的資料夾。</li>\n<li><b>群組別名:</b>例如:<code>(角色 A, 角 A, 備用名稱 A)</code>。符合「角色 A」、「角 A」或「備用名稱 A」的內容都將進入一個名為「角色 A 角 A 備用名稱 A」的資料夾(經過清理)。括號中的所有術語都成為該資料夾的別名。</li>\n</ul>\n</li>\n<li><b>用途:</b>如果貼文不符合您目前作用中的「按角色篩選」輸入,則作為資料夾命名的備用選項。您可以透過 UI 管理簡單條目,或直接編輯檔案以處理複雜的別名。應用程式會在啟動或下次使用時重新載入它。</li>\n</ul>\n</li>\n<li><b><code>cookies.txt</code>(可選):</b>\n<ul>\n<li>如果您使用「使用 Cookie」功能,且未提供直接的 cookie 字串或瀏覽特定檔案,應用程式將在其目錄中尋找名為 <code>cookies.txt</code> 的檔案。</li>\n<li><b>格式:</b>必須是 Netscape cookie 檔案格式。</li>\n<li><b>用途:</b>允許下載器使用您的瀏覽器登入工作階段來存取可能需要登入 Kemono/Coomer 的內容。</li>\n</ul>\n</li>\n</ul>\n<h3>首次使用者導覽</h3>\n<ul>\n<li>首次啟動時(或如果重設),會出現一個歡迎導覽對話方塊,引導您了解主要功能。您可以跳過它或選擇「不再顯示此導覽」。</li>\n</ul>\n<p><em>許多 UI 元素也有工具提示,當您將滑鼠懸停在它們上方時會出現,提供快速提示。</em></p>\n</body></html>",
|
||||||
|
"fetch_posts_button_text":"載入貼文",
|
||||||
|
"creator_popup_add_posts_to_queue_button":"加入所選貼文至佇列",
|
||||||
|
"posts_for_creator_header":"來自的貼文",
|
||||||
|
"untitled_post_placeholder":"未命名貼文",
|
||||||
|
"fetching_posts_for_creator_status_all_pages":"正在從 {service} 載入 {creator_name} 的所有貼文⋯⋯可能需要一些時間。",
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_translation (language_code ,key ,default_text =""):
|
def get_translation (language_code ,key ,default_text =""):
|
||||||
|
|||||||
821
main.py
@@ -204,13 +204,16 @@ class DownloadExtractedLinksDialog (QDialog ):
|
|||||||
|
|
||||||
if parent :
|
if parent :
|
||||||
parent_width =parent .width ()
|
parent_width =parent .width ()
|
||||||
parent_height =parent .height ()
|
parent_height =parent .height()
|
||||||
dialog_width =int (parent_width *0.6 )
|
screen_height = QApplication.primaryScreen().availableGeometry().height() if QApplication.primaryScreen() else 768 # Default to 768 if screen info unavailable
|
||||||
dialog_height =int (parent_height *0.7 )
|
scale_factor = screen_height / 768.0 # Scale based on height relative to 768p
|
||||||
min_w ,min_h =500 ,400
|
|
||||||
self .resize (max (dialog_width ,min_w ),max (dialog_height ,min_h ))
|
base_min_w ,base_min_h =500 ,400
|
||||||
else :
|
scaled_min_w = int(base_min_w * scale_factor)
|
||||||
self .setMinimumSize (500 ,400 )
|
scaled_min_h = int(base_min_h * scale_factor)
|
||||||
|
|
||||||
|
self.setMinimumSize(scaled_min_w, scaled_min_h)
|
||||||
|
self.resize(max(int(parent_width * 0.6 * scale_factor), scaled_min_w), max(int(parent_height * 0.7 * scale_factor), scaled_min_h))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -332,9 +335,13 @@ 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()
|
screen_height = QApplication.primaryScreen().availableGeometry().height() if QApplication.primaryScreen() else 768
|
||||||
if not app_icon.isNull():
|
scale_factor = screen_height / 768.0
|
||||||
self.setWindowIcon(app_icon)
|
|
||||||
|
base_min_w ,base_min_h =480 ,350
|
||||||
|
scaled_min_w = int(base_min_w * scale_factor)
|
||||||
|
scaled_min_h = int(base_min_h * scale_factor)
|
||||||
|
self.setMinimumSize(scaled_min_w, scaled_min_h)
|
||||||
|
|
||||||
main_layout =QVBoxLayout (self )
|
main_layout =QVBoxLayout (self )
|
||||||
|
|
||||||
@@ -388,8 +395,6 @@ class ConfirmAddAllDialog (QDialog ):
|
|||||||
main_layout .addLayout (buttons_layout )
|
main_layout .addLayout (buttons_layout )
|
||||||
self ._retranslate_ui ()
|
self ._retranslate_ui ()
|
||||||
|
|
||||||
self .setMinimumWidth (480 )
|
|
||||||
self .setMinimumHeight (350 )
|
|
||||||
if self .parent_app and hasattr (self .parent_app ,'get_dark_theme')and self .parent_app .current_theme =="dark":
|
if self .parent_app and hasattr (self .parent_app ,'get_dark_theme')and self .parent_app .current_theme =="dark":
|
||||||
self .setStyleSheet (parent .get_dark_theme ())
|
self .setStyleSheet (parent .get_dark_theme ())
|
||||||
self .add_selected_button .setDefault (True )
|
self .add_selected_button .setDefault (True )
|
||||||
@@ -453,9 +458,12 @@ 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()
|
screen_height = QApplication.primaryScreen().availableGeometry().height() if QApplication.primaryScreen() else 768
|
||||||
if not app_icon.isNull():
|
scale_factor = screen_height / 768.0
|
||||||
self.setWindowIcon(app_icon)
|
|
||||||
|
base_min_w =350
|
||||||
|
scaled_min_w = int(base_min_w * scale_factor)
|
||||||
|
self.setMinimumWidth(scaled_min_w)
|
||||||
|
|
||||||
layout =QVBoxLayout (self )
|
layout =QVBoxLayout (self )
|
||||||
|
|
||||||
@@ -490,7 +498,6 @@ class ExportOptionsDialog (QDialog ):
|
|||||||
|
|
||||||
self ._retranslate_ui ()
|
self ._retranslate_ui ()
|
||||||
|
|
||||||
self .setMinimumWidth (350 )
|
|
||||||
|
|
||||||
if self .parent_app and hasattr (self .parent_app ,'current_theme')and self .parent_app .current_theme =="dark":
|
if self .parent_app and hasattr (self .parent_app ,'current_theme')and self .parent_app .current_theme =="dark":
|
||||||
if hasattr (self .parent_app ,'get_dark_theme'):
|
if hasattr (self .parent_app ,'get_dark_theme'):
|
||||||
@@ -528,9 +535,13 @@ 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()
|
screen_height = QApplication.primaryScreen().availableGeometry().height() if QApplication.primaryScreen() else 768
|
||||||
if not app_icon.isNull():
|
scale_factor = screen_height / 768.0
|
||||||
self.setWindowIcon(app_icon)
|
|
||||||
|
base_min_w ,base_min_h =500 ,300
|
||||||
|
scaled_min_w = int(base_min_w * scale_factor)
|
||||||
|
scaled_min_h = int(base_min_h * scale_factor)
|
||||||
|
self.setMinimumSize(scaled_min_w, scaled_min_h)
|
||||||
|
|
||||||
main_layout =QVBoxLayout (self )
|
main_layout =QVBoxLayout (self )
|
||||||
|
|
||||||
@@ -580,8 +591,6 @@ class ErrorFilesDialog (QDialog ):
|
|||||||
self .retry_button .setEnabled (bool (self .error_files ))
|
self .retry_button .setEnabled (bool (self .error_files ))
|
||||||
self .export_button .setEnabled (bool (self .error_files ))
|
self .export_button .setEnabled (bool (self .error_files ))
|
||||||
|
|
||||||
self .setMinimumWidth (500 )
|
|
||||||
self .setMinimumHeight (300 )
|
|
||||||
if self .parent_app and hasattr (self .parent_app ,'get_dark_theme')and self .parent_app .current_theme =="dark":
|
if self .parent_app and hasattr (self .parent_app ,'get_dark_theme')and self .parent_app .current_theme =="dark":
|
||||||
self .setStyleSheet (self .parent_app .get_dark_theme ())
|
self .setStyleSheet (self .parent_app .get_dark_theme ())
|
||||||
self .ok_button .setDefault (True )
|
self .ok_button .setDefault (True )
|
||||||
@@ -670,9 +679,13 @@ 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()
|
screen_height = QApplication.primaryScreen().availableGeometry().height() if QApplication.primaryScreen() else 768
|
||||||
if not app_icon.isNull():
|
scale_factor = screen_height / 768.0
|
||||||
self.setWindowIcon(app_icon)
|
|
||||||
|
base_min_w ,base_min_h =380 ,250
|
||||||
|
scaled_min_w = int(base_min_w * scale_factor)
|
||||||
|
scaled_min_h = int(base_min_h * scale_factor)
|
||||||
|
self.setMinimumSize(scaled_min_w, scaled_min_h)
|
||||||
|
|
||||||
layout =QVBoxLayout (self )
|
layout =QVBoxLayout (self )
|
||||||
|
|
||||||
@@ -707,7 +720,6 @@ class FutureSettingsDialog (QDialog ):
|
|||||||
self .ok_button .clicked .connect (self .accept )
|
self .ok_button .clicked .connect (self .accept )
|
||||||
layout .addWidget (self .ok_button ,0 ,Qt .AlignRight |Qt .AlignBottom )
|
layout .addWidget (self .ok_button ,0 ,Qt .AlignRight |Qt .AlignBottom )
|
||||||
|
|
||||||
self .setMinimumSize (380 ,250 )
|
|
||||||
self ._retranslate_ui ()
|
self ._retranslate_ui ()
|
||||||
self ._apply_dialog_theme ()
|
self ._apply_dialog_theme ()
|
||||||
def _tr (self ,key ,default_text =""):
|
def _tr (self ,key ,default_text =""):
|
||||||
@@ -801,35 +813,59 @@ class EmptyPopupDialog (QDialog ):
|
|||||||
INITIAL_LOAD_LIMIT =200
|
INITIAL_LOAD_LIMIT =200
|
||||||
SCOPE_CREATORS ="Creators"
|
SCOPE_CREATORS ="Creators"
|
||||||
|
|
||||||
|
|
||||||
def __init__ (self ,app_base_dir ,parent_app_ref ,parent =None ):
|
def __init__ (self ,app_base_dir ,parent_app_ref ,parent =None ):
|
||||||
super ().__init__ (parent )
|
super ().__init__ (parent )
|
||||||
self .setMinimumSize (400 ,300 )
|
self .setMinimumSize (400 ,300 )
|
||||||
|
screen_height = QApplication.primaryScreen().availableGeometry().height() if QApplication.primaryScreen() else 768
|
||||||
|
scale_factor = screen_height / 768.0
|
||||||
|
self.setMinimumSize(int(400 * scale_factor), int(300 * scale_factor))
|
||||||
|
|
||||||
self .parent_app =parent_app_ref
|
self .parent_app =parent_app_ref
|
||||||
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 =[]
|
|
||||||
|
|
||||||
app_icon = get_app_icon_object()
|
app_icon = get_app_icon_object()
|
||||||
if not app_icon.isNull():
|
if app_icon and not app_icon.isNull():
|
||||||
self.setWindowIcon(app_icon)
|
self.setWindowIcon(app_icon)
|
||||||
self .selected_creators_for_queue =[]
|
self .selected_creators_for_queue =[]
|
||||||
self .globally_selected_creators ={}
|
self .globally_selected_creators ={}
|
||||||
|
self.fetched_posts_data = {} # Stores posts by (service, user_id)
|
||||||
|
self.post_fetch_thread = None
|
||||||
|
self.globally_selected_post_ids = set() # To store (service, user_id, post_id) tuples
|
||||||
|
|
||||||
layout =QVBoxLayout (self )
|
# Main layout for the dialog will be a QHBoxLayout holding the splitter
|
||||||
|
dialog_layout = QHBoxLayout(self)
|
||||||
|
self.setLayout(dialog_layout)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Left Pane (Creator Selection) ---
|
||||||
|
self.left_pane_widget = QWidget()
|
||||||
|
left_pane_layout = QVBoxLayout(self.left_pane_widget)
|
||||||
|
|
||||||
|
# Create a horizontal layout for search input and fetch button
|
||||||
|
search_fetch_layout = QHBoxLayout()
|
||||||
self .search_input =QLineEdit ()
|
self .search_input =QLineEdit ()
|
||||||
self .search_input .textChanged .connect (self ._filter_list )
|
self .search_input .textChanged .connect (self ._filter_list )
|
||||||
layout .addWidget (self .search_input )
|
search_fetch_layout.addWidget(self.search_input, 1) # Give search input more stretch
|
||||||
|
self.fetch_posts_button = QPushButton() # Placeholder text, will be translated
|
||||||
|
self.fetch_posts_button.setEnabled(False) # Initially disabled
|
||||||
|
self.fetch_posts_button.clicked.connect(self._handle_fetch_posts_click)
|
||||||
|
search_fetch_layout.addWidget(self.fetch_posts_button)
|
||||||
|
left_pane_layout.addLayout(search_fetch_layout)
|
||||||
|
|
||||||
self .progress_bar =QProgressBar ()
|
self .progress_bar =QProgressBar ()
|
||||||
self .progress_bar .setRange (0 ,0 )
|
self .progress_bar .setRange (0 ,0 )
|
||||||
self .progress_bar .setTextVisible (False )
|
self .progress_bar .setTextVisible (False )
|
||||||
self .progress_bar .setVisible (False )
|
self .progress_bar .setVisible (False )
|
||||||
layout .addWidget (self .progress_bar )
|
left_pane_layout.addWidget (self .progress_bar )
|
||||||
|
|
||||||
self .list_widget =QListWidget ()
|
self .list_widget =QListWidget ()
|
||||||
self .list_widget .itemChanged .connect (self ._handle_item_check_changed )
|
self .list_widget .itemChanged .connect (self ._handle_item_check_changed )
|
||||||
layout .addWidget (self .list_widget )
|
left_pane_layout.addWidget (self .list_widget )
|
||||||
button_layout =QHBoxLayout ()
|
|
||||||
|
# Bottom buttons for left pane
|
||||||
|
left_bottom_buttons_layout =QHBoxLayout ()
|
||||||
self .add_selected_button =QPushButton ()
|
self .add_selected_button =QPushButton ()
|
||||||
self .add_selected_button .setToolTip (
|
self .add_selected_button .setToolTip (
|
||||||
"Add Selected Creators to URL Input\n\n"
|
"Add Selected Creators to URL Input\n\n"
|
||||||
@@ -838,21 +874,125 @@ class EmptyPopupDialog (QDialog ):
|
|||||||
)
|
)
|
||||||
self .add_selected_button .clicked .connect (self ._handle_add_selected )
|
self .add_selected_button .clicked .connect (self ._handle_add_selected )
|
||||||
self .add_selected_button .setDefault (True )
|
self .add_selected_button .setDefault (True )
|
||||||
button_layout .addWidget (self .add_selected_button )
|
left_bottom_buttons_layout.addWidget (self .add_selected_button )
|
||||||
|
|
||||||
self .scope_button =QPushButton ()
|
self .scope_button =QPushButton ()
|
||||||
self .scope_button .clicked .connect (self ._toggle_scope_mode )
|
self .scope_button .clicked .connect (self ._toggle_scope_mode )
|
||||||
button_layout .addWidget (self .scope_button )
|
left_bottom_buttons_layout.addWidget (self .scope_button )
|
||||||
layout .addLayout (button_layout )
|
left_pane_layout.addLayout(left_bottom_buttons_layout)
|
||||||
|
|
||||||
|
# --- Right Pane (Posts - initially hidden) ---
|
||||||
|
self.right_pane_widget = QWidget()
|
||||||
|
right_pane_layout = QVBoxLayout(self.right_pane_widget)
|
||||||
|
|
||||||
|
self.posts_area_title_label = QLabel("Fetched Posts")
|
||||||
|
self.posts_area_title_label.setAlignment(Qt.AlignCenter)
|
||||||
|
right_pane_layout.addWidget(self.posts_area_title_label)
|
||||||
|
|
||||||
|
self.posts_search_input = QLineEdit()
|
||||||
|
self.posts_search_input.setVisible(False) # Initially hidden until posts are fetched
|
||||||
|
# Placeholder text will be set in _retranslate_ui
|
||||||
|
self.posts_search_input.textChanged.connect(self._filter_fetched_posts_list)
|
||||||
|
right_pane_layout.addWidget(self.posts_search_input)
|
||||||
|
|
||||||
|
self.posts_list_widget = QListWidget()
|
||||||
|
right_pane_layout.addWidget(self.posts_list_widget)
|
||||||
|
|
||||||
|
posts_buttons_top_layout = QHBoxLayout()
|
||||||
|
self.posts_select_all_button = QPushButton() # Text set in _retranslate_ui
|
||||||
|
self.posts_select_all_button.clicked.connect(self._handle_posts_select_all)
|
||||||
|
posts_buttons_top_layout.addWidget(self.posts_select_all_button)
|
||||||
|
|
||||||
|
self.posts_deselect_all_button = QPushButton() # Text set in _retranslate_ui
|
||||||
|
self.posts_deselect_all_button.clicked.connect(self._handle_posts_deselect_all)
|
||||||
|
posts_buttons_top_layout.addWidget(self.posts_deselect_all_button)
|
||||||
|
right_pane_layout.addLayout(posts_buttons_top_layout)
|
||||||
|
|
||||||
|
posts_buttons_bottom_layout = QHBoxLayout()
|
||||||
|
self.posts_add_selected_button = QPushButton() # Text set in _retranslate_ui
|
||||||
|
self.posts_add_selected_button.clicked.connect(self._handle_posts_add_selected_to_queue)
|
||||||
|
posts_buttons_bottom_layout.addWidget(self.posts_add_selected_button)
|
||||||
|
|
||||||
|
self.posts_close_button = QPushButton() # Text set in _retranslate_ui
|
||||||
|
self.posts_close_button.clicked.connect(self._handle_posts_close_view)
|
||||||
|
posts_buttons_bottom_layout.addWidget(self.posts_close_button)
|
||||||
|
right_pane_layout.addLayout(posts_buttons_bottom_layout)
|
||||||
|
|
||||||
|
self.right_pane_widget.hide() # Initially hidden
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# --- Splitter ---
|
||||||
|
self.main_splitter = QSplitter(Qt.Horizontal)
|
||||||
|
self.main_splitter.addWidget(self.left_pane_widget)
|
||||||
|
self.main_splitter.addWidget(self.right_pane_widget)
|
||||||
|
self.main_splitter.setCollapsible(0, False) # Prevent left pane from collapsing
|
||||||
|
self.main_splitter.setCollapsible(1, True)
|
||||||
|
dialog_layout.addWidget(self.main_splitter)
|
||||||
|
|
||||||
|
self.original_size = self.sizeHint() # Store initial size hint
|
||||||
|
self.main_splitter.setSizes([int(self.width() * scale_factor), 0]) # Left pane takes all width initially (before resize)
|
||||||
|
|
||||||
self ._retranslate_ui ()
|
self ._retranslate_ui ()
|
||||||
|
|
||||||
if self .parent_app and hasattr (self .parent_app ,'get_dark_theme')and self .parent_app .current_theme =="dark":
|
if self .parent_app and hasattr (self .parent_app ,'get_dark_theme')and self .parent_app .current_theme =="dark":
|
||||||
self .setStyleSheet (self .parent_app .get_dark_theme ())
|
self .setStyleSheet (self .parent_app .get_dark_theme ())
|
||||||
|
|
||||||
|
# Set initial size for the dialog (before fetching posts)
|
||||||
|
self.resize(int((self.original_size.width() + 50) * scale_factor), int((self.original_size.height() + 100) * scale_factor)) # A bit larger than pure hint
|
||||||
|
|
||||||
QTimer .singleShot (0 ,self ._perform_initial_load )
|
QTimer .singleShot (0 ,self ._perform_initial_load )
|
||||||
|
|
||||||
|
def _center_on_screen(self):
|
||||||
|
"""Centers the dialog on the parent's screen or the primary screen."""
|
||||||
|
if self.parent_app:
|
||||||
|
parent_rect = self.parent_app.frameGeometry()
|
||||||
|
self.move(parent_rect.center() - self.rect().center())
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
screen_geo = QApplication.primaryScreen().availableGeometry()
|
||||||
|
self.move(screen_geo.center() - self.rect().center())
|
||||||
|
except AttributeError: # Fallback if no screen info (e.g., headless test)
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _handle_fetch_posts_click(self):
|
||||||
|
selected_creators = list(self.globally_selected_creators.values())
|
||||||
|
if not selected_creators:
|
||||||
|
QMessageBox.information(self, self._tr("no_selection_title", "No Selection"),
|
||||||
|
"Please select at least one creator to fetch posts for.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.parent_app:
|
||||||
|
parent_geometry = self.parent_app.geometry()
|
||||||
|
new_width = int(parent_geometry.width() * 0.75)
|
||||||
|
new_height = int(parent_geometry.height() * 0.80)
|
||||||
|
self.resize(new_width, new_height)
|
||||||
|
self._center_on_screen()
|
||||||
|
|
||||||
|
self.right_pane_widget.show()
|
||||||
|
QTimer.singleShot(10, lambda: self.main_splitter.setSizes([int(self.width() * 0.3), int(self.width() * 0.7)]))
|
||||||
|
self.add_selected_button.setEnabled(False)
|
||||||
|
self.globally_selected_post_ids.clear() # Clear previous post selections
|
||||||
|
self.posts_search_input.setVisible(True)
|
||||||
|
self.setWindowTitle(self._tr("creator_popup_title_fetching", "Creator Posts"))
|
||||||
|
|
||||||
|
self.fetch_posts_button.setEnabled(False)
|
||||||
|
self.posts_list_widget.clear()
|
||||||
|
self.fetched_posts_data.clear()
|
||||||
|
self.posts_area_title_label.setText(self._tr("fav_posts_loading_status", "Loading favorite posts...")) # Generic loading
|
||||||
|
self.posts_list_widget.itemChanged.connect(self._handle_post_item_check_changed) # Connect here
|
||||||
|
self.progress_bar.setVisible(True)
|
||||||
|
|
||||||
|
if self.post_fetch_thread and self.post_fetch_thread.isRunning():
|
||||||
|
self.post_fetch_thread.cancel()
|
||||||
|
self.post_fetch_thread.wait()
|
||||||
|
self.post_fetch_thread = PostsFetcherThread(selected_creators, self)
|
||||||
|
self.post_fetch_thread.status_update.connect(self._handle_fetch_status_update)
|
||||||
|
self.post_fetch_thread.posts_fetched_signal.connect(self._handle_posts_fetched)
|
||||||
|
self.post_fetch_thread.fetch_error_signal.connect(self._handle_fetch_error)
|
||||||
|
self.post_fetch_thread.finished_signal.connect(self._handle_fetch_finished)
|
||||||
|
self.post_fetch_thread.start()
|
||||||
|
|
||||||
def _tr (self ,key ,default_text =""):
|
def _tr (self ,key ,default_text =""):
|
||||||
"""Helper to get translation based on current app language."""
|
"""Helper to get translation based on current app language."""
|
||||||
if callable (get_translation )and self .parent_app :
|
if callable (get_translation )and self .parent_app :
|
||||||
@@ -863,7 +1003,16 @@ class EmptyPopupDialog (QDialog ):
|
|||||||
self .setWindowTitle (self ._tr ("creator_popup_title","Creator Selection"))
|
self .setWindowTitle (self ._tr ("creator_popup_title","Creator Selection"))
|
||||||
self .search_input .setPlaceholderText (self ._tr ("creator_popup_search_placeholder","Search by name, service, or paste creator URL..."))
|
self .search_input .setPlaceholderText (self ._tr ("creator_popup_search_placeholder","Search by name, service, or paste creator URL..."))
|
||||||
self .add_selected_button .setText (self ._tr ("creator_popup_add_selected_button","Add Selected"))
|
self .add_selected_button .setText (self ._tr ("creator_popup_add_selected_button","Add Selected"))
|
||||||
|
self .fetch_posts_button.setText(self._tr("fetch_posts_button_text", "Fetch Posts"))
|
||||||
self ._update_scope_button_text_and_tooltip ()
|
self ._update_scope_button_text_and_tooltip ()
|
||||||
|
|
||||||
|
self.posts_search_input.setPlaceholderText(self._tr("creator_popup_posts_search_placeholder", "Search fetched posts by title..."))
|
||||||
|
# Retranslate right pane elements
|
||||||
|
self.posts_area_title_label.setText(self._tr("creator_popup_posts_area_title", "Fetched Posts")) # Placeholder key
|
||||||
|
self.posts_select_all_button.setText(self._tr("select_all_button_text", "Select All"))
|
||||||
|
self.posts_deselect_all_button.setText(self._tr("deselect_all_button_text", "Deselect All"))
|
||||||
|
self.posts_add_selected_button.setText(self._tr("creator_popup_add_posts_to_queue_button", "Add Selected Posts to Queue")) # Placeholder key
|
||||||
|
self.posts_close_button.setText(self._tr("fav_posts_cancel_button", "Cancel")) # Re-use cancel
|
||||||
|
|
||||||
def _perform_initial_load (self ):
|
def _perform_initial_load (self ):
|
||||||
"""Called by QTimer to load data after dialog is shown."""
|
"""Called by QTimer to load data after dialog is shown."""
|
||||||
@@ -1116,8 +1265,260 @@ class EmptyPopupDialog (QDialog ):
|
|||||||
self .scope_button .setToolTip (
|
self .scope_button .setToolTip (
|
||||||
f"Current Download Scope: {self .current_scope_mode }\n\n"
|
f"Current Download Scope: {self .current_scope_mode }\n\n"
|
||||||
f"Click to toggle between '{self .SCOPE_CHARACTERS }' and '{self .SCOPE_CREATORS }' scopes.\n"
|
f"Click to toggle between '{self .SCOPE_CHARACTERS }' and '{self .SCOPE_CREATORS }' scopes.\n"
|
||||||
f"'{self .SCOPE_CHARACTERS }': (Planned) Downloads into character-named folders directly in the main Download Location (artists mixed).\n"
|
f"'{self .SCOPE_CHARACTERS }': Downloads into character-named folders directly in the main Download Location (artists mixed).\n"
|
||||||
f"'{self .SCOPE_CREATORS }': (Planned) Downloads into artist-named subfolders within the main Download Location, then character folders inside those.")
|
f"'{self .SCOPE_CREATORS }': Downloads into artist-named subfolders within the main Download Location, then character folders inside those.")
|
||||||
|
|
||||||
|
def _handle_fetch_status_update(self, message):
|
||||||
|
if self.parent_app:
|
||||||
|
self.parent_app.log_signal.emit(f"[CreatorPopup Fetch] {message}")
|
||||||
|
self.posts_area_title_label.setText(message)
|
||||||
|
|
||||||
|
def _handle_posts_fetched(self, creator_info, posts_list):
|
||||||
|
creator_key = (creator_info.get('service'), str(creator_info.get('id')))
|
||||||
|
self.fetched_posts_data[creator_key] = posts_list
|
||||||
|
self._filter_fetched_posts_list() # Refresh list with current filter
|
||||||
|
|
||||||
|
def _filter_fetched_posts_list(self):
|
||||||
|
search_text = self.posts_search_input.text().lower().strip()
|
||||||
|
|
||||||
|
data_for_rebuild = {}
|
||||||
|
|
||||||
|
if not self.fetched_posts_data:
|
||||||
|
self.posts_area_title_label.setText(self._tr("no_posts_fetched_yet_status", "No posts fetched yet."))
|
||||||
|
elif not search_text:
|
||||||
|
data_for_rebuild = self.fetched_posts_data
|
||||||
|
total_posts_in_view = sum(len(posts) for posts in data_for_rebuild.values())
|
||||||
|
if total_posts_in_view > 0:
|
||||||
|
self.posts_area_title_label.setText(self._tr("fetched_posts_count_label", "Fetched {count} post(s). Select to add to queue.").format(count=total_posts_in_view))
|
||||||
|
else:
|
||||||
|
self.posts_area_title_label.setText(self._tr("no_posts_found_for_selection", "No posts found for selected creator(s)."))
|
||||||
|
else:
|
||||||
|
for creator_key, posts_list in self.fetched_posts_data.items():
|
||||||
|
matching_posts_for_creator = [
|
||||||
|
post for post in posts_list
|
||||||
|
if search_text in post.get('title', '').lower()
|
||||||
|
]
|
||||||
|
if matching_posts_for_creator:
|
||||||
|
data_for_rebuild[creator_key] = matching_posts_for_creator
|
||||||
|
|
||||||
|
total_matching_posts = sum(len(posts) for posts in data_for_rebuild.values())
|
||||||
|
if total_matching_posts > 0:
|
||||||
|
self.posts_area_title_label.setText(self._tr("fetched_posts_count_label_filtered", "Displaying {count} post(s) matching filter.").format(count=total_matching_posts))
|
||||||
|
else:
|
||||||
|
self.posts_area_title_label.setText(self._tr("no_posts_match_search_filter", "No posts match your search filter."))
|
||||||
|
|
||||||
|
self._rebuild_posts_list_widget(filtered_data_map=data_for_rebuild)
|
||||||
|
|
||||||
|
def _rebuild_posts_list_widget(self, filtered_data_map):
|
||||||
|
self.posts_list_widget.blockSignals(True) # Block signals during repopulation
|
||||||
|
self.posts_list_widget.clear()
|
||||||
|
data_to_display = filtered_data_map
|
||||||
|
|
||||||
|
if not data_to_display:
|
||||||
|
self.posts_list_widget.blockSignals(False)
|
||||||
|
return
|
||||||
|
|
||||||
|
sorted_creator_keys = sorted(
|
||||||
|
data_to_display.keys(),
|
||||||
|
key=lambda k: self.globally_selected_creators.get(k, {}).get('name', '').lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
total_posts_shown = 0
|
||||||
|
for creator_key in sorted_creator_keys:
|
||||||
|
creator_info_original = self.globally_selected_creators.get(creator_key)
|
||||||
|
if not creator_info_original:
|
||||||
|
continue
|
||||||
|
|
||||||
|
posts_for_this_creator = data_to_display.get(creator_key, [])
|
||||||
|
if not posts_for_this_creator:
|
||||||
|
continue
|
||||||
|
|
||||||
|
creator_header_item = QListWidgetItem(f"--- {self._tr('posts_for_creator_header', 'Posts for')} {creator_info_original['name']} ({creator_info_original['service']}) ---")
|
||||||
|
font = creator_header_item.font()
|
||||||
|
font.setBold(True)
|
||||||
|
creator_header_item.setFont(font)
|
||||||
|
creator_header_item.setFlags(Qt.NoItemFlags)
|
||||||
|
self.posts_list_widget.addItem(creator_header_item)
|
||||||
|
|
||||||
|
for post in posts_for_this_creator:
|
||||||
|
post_title = post.get('title', self._tr('untitled_post_placeholder', 'Untitled Post'))
|
||||||
|
item = QListWidgetItem(f" {post_title}")
|
||||||
|
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
|
||||||
|
item.setCheckState(Qt.Unchecked)
|
||||||
|
item_data = {
|
||||||
|
'title': post_title,
|
||||||
|
'id': post.get('id'),
|
||||||
|
'service': creator_info_original['service'],
|
||||||
|
'user_id': creator_info_original['id'],
|
||||||
|
'creator_name': creator_info_original['name'],
|
||||||
|
'full_post_data': post
|
||||||
|
}
|
||||||
|
item.setData(Qt.UserRole, item_data)
|
||||||
|
post_unique_key = (
|
||||||
|
item_data['service'],
|
||||||
|
str(item_data['user_id']),
|
||||||
|
str(item_data['id'])
|
||||||
|
)
|
||||||
|
if post_unique_key in self.globally_selected_post_ids:
|
||||||
|
item.setCheckState(Qt.Checked)
|
||||||
|
else:
|
||||||
|
item.setCheckState(Qt.Unchecked)
|
||||||
|
|
||||||
|
self.posts_list_widget.addItem(item)
|
||||||
|
total_posts_shown += 1
|
||||||
|
self.posts_list_widget.blockSignals(False) # Unblock signals
|
||||||
|
|
||||||
|
def _handle_fetch_error(self, creator_info, error_message):
|
||||||
|
creator_name = creator_info.get('name', 'Unknown Creator')
|
||||||
|
if self.parent_app:
|
||||||
|
self.parent_app.log_signal.emit(f"[CreatorPopup Fetch ERROR] For {creator_name}: {error_message}")
|
||||||
|
# Update title label to show there was an error for this creator
|
||||||
|
self.posts_area_title_label.setText(self._tr("fetch_error_for_creator_label", "Error fetching for {creator_name}").format(creator_name=creator_name))
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_fetch_finished(self):
|
||||||
|
self.fetch_posts_button.setEnabled(True)
|
||||||
|
self.progress_bar.setVisible(False)
|
||||||
|
|
||||||
|
if not self.fetched_posts_data:
|
||||||
|
if self.post_fetch_thread and self.post_fetch_thread.cancellation_flag.is_set():
|
||||||
|
self.posts_area_title_label.setText(self._tr("post_fetch_cancelled_status_done", "Post fetching cancelled."))
|
||||||
|
else:
|
||||||
|
self.posts_area_title_label.setText(self._tr("failed_to_fetch_or_no_posts_label", "Failed to fetch posts or no posts found."))
|
||||||
|
self.posts_search_input.setVisible(False)
|
||||||
|
elif not self.posts_list_widget.count() and not self.posts_search_input.text().strip():
|
||||||
|
self.posts_area_title_label.setText(self._tr("no_posts_found_for_selection", "No posts found for selected creator(s)."))
|
||||||
|
self.posts_search_input.setVisible(True)
|
||||||
|
else:
|
||||||
|
self.posts_search_input.setVisible(True)
|
||||||
|
|
||||||
|
def _handle_posts_select_all(self):
|
||||||
|
self.posts_list_widget.blockSignals(True)
|
||||||
|
for i in range(self.posts_list_widget.count()):
|
||||||
|
item = self.posts_list_widget.item(i)
|
||||||
|
if item.flags() & Qt.ItemIsUserCheckable:
|
||||||
|
item.setCheckState(Qt.Checked)
|
||||||
|
|
||||||
|
# Add to global selection if not already there
|
||||||
|
item_data = item.data(Qt.UserRole)
|
||||||
|
if item_data:
|
||||||
|
post_unique_key = (
|
||||||
|
item_data['service'],
|
||||||
|
str(item_data['user_id']),
|
||||||
|
str(item_data['id'])
|
||||||
|
)
|
||||||
|
self.globally_selected_post_ids.add(post_unique_key)
|
||||||
|
self.posts_list_widget.blockSignals(False)
|
||||||
|
|
||||||
|
def _handle_posts_deselect_all(self):
|
||||||
|
self.posts_list_widget.blockSignals(True)
|
||||||
|
for i in range(self.posts_list_widget.count()):
|
||||||
|
item = self.posts_list_widget.item(i)
|
||||||
|
if item.flags() & Qt.ItemIsUserCheckable:
|
||||||
|
item.setCheckState(Qt.Unchecked)
|
||||||
|
self.globally_selected_post_ids.clear() # Deselect all means clear all global selections
|
||||||
|
self.posts_list_widget.blockSignals(False)
|
||||||
|
|
||||||
|
def _handle_post_item_check_changed(self, item):
|
||||||
|
if not item or not item.data(Qt.UserRole): # Ignore header items or invalid items
|
||||||
|
return
|
||||||
|
|
||||||
|
item_data = item.data(Qt.UserRole)
|
||||||
|
post_unique_key = (
|
||||||
|
item_data['service'],
|
||||||
|
str(item_data['user_id']),
|
||||||
|
str(item_data['id'])
|
||||||
|
)
|
||||||
|
|
||||||
|
if item.checkState() == Qt.Checked:
|
||||||
|
self.globally_selected_post_ids.add(post_unique_key)
|
||||||
|
else:
|
||||||
|
self.globally_selected_post_ids.discard(post_unique_key)
|
||||||
|
|
||||||
|
def _handle_posts_add_selected_to_queue(self):
|
||||||
|
selected_posts_for_queue = []
|
||||||
|
if not self.globally_selected_post_ids:
|
||||||
|
QMessageBox.information(self, self._tr("no_selection_title", "No Selection"),
|
||||||
|
self._tr("select_posts_to_queue_message", "Please select at least one post to add to the queue."))
|
||||||
|
return
|
||||||
|
|
||||||
|
for post_key in self.globally_selected_post_ids:
|
||||||
|
service, user_id_str, post_id_str = post_key
|
||||||
|
post_data_found = None
|
||||||
|
creator_key_for_fetched_data = (service, user_id_str)
|
||||||
|
|
||||||
|
if creator_key_for_fetched_data in self.fetched_posts_data:
|
||||||
|
for post_in_list in self.fetched_posts_data[creator_key_for_fetched_data]:
|
||||||
|
if str(post_in_list.get('id')) == post_id_str:
|
||||||
|
post_data_found = post_in_list
|
||||||
|
break
|
||||||
|
|
||||||
|
if post_data_found:
|
||||||
|
creator_info_original = self.globally_selected_creators.get(creator_key_for_fetched_data)
|
||||||
|
creator_name = creator_info_original.get('name', 'Unknown Creator') if creator_info_original else 'Unknown Creator'
|
||||||
|
|
||||||
|
domain = self._get_domain_for_service(service)
|
||||||
|
post_url = f"https://{domain}/{service}/user/{user_id_str}/post/{post_id_str}"
|
||||||
|
queue_item = {
|
||||||
|
'type': 'single_post_from_popup',
|
||||||
|
'url': post_url,
|
||||||
|
'name': post_data_found.get('title', self._tr('untitled_post_placeholder', 'Untitled Post')),
|
||||||
|
'name_for_folder': creator_name,
|
||||||
|
'service': service,
|
||||||
|
'user_id': user_id_str,
|
||||||
|
'post_id': post_id_str
|
||||||
|
}
|
||||||
|
selected_posts_for_queue.append(queue_item)
|
||||||
|
else:
|
||||||
|
# This case might happen if fetched_posts_data was cleared or modified unexpectedly
|
||||||
|
# For robustness, we could try to reconstruct minimal info if needed,
|
||||||
|
# or log that the full data for a selected post was not found.
|
||||||
|
# For now, just log it if parent_app is available.
|
||||||
|
if self.parent_app and hasattr(self.parent_app, 'log_signal'):
|
||||||
|
self.parent_app.log_signal.emit(f"⚠️ Could not find full post data for selected key: {post_key} when adding to queue.")
|
||||||
|
# Fallback: create a queue item with minimal info from the key itself
|
||||||
|
else: # Minimal fallback if full data is gone
|
||||||
|
domain = self._get_domain_for_service(service)
|
||||||
|
post_url = f"https://{domain}/{service}/user/{user_id_str}/post/{post_id_str}"
|
||||||
|
queue_item = {
|
||||||
|
'type': 'single_post_from_popup',
|
||||||
|
'url': post_url,
|
||||||
|
'name': f"post id {post_id_str}", # fallback name
|
||||||
|
'name_for_folder': user_id_str, # fallback folder name
|
||||||
|
'service': service,
|
||||||
|
'user_id': user_id_str,
|
||||||
|
'post_id': post_id_str
|
||||||
|
}
|
||||||
|
selected_posts_for_queue.append(queue_item)
|
||||||
|
|
||||||
|
if selected_posts_for_queue:
|
||||||
|
if self.parent_app and hasattr(self.parent_app, 'favorite_download_queue'):
|
||||||
|
for qi in selected_posts_for_queue:
|
||||||
|
self.parent_app.favorite_download_queue.append(qi)
|
||||||
|
self.parent_app.log_signal.emit(f"ℹ️ Added {len(selected_posts_for_queue)} selected posts to the download queue.")
|
||||||
|
if self.parent_app.link_input:
|
||||||
|
self.parent_app.link_input.setPlaceholderText(
|
||||||
|
self._tr("items_in_queue_placeholder", "{count} items in queue from popup.").format(count=len(self.parent_app.favorite_download_queue))
|
||||||
|
)
|
||||||
|
self.parent_app.link_input.clear()
|
||||||
|
self.accept()
|
||||||
|
else:
|
||||||
|
QMessageBox.information(self, self._tr("no_selection_title", "No Selection"),
|
||||||
|
self._tr("select_posts_to_queue_message", "Please select at least one post to add to the queue."))
|
||||||
|
|
||||||
|
def _handle_posts_close_view(self):
|
||||||
|
self.right_pane_widget.hide()
|
||||||
|
self.main_splitter.setSizes([self.width(), 0])
|
||||||
|
self.posts_list_widget.itemChanged.disconnect(self._handle_post_item_check_changed) # Disconnect
|
||||||
|
self.posts_search_input.setVisible(False)
|
||||||
|
self.posts_search_input.clear()
|
||||||
|
self.globally_selected_post_ids.clear()
|
||||||
|
self.add_selected_button.setEnabled(True)
|
||||||
|
self.setWindowTitle(self._tr("creator_popup_title", "Creator Selection"))
|
||||||
|
# Optionally clear posts list and data
|
||||||
|
# self.posts_list_widget.clear()
|
||||||
|
# self.fetched_posts_data.clear()
|
||||||
|
|
||||||
def _get_domain_for_service (self ,service_name ):
|
def _get_domain_for_service (self ,service_name ):
|
||||||
"""Determines the base domain for a given service."""
|
"""Determines the base domain for a given service."""
|
||||||
service_lower =service_name .lower ()
|
service_lower =service_name .lower ()
|
||||||
@@ -1163,6 +1564,105 @@ class EmptyPopupDialog (QDialog ):
|
|||||||
else :
|
else :
|
||||||
if unique_key in self .globally_selected_creators :
|
if unique_key in self .globally_selected_creators :
|
||||||
del self .globally_selected_creators [unique_key ]
|
del self .globally_selected_creators [unique_key ]
|
||||||
|
self.fetch_posts_button.setEnabled(bool(self.globally_selected_creators))
|
||||||
|
|
||||||
|
class PostsFetcherThread(QThread):
|
||||||
|
status_update = pyqtSignal(str)
|
||||||
|
posts_fetched_signal = pyqtSignal(object, list) # creator_info (dict), posts_list
|
||||||
|
fetch_error_signal = pyqtSignal(object, str) # creator_info (dict), error_message
|
||||||
|
finished_signal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, creators_to_fetch, parent_dialog_ref):
|
||||||
|
super().__init__()
|
||||||
|
self.creators_to_fetch = creators_to_fetch
|
||||||
|
self.parent_dialog = parent_dialog_ref
|
||||||
|
self.cancellation_flag = threading.Event() # Use a threading.Event for cancellation
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
self.cancellation_flag.set() # Set the event
|
||||||
|
self.status_update.emit(self.parent_dialog._tr("post_fetch_cancelled_status", "Post fetching cancellation requested..."))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
if not self.creators_to_fetch:
|
||||||
|
self.status_update.emit(self.parent_dialog._tr("no_creators_to_fetch_status", "No creators selected to fetch posts for."))
|
||||||
|
self.finished_signal.emit()
|
||||||
|
return
|
||||||
|
|
||||||
|
for creator_data in self.creators_to_fetch:
|
||||||
|
if self.cancellation_flag.is_set(): # Check the event
|
||||||
|
break
|
||||||
|
|
||||||
|
creator_name = creator_data.get('name', 'Unknown Creator')
|
||||||
|
service = creator_data.get('service')
|
||||||
|
user_id = creator_data.get('id')
|
||||||
|
|
||||||
|
if not service or not user_id:
|
||||||
|
self.fetch_error_signal.emit(creator_data, f"Missing service or ID for {creator_name}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.status_update.emit(self.parent_dialog._tr("fetching_posts_for_creator_status_all_pages", "Fetching all posts for {creator_name} ({service})... This may take a while.").format(creator_name=creator_name, service=service))
|
||||||
|
|
||||||
|
domain = self.parent_dialog._get_domain_for_service(service)
|
||||||
|
api_url_base = f"https://{domain}/api/v1/{service}/user/{user_id}"
|
||||||
|
|
||||||
|
# download_from_api will handle cookie preparation based on these params
|
||||||
|
use_cookie_param = False
|
||||||
|
cookie_text_param = ""
|
||||||
|
selected_cookie_file_param = None
|
||||||
|
app_base_dir_param = None
|
||||||
|
|
||||||
|
if self.parent_dialog.parent_app:
|
||||||
|
app = self.parent_dialog.parent_app
|
||||||
|
use_cookie_param = app.use_cookie_checkbox.isChecked()
|
||||||
|
cookie_text_param = app.cookie_text_input.text().strip()
|
||||||
|
selected_cookie_file_param = app.selected_cookie_filepath
|
||||||
|
app_base_dir_param = app.app_base_dir
|
||||||
|
|
||||||
|
all_posts_for_this_creator = []
|
||||||
|
try:
|
||||||
|
post_generator = download_from_api(
|
||||||
|
api_url_base,
|
||||||
|
logger=lambda msg: self.status_update.emit(f"[API Fetch - {creator_name}] {msg}"),
|
||||||
|
# end_page=1, # REMOVED to fetch all pages
|
||||||
|
use_cookie=use_cookie_param,
|
||||||
|
cookie_text=cookie_text_param,
|
||||||
|
selected_cookie_file=selected_cookie_file_param,
|
||||||
|
app_base_dir=app_base_dir_param,
|
||||||
|
cancellation_event=self.cancellation_flag # Pass the thread's own cancellation event
|
||||||
|
)
|
||||||
|
|
||||||
|
for posts_batch in post_generator:
|
||||||
|
if self.cancellation_flag.is_set(): # Check event here as well
|
||||||
|
self.status_update.emit(f"Post fetching for {creator_name} cancelled during pagination.")
|
||||||
|
break
|
||||||
|
all_posts_for_this_creator.extend(posts_batch)
|
||||||
|
self.status_update.emit(f"Fetched {len(all_posts_for_this_creator)} posts so far for {creator_name}...")
|
||||||
|
|
||||||
|
if not self.cancellation_flag.is_set():
|
||||||
|
self.posts_fetched_signal.emit(creator_data, all_posts_for_this_creator)
|
||||||
|
self.status_update.emit(f"Finished fetching {len(all_posts_for_this_creator)} posts for {creator_name}.")
|
||||||
|
else:
|
||||||
|
self.posts_fetched_signal.emit(creator_data, all_posts_for_this_creator) # Emit partial if any
|
||||||
|
self.status_update.emit(f"Fetching for {creator_name} cancelled. {len(all_posts_for_this_creator)} posts collected.")
|
||||||
|
|
||||||
|
except RuntimeError as e:
|
||||||
|
if "cancelled by user" in str(e).lower() or self.cancellation_flag.is_set():
|
||||||
|
self.status_update.emit(f"Post fetching for {creator_name} cancelled: {e}")
|
||||||
|
self.posts_fetched_signal.emit(creator_data, all_posts_for_this_creator)
|
||||||
|
else:
|
||||||
|
self.fetch_error_signal.emit(creator_data, f"Runtime error fetching posts for {creator_name}: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
self.fetch_error_signal.emit(creator_data, f"Error fetching posts for {creator_name}: {e}")
|
||||||
|
|
||||||
|
if self.cancellation_flag.is_set():
|
||||||
|
break
|
||||||
|
QThread.msleep(200)
|
||||||
|
|
||||||
|
if self.cancellation_flag.is_set():
|
||||||
|
self.status_update.emit(self.parent_dialog._tr("post_fetch_cancelled_status_done", "Post fetching cancelled."))
|
||||||
|
else:
|
||||||
|
self.status_update.emit(self.parent_dialog._tr("post_fetch_finished_status", "Finished fetching posts for selected creators."))
|
||||||
|
self.finished_signal.emit()
|
||||||
|
|
||||||
class CookieHelpDialog (QDialog ):
|
class CookieHelpDialog (QDialog ):
|
||||||
"""A dialog to explain how to get a cookies.txt file."""
|
"""A dialog to explain how to get a cookies.txt file."""
|
||||||
@@ -2251,7 +2751,10 @@ class HelpGuideDialog (QDialog ):
|
|||||||
self .parent_app =parent_app
|
self .parent_app =parent_app
|
||||||
|
|
||||||
app_icon = get_app_icon_object()
|
app_icon = get_app_icon_object()
|
||||||
if not app_icon.isNull():
|
if not app_icon.isNull(): # Check if icon is valid
|
||||||
|
self.setWindowIcon(app_icon)
|
||||||
|
else: # Fallback to default if icon is null
|
||||||
|
self.setWindowIcon(QIcon())
|
||||||
self.setWindowIcon(app_icon)
|
self.setWindowIcon(app_icon)
|
||||||
|
|
||||||
self .setModal (True )
|
self .setModal (True )
|
||||||
@@ -5264,18 +5767,199 @@ class DownloaderApp (QWidget ):
|
|||||||
if self ._is_download_active ():
|
if self ._is_download_active ():
|
||||||
QMessageBox .warning (self ,"Busy","A download is already running.")
|
QMessageBox .warning (self ,"Busy","A download is already running.")
|
||||||
return False
|
return False
|
||||||
if not direct_api_url and self .favorite_download_queue and not self .is_processing_favorites_queue :
|
|
||||||
is_from_creator_popup =False
|
|
||||||
if self .favorite_download_queue :
|
|
||||||
self .cancellation_message_logged_this_session =False
|
|
||||||
first_item_in_queue =self .favorite_download_queue [0 ]
|
|
||||||
if first_item_in_queue .get ('type')=='creator_popup_selection':
|
|
||||||
is_from_creator_popup =True
|
|
||||||
|
|
||||||
if is_from_creator_popup :
|
# If this call to start_download is not for a specific URL (e.g., user clicked main "Download" button)
|
||||||
self .log_signal .emit (f"ℹ️ Detected {len (self .favorite_download_queue )} creators queued from popup. Starting processing...")
|
# AND there are items in the favorite queue AND we are not already processing it.
|
||||||
self ._process_next_favorite_download ()
|
if not direct_api_url and self.favorite_download_queue and not self.is_processing_favorites_queue:
|
||||||
return True
|
self.log_signal.emit(f"ℹ️ Detected {len(self.favorite_download_queue)} item(s) in the queue. Starting processing...")
|
||||||
|
self.cancellation_message_logged_this_session = False # Reset for new queue processing session
|
||||||
|
self._process_next_favorite_download() # Directly call this to start processing the queue
|
||||||
|
return True # Indicate that the download process has been initiated via the queue
|
||||||
|
|
||||||
|
# If we reach here, it means either:
|
||||||
|
# 1. direct_api_url was provided (e.g., recursive call from _process_next_favorite_download)
|
||||||
|
# 2. The favorite_download_queue was empty or already being processed, so we fall back to link_input.
|
||||||
|
api_url = direct_api_url if direct_api_url else self.link_input.text().strip()
|
||||||
|
|
||||||
|
if self.favorite_mode_checkbox and self.favorite_mode_checkbox.isChecked() and not direct_api_url and not api_url: # Check api_url here too
|
||||||
|
QMessageBox.information(self, "Favorite Mode Active",
|
||||||
|
"Favorite Mode is active. Please use the 'Favorite Artists' or 'Favorite Posts' buttons to start downloads in this mode, or uncheck 'Favorite Mode' to use the URL input.")
|
||||||
|
self.set_ui_enabled(True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
main_ui_download_dir = self.dir_input.text().strip()
|
||||||
|
|
||||||
|
if not api_url and not self.favorite_download_queue: # If still no api_url and queue is empty
|
||||||
|
QMessageBox.critical(self, "Input Error", "URL is required.")
|
||||||
|
return False
|
||||||
|
elif not api_url and self.favorite_download_queue: # Safeguard: if URL input is empty but queue has items
|
||||||
|
self.log_signal.emit("ℹ️ URL input is empty, but queue has items. Processing queue...")
|
||||||
|
self.cancellation_message_logged_this_session = False
|
||||||
|
self._process_next_favorite_download() # This was the line with the unexpected indent
|
||||||
|
return True
|
||||||
|
|
||||||
|
self.cancellation_message_logged_this_session = False
|
||||||
|
use_subfolders = self.use_subfolders_checkbox.isChecked()
|
||||||
|
use_post_subfolders = self.use_subfolder_per_post_checkbox.isChecked()
|
||||||
|
compress_images = self.compress_images_checkbox.isChecked()
|
||||||
|
download_thumbnails = self.download_thumbnails_checkbox.isChecked()
|
||||||
|
|
||||||
|
use_multithreading_enabled_by_checkbox = self.use_multithreading_checkbox.isChecked()
|
||||||
|
try:
|
||||||
|
num_threads_from_gui = int(self.thread_count_input.text().strip())
|
||||||
|
if num_threads_from_gui < 1: num_threads_from_gui = 1
|
||||||
|
except ValueError:
|
||||||
|
QMessageBox.critical(self, "Thread Count Error", "Invalid number of threads. Please enter a positive number.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if use_multithreading_enabled_by_checkbox:
|
||||||
|
if num_threads_from_gui > MAX_THREADS:
|
||||||
|
hard_warning_msg = (
|
||||||
|
f"You've entered a thread count ({num_threads_from_gui}) exceeding the maximum of {MAX_THREADS}.\n\n"
|
||||||
|
"Using an extremely high number of threads can lead to:\n"
|
||||||
|
" - Diminishing returns (no significant speed increase).\n"
|
||||||
|
" - Increased system instability or application crashes.\n"
|
||||||
|
" - Higher chance of being rate-limited or temporarily IP-banned by the server.\n\n"
|
||||||
|
f"The thread count has been automatically capped to {MAX_THREADS} for stability."
|
||||||
|
)
|
||||||
|
QMessageBox.warning(self, "High Thread Count Warning", hard_warning_msg)
|
||||||
|
num_threads_from_gui = MAX_THREADS
|
||||||
|
self.thread_count_input.setText(str(MAX_THREADS))
|
||||||
|
self.log_signal.emit(f"⚠️ User attempted {num_threads_from_gui} threads, capped to {MAX_THREADS}.")
|
||||||
|
if SOFT_WARNING_THREAD_THRESHOLD < num_threads_from_gui <= MAX_THREADS:
|
||||||
|
soft_warning_msg_box = QMessageBox(self)
|
||||||
|
soft_warning_msg_box.setIcon(QMessageBox.Question)
|
||||||
|
soft_warning_msg_box.setWindowTitle("Thread Count Advisory")
|
||||||
|
soft_warning_msg_box.setText(
|
||||||
|
f"You've set the thread count to {num_threads_from_gui}.\n\n"
|
||||||
|
"While this is within the allowed limit, using a high number of threads (typically above 40-50) can sometimes lead to:\n"
|
||||||
|
" - Increased errors or failed file downloads.\n"
|
||||||
|
" - Connection issues with the server.\n"
|
||||||
|
" - Higher system resource usage.\n\n"
|
||||||
|
"For most users and connections, 10-30 threads provide a good balance.\n\n"
|
||||||
|
f"Do you want to proceed with {num_threads_from_gui} threads, or would you like to change the value?"
|
||||||
|
)
|
||||||
|
proceed_button = soft_warning_msg_box.addButton("Proceed Anyway", QMessageBox.AcceptRole)
|
||||||
|
change_button = soft_warning_msg_box.addButton("Change Thread Value", QMessageBox.RejectRole)
|
||||||
|
soft_warning_msg_box.setDefaultButton(proceed_button)
|
||||||
|
soft_warning_msg_box.setEscapeButton(change_button)
|
||||||
|
soft_warning_msg_box.exec_()
|
||||||
|
|
||||||
|
if soft_warning_msg_box.clickedButton() == change_button:
|
||||||
|
self.log_signal.emit(f"ℹ️ User opted to change thread count from {num_threads_from_gui} after advisory.")
|
||||||
|
self.thread_count_input.setFocus()
|
||||||
|
self.thread_count_input.selectAll()
|
||||||
|
return False
|
||||||
|
|
||||||
|
raw_skip_words = self.skip_words_input.text().strip()
|
||||||
|
skip_words_list = [word.strip().lower() for word in raw_skip_words.split(',') if word.strip()]
|
||||||
|
|
||||||
|
raw_remove_filename_words = self.remove_from_filename_input.text().strip() if hasattr(self, 'remove_from_filename_input') else ""
|
||||||
|
allow_multipart = self.allow_multipart_download_setting
|
||||||
|
remove_from_filename_words_list = [word.strip() for word in raw_remove_filename_words.split(',') if word.strip()]
|
||||||
|
scan_content_for_images = self.scan_content_images_checkbox.isChecked() if hasattr(self, 'scan_content_images_checkbox') else False
|
||||||
|
use_cookie_from_checkbox = self.use_cookie_checkbox.isChecked() if hasattr(self, 'use_cookie_checkbox') else False
|
||||||
|
app_base_dir_for_cookies = os.path.dirname(self.config_file)
|
||||||
|
cookie_text_from_input = self.cookie_text_input.text().strip() if hasattr(self, 'cookie_text_input') and use_cookie_from_checkbox else ""
|
||||||
|
|
||||||
|
use_cookie_for_this_run = use_cookie_from_checkbox
|
||||||
|
selected_cookie_file_path_for_backend = self.selected_cookie_filepath if use_cookie_from_checkbox and self.selected_cookie_filepath else None
|
||||||
|
|
||||||
|
if use_cookie_from_checkbox and not direct_api_url: # Only show cookie help if it's a fresh download start from UI
|
||||||
|
temp_cookies_for_check = prepare_cookies_for_request(
|
||||||
|
use_cookie_for_this_run,
|
||||||
|
cookie_text_from_input,
|
||||||
|
selected_cookie_file_path_for_backend,
|
||||||
|
app_base_dir_for_cookies,
|
||||||
|
lambda msg: self.log_signal.emit(f"[UI Cookie Check] {msg}")
|
||||||
|
)
|
||||||
|
if temp_cookies_for_check is None:
|
||||||
|
cookie_dialog = CookieHelpDialog(self, self, offer_download_without_option=True)
|
||||||
|
dialog_exec_result = cookie_dialog.exec_()
|
||||||
|
|
||||||
|
if cookie_dialog.user_choice == CookieHelpDialog.CHOICE_PROCEED_WITHOUT_COOKIES and dialog_exec_result == QDialog.Accepted:
|
||||||
|
self.log_signal.emit("ℹ️ User chose to download without cookies for this session.")
|
||||||
|
use_cookie_for_this_run = False
|
||||||
|
elif cookie_dialog.user_choice == CookieHelpDialog.CHOICE_CANCEL_DOWNLOAD or dialog_exec_result == QDialog.Rejected:
|
||||||
|
self.log_signal.emit("❌ Download cancelled by user at cookie prompt.")
|
||||||
|
return False
|
||||||
|
else: # Should not happen if dialog is modal and choices are handled
|
||||||
|
self.log_signal.emit("⚠️ Cookie dialog closed or unexpected choice. Aborting download.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
current_skip_words_scope = self.get_skip_words_scope()
|
||||||
|
manga_mode_is_checked = self.manga_mode_checkbox.isChecked() if self.manga_mode_checkbox else False
|
||||||
|
|
||||||
|
extract_links_only = (self.radio_only_links and self.radio_only_links.isChecked())
|
||||||
|
backend_filter_mode = self.get_filter_mode()
|
||||||
|
checked_radio_button = self.radio_group.checkedButton()
|
||||||
|
user_selected_filter_text = checked_radio_button.text() if checked_radio_button else "All"
|
||||||
|
|
||||||
|
effective_output_dir_for_run = ""
|
||||||
|
|
||||||
|
if selected_cookie_file_path_for_backend: # If a file is selected, cookie_text_from_input should be ignored by backend
|
||||||
|
cookie_text_from_input = ""
|
||||||
|
|
||||||
|
if backend_filter_mode == 'archive':
|
||||||
|
effective_skip_zip = False
|
||||||
|
effective_skip_rar = False
|
||||||
|
else:
|
||||||
|
effective_skip_zip = self.skip_zip_checkbox.isChecked()
|
||||||
|
effective_skip_rar = self.skip_rar_checkbox.isChecked()
|
||||||
|
if backend_filter_mode == 'audio': # Ensure audio mode doesn't force skip_zip/rar off
|
||||||
|
effective_skip_zip = self.skip_zip_checkbox.isChecked()
|
||||||
|
effective_skip_rar = self.skip_rar_checkbox.isChecked()
|
||||||
|
|
||||||
|
if not api_url: # This check is now after the queue processing
|
||||||
|
QMessageBox.critical(self, "Input Error", "URL is required.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if override_output_dir: # This is for items from the queue that need specific artist folders
|
||||||
|
if not main_ui_download_dir: # Main download dir must be set for this
|
||||||
|
QMessageBox.critical(self, "Configuration Error",
|
||||||
|
"The main 'Download Location' must be set in the UI "
|
||||||
|
"before downloading favorites with 'Artist Folders' scope.")
|
||||||
|
if self.is_processing_favorites_queue:
|
||||||
|
self.log_signal.emit(f"❌ Favorite download for '{api_url}' skipped: Main download directory not set.")
|
||||||
|
return False # Stop this specific item
|
||||||
|
|
||||||
|
if not os.path.isdir(main_ui_download_dir):
|
||||||
|
QMessageBox.critical(self, "Directory Error",
|
||||||
|
f"The main 'Download Location' ('{main_ui_download_dir}') "
|
||||||
|
"does not exist or is not a directory. Please set a valid one for 'Artist Folders' scope.")
|
||||||
|
if self.is_processing_favorites_queue:
|
||||||
|
self.log_signal.emit(f"❌ Favorite download for '{api_url}' skipped: Main download directory invalid.")
|
||||||
|
return False # Stop this specific item
|
||||||
|
effective_output_dir_for_run = os.path.normpath(override_output_dir)
|
||||||
|
else: # For direct URL input or items not needing override
|
||||||
|
if not extract_links_only and not main_ui_download_dir:
|
||||||
|
QMessageBox.critical(self, "Input Error", "Download Directory is required when not in 'Only Links' mode.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not extract_links_only and main_ui_download_dir and not os.path.isdir(main_ui_download_dir):
|
||||||
|
reply = QMessageBox.question(self, "Create Directory?",
|
||||||
|
f"The directory '{main_ui_download_dir}' does not exist.\nCreate it now?",
|
||||||
|
QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes)
|
||||||
|
if reply == QMessageBox.Yes:
|
||||||
|
try:
|
||||||
|
os.makedirs(main_ui_download_dir, exist_ok=True)
|
||||||
|
self.log_signal.emit(f"ℹ️ Created directory: {main_ui_download_dir}")
|
||||||
|
except Exception as e:
|
||||||
|
QMessageBox.critical(self, "Directory Error", f"Could not create directory: {e}")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.log_signal.emit("❌ Download cancelled: Output directory does not exist and was not created.")
|
||||||
|
return False
|
||||||
|
effective_output_dir_for_run = os.path.normpath(main_ui_download_dir)
|
||||||
|
|
||||||
|
service, user_id, post_id_from_url = extract_post_info(api_url)
|
||||||
|
if not service or not user_id: # This check is fine here
|
||||||
|
QMessageBox.critical(self, "Input Error", "Invalid or unsupported URL format.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ... (rest of the start_download method remains the same)
|
||||||
|
self ._process_next_favorite_download ()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
if self .favorite_mode_checkbox and self .favorite_mode_checkbox .isChecked ()and not direct_api_url :
|
if self .favorite_mode_checkbox and self .favorite_mode_checkbox .isChecked ()and not direct_api_url :
|
||||||
@@ -5849,6 +6533,8 @@ class DownloaderApp (QWidget ):
|
|||||||
self .download_thread .missed_character_post_signal .connect (self .handle_missed_character_post )
|
self .download_thread .missed_character_post_signal .connect (self .handle_missed_character_post )
|
||||||
if hasattr (self .download_thread ,'retryable_file_failed_signal'):
|
if hasattr (self .download_thread ,'retryable_file_failed_signal'):
|
||||||
self .download_thread .retryable_file_failed_signal .connect (self ._handle_retryable_file_failure )
|
self .download_thread .retryable_file_failed_signal .connect (self ._handle_retryable_file_failure )
|
||||||
|
if hasattr(self.download_thread, 'permanent_file_failed_signal'): # Ensure this signal exists on BackendDownloadThread
|
||||||
|
self.download_thread.permanent_file_failed_signal.connect(self._handle_permanent_file_failure_from_thread)
|
||||||
self .download_thread .start ()
|
self .download_thread .start ()
|
||||||
self .log_signal .emit ("✅ Single download thread (for posts) started.")
|
self .log_signal .emit ("✅ Single download thread (for posts) started.")
|
||||||
except Exception as e :
|
except Exception as e :
|
||||||
@@ -5876,6 +6562,12 @@ class DownloaderApp (QWidget ):
|
|||||||
if list_of_retry_details :
|
if list_of_retry_details :
|
||||||
self .retryable_failed_files_info .extend (list_of_retry_details )
|
self .retryable_failed_files_info .extend (list_of_retry_details )
|
||||||
|
|
||||||
|
def _handle_permanent_file_failure_from_thread(self, list_of_permanent_failure_details):
|
||||||
|
"""Handles permanently failed files signaled by the single BackendDownloadThread."""
|
||||||
|
if list_of_permanent_failure_details:
|
||||||
|
self.permanently_failed_files_for_dialog.extend(list_of_permanent_failure_details)
|
||||||
|
self.log_signal.emit(f"ℹ️ {len(list_of_permanent_failure_details)} file(s) from single-thread download marked as permanently failed for this session.")
|
||||||
|
|
||||||
def _submit_post_to_worker_pool (self ,post_data_item ,worker_args_template ,num_file_dl_threads_for_each_worker ,emitter_for_worker ,ppw_expected_keys ,ppw_optional_keys_with_defaults ):
|
def _submit_post_to_worker_pool (self ,post_data_item ,worker_args_template ,num_file_dl_threads_for_each_worker ,emitter_for_worker ,ppw_expected_keys ,ppw_optional_keys_with_defaults ):
|
||||||
"""Helper to prepare and submit a single post processing task to the thread pool."""
|
"""Helper to prepare and submit a single post processing task to the thread pool."""
|
||||||
global PostProcessorWorker
|
global PostProcessorWorker
|
||||||
@@ -6437,6 +7129,16 @@ class DownloaderApp (QWidget ):
|
|||||||
self .last_link_input_text_for_queue_sync =self .link_input .text ()
|
self .last_link_input_text_for_queue_sync =self .link_input .text ()
|
||||||
self .cancellation_message_logged_this_session =False
|
self .cancellation_message_logged_this_session =False
|
||||||
|
|
||||||
|
def _get_domain_for_service(self, service_name: str) -> str:
|
||||||
|
"""Determines the base domain for a given service."""
|
||||||
|
if not isinstance(service_name, str): # Basic type check
|
||||||
|
return "kemono.su" # Default fallback
|
||||||
|
service_lower = service_name.lower()
|
||||||
|
coomer_primary_services = {'onlyfans', 'fansly', 'manyvids', 'candfans', 'gumroad', 'patreon', 'subscribestar', 'dlsite', 'discord', 'fantia', 'boosty', 'pixiv', 'fanbox'} # Added more from your general usage
|
||||||
|
if service_lower in coomer_primary_services and service_lower not in ['patreon', 'discord', 'fantia', 'boosty', 'pixiv', 'fanbox']: # Explicitly keep these on kemono
|
||||||
|
return "coomer.su"
|
||||||
|
return "kemono.su"
|
||||||
|
|
||||||
def download_finished (self ,total_downloaded ,total_skipped ,cancelled_by_user ,kept_original_names_list =None ):
|
def download_finished (self ,total_downloaded ,total_skipped ,cancelled_by_user ,kept_original_names_list =None ):
|
||||||
if kept_original_names_list is None :
|
if kept_original_names_list is None :
|
||||||
kept_original_names_list =list (self .all_kept_original_filenames )if hasattr (self ,'all_kept_original_filenames')else []
|
kept_original_names_list =list (self .all_kept_original_filenames )if hasattr (self ,'all_kept_original_filenames')else []
|
||||||
@@ -7156,7 +7858,7 @@ class DownloaderApp (QWidget ):
|
|||||||
queue_item ={
|
queue_item ={
|
||||||
'url':direct_post_url ,
|
'url':direct_post_url ,
|
||||||
'name':post_data ['title'],
|
'name':post_data ['title'],
|
||||||
'name_for_folder':post_data ['creator_id'],
|
'name_for_folder': post_data['creator_name_resolved'], # Use resolved name
|
||||||
'type':'post'
|
'type':'post'
|
||||||
}
|
}
|
||||||
self .favorite_download_queue .append (queue_item )
|
self .favorite_download_queue .append (queue_item )
|
||||||
@@ -7187,6 +7889,7 @@ class DownloaderApp (QWidget ):
|
|||||||
next_url =self .current_processing_favorite_item_info ['url']
|
next_url =self .current_processing_favorite_item_info ['url']
|
||||||
item_display_name =self .current_processing_favorite_item_info .get ('name','Unknown Item')
|
item_display_name =self .current_processing_favorite_item_info .get ('name','Unknown Item')
|
||||||
|
|
||||||
|
item_type = self.current_processing_favorite_item_info.get('type', 'artist')
|
||||||
self .log_signal .emit (f"▶️ Processing next favorite from queue: '{item_display_name }' ({next_url })")
|
self .log_signal .emit (f"▶️ Processing next favorite from queue: '{item_display_name }' ({next_url })")
|
||||||
|
|
||||||
override_dir =None
|
override_dir =None
|
||||||
@@ -7195,11 +7898,21 @@ class DownloaderApp (QWidget ):
|
|||||||
item_scope =self .favorite_download_scope
|
item_scope =self .favorite_download_scope
|
||||||
|
|
||||||
main_download_dir =self .dir_input .text ().strip ()
|
main_download_dir =self .dir_input .text ().strip ()
|
||||||
if item_scope ==EmptyPopupDialog .SCOPE_CREATORS or (item_scope ==FAVORITE_SCOPE_ARTIST_FOLDERS and main_download_dir ):
|
|
||||||
|
# Determine if folder override is needed based on scope
|
||||||
|
# For 'creator_popup_selection', the scope is determined by dialog.current_scope_mode
|
||||||
|
# For 'artist' or 'single_post_from_popup' (queued from Favorite Artists/Posts dialogs), it's self.favorite_download_scope
|
||||||
|
should_create_artist_folder = False
|
||||||
|
if item_type == 'creator_popup_selection' and item_scope == EmptyPopupDialog.SCOPE_CREATORS:
|
||||||
|
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
|
||||||
|
|
||||||
|
if should_create_artist_folder and main_download_dir:
|
||||||
folder_name_key =self .current_processing_favorite_item_info .get ('name_for_folder','Unknown_Folder')
|
folder_name_key =self .current_processing_favorite_item_info .get ('name_for_folder','Unknown_Folder')
|
||||||
item_specific_folder_name =clean_folder_name (folder_name_key )
|
item_specific_folder_name =clean_folder_name (folder_name_key )
|
||||||
override_dir =os .path .normpath (os .path .join (main_download_dir ,item_specific_folder_name ))
|
override_dir =os .path .normpath (os .path .join (main_download_dir ,item_specific_folder_name ))
|
||||||
self .log_signal .emit (f" Favorite Scope: Artist Folders. Target directory: '{override_dir }'")
|
self .log_signal .emit (f" Scope requires artist folder. Target directory: '{override_dir }'")
|
||||||
|
|
||||||
success_starting_download =self .start_download (direct_api_url =next_url ,override_output_dir =override_dir )
|
success_starting_download =self .start_download (direct_api_url =next_url ,override_output_dir =override_dir )
|
||||||
|
|
||||||
|
|||||||