diff --git a/PyBuild/win_build.bat b/PyBuild/win_build.bat
index 44d6b98..57b074e 100644
--- a/PyBuild/win_build.bat
+++ b/PyBuild/win_build.bat
@@ -1,5 +1,5 @@
-@echo off
-cd..
-pip install -r requirements.txt
-pip install pyinstaller
+@echo off
+cd..
+pip install -r requirements.txt
+pip install pyinstaller
pyinstaller main.spec
\ No newline at end of file
diff --git a/README.md b/README.md
index 3f6ff74..dbeb485 100644
--- a/README.md
+++ b/README.md
@@ -1,49 +1,56 @@
-
-
-
Feed The Forge
-
-
-## Introduce
-This is a simple tool to download modpacks from FTB without the need of the FTB Launcher.
-
-You can then import or drag this zip file into any curseforge compatible launcher.
-
-For example: HMCL, PCL2, Prism Launcher etc.
-
-## Usage
-WIP
-
-## Develop and Build
-### Requirements
-
-- **Python Version**: 3.8+
-- **Supported Operating Systems**: Windows 10 or later, macOS, Linux
-
-### Running from Source
-
-1. **Install Dependencies**:
- - Open a terminal and run the following command to install required packages:
- ```bash
- pip install -r requirements.txt
- ```
-
-2. **Run**:
- - After installing the dependencies, run `__main__.py`
-
-### Building Executable for Windows
-
-1. **Package as Executable**:
- - To package the application as an executable for Windows, run the following script:
- ```bash
- cd PyBuild
- win_build.bat
- ```
-
-2. **Locate the Executable**:
- - The resulting `.exe` file will be located in the `dist` folder.
-
-3. **Additional Steps**:
- - Copy the `feedtheforge/lang` folder to the same directory as the `.exe` file to ensure the application runs correctly.
-
-## LICENSE
+
+
+
Feed The Forge
+
+
+## Introduce
+This is a simple tool to download modpacks from FTB without the need of the FTB Launcher.
+
+You can then import or drag this zip file into any curseforge compatible launcher.
+
+For example: HMCL, PCL2, Prism Launcher etc.
+
+## Usage
+WIP
+
+## Develop and Build
+### Requirements
+- **Git**: 3.8+
+- **Python Version**: 3.8+
+- **Supported Operating Systems**: Windows 10 or later, macOS, Linux
+
+### Running from Source
+
+1. **Install Dependencies**:
+ - Open a terminal and run the following command to install required packages:
+ ```bash
+ git clone https://github.com/Wulian233/FeedTheForge.git
+ cd FeedTheForge
+ pip install -r requirements.txt
+ ```
+
+2. **Run**:
+ - After installing the dependencies, run `__main__.py`
+
+### Building Executable for Windows
+
+1. **Package as Executable**:
+ - To package the application as an executable for Windows, run the following script:
+ - Windows
+ ```bash
+ cd PyBuild
+ win_build.bat
+ ```
+ - Unix
+ ```bash
+ cd PyBuild
+ unix_build.sh
+ ```
+2. **Locate the Executable**:
+ - The resulting `.exe` file will be located in the `dist` folder.
+
+3. **Additional Steps**:
+ - Copy the `feedtheforge/lang` folder to the `dist` directory
+
+## LICENSE
[GNU General Public License v3.0](.LICENSE)
\ No newline at end of file
diff --git a/__main__.py b/__main__.py
index f9357b0..beb431d 100644
--- a/__main__.py
+++ b/__main__.py
@@ -1,14 +1,14 @@
-if __name__ == "__main__":
- import asyncio
- import sys
- from main import *
- from feedtheforge.utils import pause
-
- py_version = sys.version_info
-
- if not (3, 8) < py_version < (3, 15):
- print(lang.t("feedtheforge.main.unsupported_version",
- cur=f"{py_version.major}.{py_version.minor}.{py_version.micro}"))
- pause()
- asyncio.run(main())
+if __name__ == "__main__":
+ import asyncio
+ import sys
+ from main import *
+ from feedtheforge.utils import pause
+
+ py_version = sys.version_info
+
+ if not (3, 8) < py_version < (3, 15):
+ print(lang.t("feedtheforge.main.unsupported_version",
+ cur=f"{py_version.major}.{py_version.minor}.{py_version.micro}"))
+ pause()
+ asyncio.run(main())
\ No newline at end of file
diff --git a/feedtheforge/const.py b/feedtheforge/const.py
index 916f0d7..aef4b47 100644
--- a/feedtheforge/const.py
+++ b/feedtheforge/const.py
@@ -1,42 +1,41 @@
-import locale
-import os
-import tempfile
-
-from feedtheforge.i18n import Locale
-
-# 自动切换语言
-if locale._getdefaultlocale()[0] == "zh_CN":
- lang = Locale("zh_CN")
-else:
- lang = Locale("en_US")
-
-current_language = lang.get_language()
-
-cache_dir = os.path.join(tempfile.gettempdir(), "FeedTheForge")
-packlist_path = os.path.join(cache_dir, "packlist.json")
-modpack_path = os.path.join(cache_dir, "pack_files")
-
-patch = os.path.join(cache_dir, "patch.zip")
-patch_folder = os.path.join(cache_dir, "patch")
-i18nupdate_link = "https://mediafilez.forgecdn.net/files/5335/196/I18nUpdateMod-3.5.5-all.jar"
-mod_path = os.path.join(modpack_path, "overrides", "mods")
-
-api_list = "https://api.modpacks.ch/public/modpack/all"
-api_featured = "https://api.modpacks.ch/public/modpack/featured/20"
-api_search = "https://api.modpacks.ch/public/modpack/search/20/detailed?platform=modpacksch&term="
-
-# 全部汉化 key: FTB唯一包版本 vaule: 蓝奏云汉化下载链接
-# TODO 未来应单独做出一个json并联网更新,不宜写死。支持匹配id全部版本支持
-all_patch = {
- # 100 StoneBlock 3
- "6498": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
- "6647": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
- "6967": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
- "11655": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
- # 115 Arcanum Institute
- "11512": "https://vmhanhuazu.lanzouj.com/i8W7Y1nr83le",
- # 122 Builders Paradise 2
- "11840": "https://wulian233.lanzouj.com/ib5G81wnrpwb",
- "11937": "https://wulian233.lanzouj.com/ib5G81wnrpwb",
- "12266": "https://wulian233.lanzouj.com/ib5G81wnrpwb"
-}
+import locale
+import os
+import tempfile
+
+from feedtheforge.i18n import Locale
+
+# 自动切换语言
+default_locale = locale.getdefaultlocale()[0]
+lang = Locale("zh_CN" if default_locale == "zh_CN" else "en_US")
+current_language = lang.get_language()
+
+# 缓存目录设置
+cache_dir = os.path.join(tempfile.gettempdir(), "FeedTheForge")
+packlist_path = os.path.join(cache_dir, "packlist.json")
+modpack_path = os.path.join(cache_dir, "pack_files")
+patch = os.path.join(cache_dir, "patch.zip")
+patch_folder = os.path.join(cache_dir, "patch")
+mod_path = os.path.join(modpack_path, "overrides", "mods")
+
+I18NUPDATE_LINK = "https://mediafilez.forgecdn.net/files/5335/196/I18nUpdateMod-3.5.5-all.jar"
+
+# API链接
+API_LIST = "https://api.modpacks.ch/public/modpack/all"
+API_FEATURED = "https://api.modpacks.ch/public/modpack/featured/20"
+API_SEARCH = "https://api.modpacks.ch/public/modpack/search/20/detailed?platform=modpacksch&term="
+
+# 全部汉化 key: FTB唯一包版本 vaule: 蓝奏云汉化下载链接
+# TODO 未来应单独做出一个json并联网更新,不宜写死。支持匹配id全部版本支持
+all_patch = {
+ # 100 StoneBlock 3
+ "6498": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
+ "6647": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
+ "6967": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
+ "11655": "https://wulian233.lanzouj.com/iwAZ61xg3yib",
+ # 115 Arcanum Institute
+ "11512": "https://vmhanhuazu.lanzouj.com/i8W7Y1nr83le",
+ # 122 Builders Paradise 2
+ "11840": "https://wulian233.lanzouj.com/ib5G81wnrpwb",
+ "11937": "https://wulian233.lanzouj.com/ib5G81wnrpwb",
+ "12266": "https://wulian233.lanzouj.com/ib5G81wnrpwb"
+}
diff --git a/feedtheforge/i18n.py b/feedtheforge/i18n.py
index 079e4e7..329ad01 100644
--- a/feedtheforge/i18n.py
+++ b/feedtheforge/i18n.py
@@ -1,40 +1,40 @@
-import json
-from pathlib import Path
-from string import Template
-
-
-class Locale:
- def __init__(self, lang: str):
- self.lang = lang
- self.path = Path(f"./feedtheforge/lang/{lang}.json")
- self.data = {}
- self.load()
-
- def __getitem__(self, key: str):
- return self.data[key]
-
- def __contains__(self, key: str):
- return key in self.data
-
- def load(self):
- with open(self.path, "r", encoding="utf-8") as f:
- d = f.read()
- self.data = json.loads(d)
- f.close()
-
- def get_string(self, key: str, failed_prompt):
- n = self.data.get(key, None)
- if n != None:
- return n
- if failed_prompt:
- return str(key) + self.t("feedtheforge.i18n.failed")
- return key
-
- def t(self, key: str, failed_prompt=True, *args, **kwargs):
- localized = self.get_string(key, failed_prompt)
- return Template(localized).safe_substitute(*args, **kwargs)
-
- def get_language(self):
- return self.lang
-
+import json
+from pathlib import Path
+from string import Template
+
+
+class Locale:
+ def __init__(self, lang: str):
+ self.lang = lang
+ self.path = Path(f"./feedtheforge/lang/{lang}.json")
+ self.data = {}
+ self.load()
+
+ def __getitem__(self, key: str):
+ return self.data[key]
+
+ def __contains__(self, key: str):
+ return key in self.data
+
+ def load(self):
+ with open(self.path, "r", encoding="utf-8") as f:
+ d = f.read()
+ self.data = json.loads(d)
+ f.close()
+
+ def get_string(self, key: str, failed_prompt):
+ n = self.data.get(key, None)
+ if n != None:
+ return n
+ if failed_prompt:
+ return str(key) + self.t("feedtheforge.i18n.failed")
+ return key
+
+ def t(self, key: str, failed_prompt=True, *args, **kwargs):
+ localized = self.get_string(key, failed_prompt)
+ return Template(localized).safe_substitute(*args, **kwargs)
+
+ def get_language(self):
+ return self.lang
+
lang = Locale("zh_CN")
\ No newline at end of file
diff --git a/feedtheforge/lang/en_US.json b/feedtheforge/lang/en_US.json
index 632a947..9551956 100644
--- a/feedtheforge/lang/en_US.json
+++ b/feedtheforge/lang/en_US.json
@@ -1,32 +1,33 @@
-{
- "feedtheforge.start.title": "Press Up or Down to select, Enter to confirm:",
- "feedtheforge.start.featured_modpack": "View Featured Modpacks",
- "feedtheforge.start.featured_modpack_desc": "View and download the top 5 modpacks of the last 20 days",
- "feedtheforge.start.search_modpack": "Search Modpacks",
- "feedtheforge.start.search_modpack_desc": "Enter English keywords to search for modpacks, then select to download",
- "feedtheforge.start.enter_id": "Enter Modpack ID",
- "feedtheforge.start.enter_id_desc": "Directly enter the numeric ID of the modpack to download",
- "feedtheforge.start.clean_temp": "Clean Cache",
- "feedtheforge.start.clean_temp_desc": "Clear cached modpack information. The next startup will be slower",
- "feedtheforge.start.exit": "Exit",
- "feedtheforge.start.exit_desc": "Exit the tool and close the window",
-
- "feedtheforge.main.clean_temp": "Cache cleaned successfully, a total of $size KB was cleaned",
- "feedtheforge.main.default_version": "Automatically selected the latest version $selected_version",
- "feedtheforge.main.enter_id": "Please enter the ID of the modpack to download:",
- "feedtheforge.main.enter_version": "Please enter the modpack version (leave blank for latest):",
- "feedtheforge.main.getting_error": "Network error, failed to retrieve modpack list.",
- "feedtheforge.main.getting_list": "Getting modpacks list...",
- "feedtheforge.main.has_chinese_patch": "Don't show in English",
- "feedtheforge.main.search_modpack": "Please enter keywords for the modpack:",
- "feedtheforge.main.invalid_pack_id": "No corresponding modpack found. Please enter a valid modpack ID",
- "feedtheforge.main.invalid_modpack_version": "No corresponding modpack version found. Please enter a valid version.",
- "feedtheforge.main.unsupported_version": "This program requires Python 3.8+ to run. The current version is Python $cur .",
- "feedtheforge.main.version_list": "Available versions for the current modpack: $version_list",
- "feedtheforge.main.modpack_created": "Compression successful. A modpack named $modpack_name has been created. Please drag it into the launcher to install",
- "feedtheforge.main.modpack_name": "Successfully selected $modpack_name",
- "feedtheforge.main.zipping_modpack": "Download completed, creating modpack installation package.",
-
- "feedtheforge.main.pause": "Press Enter to exit",
- "feedtheforge.i18n.failed": "Error: No corresponding localization string"
+{
+ "feedtheforge.start.title": "Press Up or Down to select, Enter to confirm:",
+ "feedtheforge.start.featured_modpack": "View Featured Modpacks",
+ "feedtheforge.start.featured_modpack_desc": "View and download the top 5 modpacks of the last 20 days",
+ "feedtheforge.start.search_modpack": "Search Modpacks",
+ "feedtheforge.start.search_modpack_desc": "Enter English keywords to search for modpacks, then select to download",
+ "feedtheforge.start.enter_id": "Enter Modpack ID",
+ "feedtheforge.start.enter_id_desc": "Directly enter the numeric ID of the modpack to download",
+ "feedtheforge.start.clean_temp": "Clean Cache",
+ "feedtheforge.start.clean_temp_desc": "Clear cached modpack information. The next startup will be slower",
+ "feedtheforge.start.exit": "Exit",
+ "feedtheforge.start.exit_desc": "Exit the tool and close the window",
+
+ "feedtheforge.main.clean_temp": "Cache cleaned successfully, a total of $size KB was cleaned",
+ "feedtheforge.main.default_version": "Automatically selected the latest version $selected_version",
+ "feedtheforge.main.empty_search": "No keywords entered or search results are empty, no corresponding modpack available",
+ "feedtheforge.main.enter_id": "Please enter the ID of the modpack to download:",
+ "feedtheforge.main.enter_version": "Please enter the modpack version (leave blank for latest):",
+ "feedtheforge.main.getting_error": "Network error, failed to retrieve modpack list.",
+ "feedtheforge.main.getting_list": "Getting modpacks list...",
+ "feedtheforge.main.has_chinese_patch": "Don't show in English",
+ "feedtheforge.main.search_modpack": "Please enter keywords for the modpack:",
+ "feedtheforge.main.invalid_pack_id": "No corresponding modpack found. Please enter a valid modpack ID",
+ "feedtheforge.main.invalid_modpack_version": "No corresponding modpack version found. Please enter a valid version.",
+ "feedtheforge.main.unsupported_version": "This program requires Python 3.8+ to run. The current version is Python $cur .",
+ "feedtheforge.main.version_list": "Available versions for the current modpack: $version_list",
+ "feedtheforge.main.modpack_created": "Compression successful. A modpack named $modpack_name has been created. Please drag it into the launcher to install",
+ "feedtheforge.main.modpack_name": "Successfully selected $modpack_name",
+ "feedtheforge.main.zipping_modpack": "Download completed, creating modpack installation package.",
+
+ "feedtheforge.main.pause": "Press Enter to exit",
+ "feedtheforge.i18n.failed": "Error: No corresponding localization string"
}
\ No newline at end of file
diff --git a/feedtheforge/lang/zh_CN.json b/feedtheforge/lang/zh_CN.json
index 9106f1c..213a4d8 100644
--- a/feedtheforge/lang/zh_CN.json
+++ b/feedtheforge/lang/zh_CN.json
@@ -1,32 +1,33 @@
-{
- "feedtheforge.start.title": "按 上 下 键 选 择 , 回 车 确 认 :",
- "feedtheforge.start.featured_modpack": "查 看 热 门 整 合 包",
- "feedtheforge.start.featured_modpack_desc": "查 看 当 前 近 20天 最 热 门 的 5个 整 合 包 并 选 择 下 载",
- "feedtheforge.start.search_modpack": "搜 索 整 合 包",
- "feedtheforge.start.search_modpack_desc": "输 入 英 文 关 键 词 搜 索 整 合 包 , 选 择 后 下 载",
- "feedtheforge.start.enter_id": "输 入 整 合 包 id",
- "feedtheforge.start.enter_id_desc":"直 接 输 入 整 合 包 对 应 的 数 字 id下 载",
- "feedtheforge.start.clean_temp": "清 除 工 具 缓 存",
- "feedtheforge.start.clean_temp_desc": "清 除 缓 存 的 整 合 包 信 息 , 下 次 启 动 会 变 慢",
- "feedtheforge.start.exit": "退 出",
- "feedtheforge.start.exit_desc": "退 出 工 具 , 关 闭 窗 口",
-
- "feedtheforge.main.clean_temp": "清理缓存成功,共清理了 $size kb",
- "feedtheforge.main.default_version": "已自动选择最新的 $selected_version 版本",
- "feedtheforge.main.enter_id": "请输入要下载的整合包id:",
- "feedtheforge.main.enter_version": "请输入整合包版本(留空默认最新):",
- "feedtheforge.main.getting_error": "网络错误,获取整合包列表失败。",
- "feedtheforge.main.getting_list": "正在获取整合包列表",
- "feedtheforge.main.has_chinese_patch": "本整合包有人工汉化补丁可用,是否自动下载并安装?输入Y安装:",
- "feedtheforge.main.search_modpack": "请输入整合包英文关键词:",
- "feedtheforge.main.invalid_pack_id": "没有对应的整合包。请输入正确的整合包id",
- "feedtheforge.main.invalid_modpack_version": "没有对应的整合包版本。请输入一个正确的版本。",
- "feedtheforge.main.unsupported_version": "该程序需要Python 3.8+运行,当前版本为Python $cur 。",
- "feedtheforge.main.version_list": "当前整合包可下载版本:$version_list",
- "feedtheforge.main.modpack_created": "压缩成功。已创建名为 $modpack_name 的整合包,请拖入启动器安装",
- "feedtheforge.main.modpack_name": "成功选择了 $modpack_name",
- "feedtheforge.main.zipping_modpack": "下载完成,正在压缩制作整合包安装包。",
-
- "feedtheforge.main.pause": "请按Enter键退出",
- "feedtheforge.i18n.failed": "错误:没有对应的本地化字符串"
+{
+ "feedtheforge.start.title": "按 上 下 键 选 择 , 回 车 确 认 :",
+ "feedtheforge.start.featured_modpack": "查 看 热 门 整 合 包",
+ "feedtheforge.start.featured_modpack_desc": "查 看 当 前 近 20天 最 热 门 的 5个 整 合 包 并 选 择 下 载",
+ "feedtheforge.start.search_modpack": "搜 索 整 合 包",
+ "feedtheforge.start.search_modpack_desc": "输 入 英 文 关 键 词 搜 索 整 合 包 , 选 择 后 下 载",
+ "feedtheforge.start.enter_id": "输 入 整 合 包 id",
+ "feedtheforge.start.enter_id_desc":"直 接 输 入 整 合 包 对 应 的 数 字 id下 载",
+ "feedtheforge.start.clean_temp": "清 除 工 具 缓 存",
+ "feedtheforge.start.clean_temp_desc": "清 除 缓 存 的 整 合 包 信 息 , 下 次 启 动 会 变 慢",
+ "feedtheforge.start.exit": "退 出",
+ "feedtheforge.start.exit_desc": "退 出 工 具 , 关 闭 窗 口",
+
+ "feedtheforge.main.clean_temp": "清理缓存成功,共清理了 $size kb",
+ "feedtheforge.main.default_version": "已自动选择最新的 $selected_version 版本",
+ "feedtheforge.main.empty_search": "",
+ "feedtheforge.main.enter_id": "请输入要下载的整合包id:",
+ "feedtheforge.main.enter_version": "请输入整合包版本(留未输入关键词或搜索结果为空,无对应整合包空默认最新):",
+ "feedtheforge.main.getting_error": "网络错误,获取整合包列表失败。",
+ "feedtheforge.main.getting_list": "正在获取整合包列表",
+ "feedtheforge.main.has_chinese_patch": "本整合包有人工汉化补丁可用,是否自动下载并安装?输入Y安装:",
+ "feedtheforge.main.search_modpack": "请输入整合包英文关键词:",
+ "feedtheforge.main.invalid_pack_id": "没有对应的整合包。请输入正确的整合包id",
+ "feedtheforge.main.invalid_modpack_version": "没有对应的整合包版本。请输入一个正确的版本。",
+ "feedtheforge.main.unsupported_version": "该程序需要Python 3.8+运行,当前版本为Python $cur 。",
+ "feedtheforge.main.version_list": "当前整合包可下载版本:$version_list",
+ "feedtheforge.main.modpack_created": "压缩成功。已创建名为 $modpack_name 的整合包,请拖入启动器安装",
+ "feedtheforge.main.modpack_name": "成功选择了 $modpack_name",
+ "feedtheforge.main.zipping_modpack": "下载完成,正在压缩制作整合包安装包。",
+
+ "feedtheforge.main.pause": "请按Enter键退出",
+ "feedtheforge.i18n.failed": "错误:没有对应的本地化字符串"
}
\ No newline at end of file
diff --git a/feedtheforge/utils.py b/feedtheforge/utils.py
index 7d05826..f77cc78 100644
--- a/feedtheforge/utils.py
+++ b/feedtheforge/utils.py
@@ -5,6 +5,12 @@
from feedtheforge.const import *
+async def download_file(session, url, output_path):
+ async with session.get(url) as response:
+ with open(output_path, "wb") as f:
+ while chunk := await response.content.read(1024):
+ f.write(chunk)
+
async def create_directory(path):
"""
创建目录,如果目录不存在则创建
diff --git a/main.py b/main.py
index bfb5502..f362693 100644
--- a/main.py
+++ b/main.py
@@ -1,255 +1,269 @@
-import aiohttp
-import asyncio
-import json
-import os
-import shutil
-from pick import pick, Option
-
-from feedtheforge import utils
-from feedtheforge.const import *
-
-
-async def download_file(session, url, output_path):
- async with session.get(url) as response:
- with open(output_path, "wb") as f:
- while chunk := await response.content.read(1024):
- f.write(chunk)
-
-
-async def download_files(session, files):
- tasks = []
- for file_info in files:
- file_path = file_info["path"][2:]
- file_name = file_info["name"]
- full_path = os.path.join(modpack_path, "overrides", file_path)
- output_path = os.path.join(full_path, file_name)
-
- if not os.path.exists(output_path):
- await utils.create_directory(full_path)
- tasks.append(download_file(session, file_info["url"], output_path))
-
- await asyncio.gather(*tasks)
-
-
-async def load_modpack_data(modpack_id):
- """异步加载整合包数据"""
- modpack_id_path = os.path.join(cache_dir, f"pack-{modpack_id}.json")
- url = f"https://api.modpacks.ch/public/modpack/{modpack_id}"
-
- async with aiohttp.ClientSession() as session:
- async with session.get(url) as response:
- data = await response.json()
-
- with open(modpack_id_path, "w", encoding="utf-8") as f:
- json.dump(data, f, indent=4)
-
- return data
-
-
-async def display_modpack_list(load_json):
- """读取json并制作对应的选择菜单"""
- options = []
- with open(load_json, "r", encoding="utf-8") as f:
- data = json.load(f)
- for modpack_id in data["packs"]:
- modpack_data = await load_modpack_data(modpack_id)
- modpack_name = modpack_data['name']
- options.append(Option(f"{modpack_name} (id: {modpack_id})", modpack_id))
-
- title = lang.t("feedtheforge.start.title")
- selected_modpack = pick(options, title, indicator="=>")
- return selected_modpack[0].value
-
-
-async def download_featured_modpack():
- featured_json = os.path.join(cache_dir, "featured_modpacks.json")
- # 检查7天内有没有缓存,加快速度
- if not await utils.is_recent_file(featured_json):
- async with aiohttp.ClientSession() as session:
- await download_file(session, api_featured, featured_json)
-
- # 下载选择的整合包
- modpack_id = await display_modpack_list(featured_json)
- await download_modpack(modpack_id)
-
-
-async def search_modpack():
- search_json = os.path.join(cache_dir, "search_modpacks.json")
- keyword = input(lang.t("feedtheforge.main.search_modpack"))
- print("未完成,敬请期待")
-
-
-async def apply_chinese_patch(lanzou_url):
- """蓝奏云直链解析下载汉化并自动覆盖应用汉化"""
- from feedtheforge.lanzou import LanzouDownloader
- from zipfile import ZipFile
- # 获取返回的json中downUrl的值为下载链接
- data = json.loads(LanzouDownloader().get_direct_link(lanzou_url))
- down_url = data.get("downUrl")
- async with aiohttp.ClientSession() as session:
- await download_file(session, down_url, patch)
-
- with ZipFile(patch, 'r') as zip_ref:
- zip_ref.extractall(patch_folder)
- os.remove(patch)
- # 把汉化移动剪切到整合包临时目录完成汉化
- for root, _, files in os.walk(patch_folder):
- for file in files:
- patch_file = os.path.join(root, file)
- relative_path = os.path.relpath(patch_file, patch_folder)
- target_path = os.path.join(modpack_path, "overrides", relative_path)
- os.makedirs(os.path.dirname(target_path), exist_ok=True)
- shutil.move(patch_file, target_path)
- shutil.rmtree(patch_folder)
-
-
-async def download_modpack(modpack_id):
- modpack_data = await load_modpack_data(modpack_id)
- modpack_name = modpack_data["name"]
- modpack_author = modpack_data["authors"][0]["name"]
- versions = modpack_data["versions"]
- version_list = [version["id"] for version in versions]
-
- print(lang.t("feedtheforge.main.modpack_name", modpack_name=modpack_name))
- print(lang.t("feedtheforge.main.version_list", version_list=version_list))
- selected_version = input(lang.t("feedtheforge.main.enter_version"))
- # 输入为空且有版本可下载(更保险),取最新版本
- if not selected_version and version_list:
- selected_version = str(max(version_list))
- print(lang.t("feedtheforge.main.default_version", selected_version=selected_version))
- # id无对应整合包或不是数字
- else:
- print(lang.t("feedtheforge.main.invalid_modpack_version"))
- utils.pause()
-
-
- async with aiohttp.ClientSession() as session:
- download_url = f"https://api.modpacks.ch/public/modpack/{modpack_id}/{selected_version}"
- await download_file(session, download_url, os.path.join(cache_dir, "download.json"))
- await prepare_modpack_files(modpack_name, modpack_author, selected_version, session)
-
- if current_language == "zh_CN":
- async with aiohttp.ClientSession() as session:
- # 切片[-27:]恰为模组文件名
- await download_file(session, i18nupdate_link, os.path.join(mod_path, i18nupdate_link[-27:]))
- # 检查有无对应汉化
- if str(selected_version) in all_patch:
- install = input(lang.t("feedtheforge.main.has_chinese_patch"))
- if install.lower() == "y":
- await apply_chinese_patch(all_patch[selected_version])
-
- utils.zip_modpack(modpack_name)
-
-
-async def prepare_modpack_files(modpack_name, modpack_author, modpack_version, session):
- os.makedirs(modpack_path, exist_ok=True)
- with open(os.path.join(cache_dir, "download.json"), "r", encoding="utf-8") as f:
- data = json.load(f)
- # 下面均为CurseForge整合包识别的固定格式
- mc_version = data["targets"][1]["version"]
- modloader_name = data["targets"][0]["name"]
- modloader_version = data["targets"][0]["version"]
-
- curse_files, non_curse_files = [], []
- for file_info in data["files"]:
- if "curseforge" in file_info:
- curse_files.append({
- "fileID": file_info["curseforge"]["file"],
- "projectID": file_info["curseforge"]["project"],
- "required": True
- })
- else:
- non_curse_files.append(file_info)
-
- modloader_id = f"{modloader_name}-{modloader_version}"
- if modloader_name == "neoforge" and mc_version == "1.20.1":
- modloader_id = f"{modloader_name}-{mc_version}-{modloader_version}"
-
- manifest_data = {
- "author": modpack_author,
- "files": curse_files,
- "manifestType": "minecraftModpack",
- "manifestVersion": 1,
- "minecraft": {
- "version": mc_version,
- "modLoaders": [{"id": modloader_id, "primary": True}]
- },
- "name": modpack_name,
- "overrides": "overrides",
- "version": modpack_version
- }
-
- with open(os.path.join(modpack_path, "manifest.json"), "w", encoding="utf-8") as f:
- json.dump(manifest_data, f, indent=4)
-
- # FIXME 生成 modlist.html 文件
- modlist_file = os.path.join(modpack_path, "modlist.html")
- with open(modlist_file, "w", encoding="utf-8") as f:
- f.write("\n")
- # for file_info in curse_files:
- # mod_page_url = file_info.get("url", "#")
- # f.write(f'- {file_info["fileID"]}
\n')
- f.write("
\n")
-
- os.makedirs(os.path.join(modpack_path, "overrides"), exist_ok=True)
- await download_files(session, non_curse_files)
-
-
-async def fetch_modpack_list():
- print(lang.t("feedtheforge.main.getting_list"))
- try:
- async with aiohttp.ClientSession() as session:
- async with session.get(api_list) as response:
- if response.status == 200:
- modpacks_data = await response.json()
- with open(packlist_path, "w", encoding="utf-8") as f:
- json.dump(modpacks_data, f, indent=4)
- except OSError:
- print(lang.t("feedtheforge.main.getting_error"))
- utils.pause()
-
- # 从本地文件读取数据
- with open(packlist_path, "r", encoding="utf-8") as f:
- modpacks_data = json.load(f)
-
- global all_pack_ids
- all_pack_ids = [str(pack_id) for pack_id in modpacks_data.get("packs", [])]
- print(all_pack_ids)
-
-async def main():
- if not os.path.exists(cache_dir):
- os.makedirs(cache_dir)
-
- # 本地化中这里的字中间要有空格,不加空格VSCode终端正常,在cmd中字会重叠
- title = lang.t("feedtheforge.start.title")
- options = [
- Option(lang.t("feedtheforge.start.featured_modpack"),
- description=lang.t("feedtheforge.start.featured_modpack_desc")),
- Option(lang.t("feedtheforge.start.search_modpack"),
- description=lang.t("feedtheforge.start.search_modpack_desc")),
- Option(lang.t("feedtheforge.start.enter_id"),
- description=lang.t("feedtheforge.start.enter_id_desc")),
- Option(lang.t("feedtheforge.start.clean_temp"),
- description=lang.t("feedtheforge.start.clean_temp_desc")),
- Option(lang.t("feedtheforge.start.exit"),
- description=lang.t("feedtheforge.start.exit_desc"))
- ]
-
- option, index = pick(options, title, indicator="=>")
-
- # 根据选择执行相应的操作
- if index == 0:
- await download_featured_modpack()
- elif index == 1:
- await search_modpack()
- utils.pause()
- elif index == 2:
- await fetch_modpack_list()
- modpack_id = input(lang.t("feedtheforge.main.enter_id"))
- if modpack_id not in all_pack_ids:
- print(lang.t("feedtheforge.main.invalid_pack_id"))
- return
- await download_modpack(modpack_id)
- elif index == 3:
- utils.clean_temp()
- utils.pause()
+import aiohttp
+import asyncio
+import json
+import os
+import shutil
+from pick import pick, Option
+
+from feedtheforge import utils
+from feedtheforge.const import *
+
+
+async def download_files(session, files):
+ tasks = []
+ for file_info in files:
+ file_path = file_info["path"][2:]
+ file_name = file_info["name"]
+ full_path = os.path.join(modpack_path, "overrides", file_path)
+ output_path = os.path.join(full_path, file_name)
+
+ if not os.path.exists(output_path):
+ await utils.create_directory(full_path)
+ tasks.append(utils.download_file(session, file_info["url"], output_path))
+
+ await asyncio.gather(*tasks)
+
+
+async def load_modpack_data(modpack_id):
+ """异步加载整合包数据"""
+ modpack_id_path = os.path.join(cache_dir, f"pack-{modpack_id}.json")
+ url = f"https://api.modpacks.ch/public/modpack/{modpack_id}"
+
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ data = await response.json()
+
+ with open(modpack_id_path, "w", encoding="utf-8") as f:
+ json.dump(data, f, indent=4)
+
+ return data
+
+
+async def display_modpack_list(load_json):
+ """读取json并制作对应的选择菜单"""
+ options = []
+ with open(load_json, "r", encoding="utf-8") as f:
+ data = json.load(f)
+ for modpack in data["packs"]:
+ # 处理字典形式的数据,即搜索整合包
+ if isinstance(modpack, dict): modpack_id = modpack['id']
+ # 直接是ID的情况,即查看流行的整合包
+ else: modpack_id = modpack
+ # 屏蔽模组加载器
+ if modpack_id in [116, 104, 81, 105]:
+ continue
+ modpack_data = await load_modpack_data(modpack_id)
+ modpack_name = modpack_data['name']
+
+ options.append(Option(f"{modpack_name} (id: {modpack_id})", modpack_id))
+ # 防止搜索结果为空
+ if not options:
+ print(lang.t("feedtheforge.main.empty_search"))
+ utils.pause()
+
+ title = lang.t("feedtheforge.start.title")
+ selected_modpack = pick(options, title, indicator="=>")
+ return selected_modpack[0].value
+
+
+async def download_featured_modpack():
+ featured_json = os.path.join(cache_dir, "featured_modpacks.json")
+ # 检查7天内有没有缓存,加快速度
+ if not await utils.is_recent_file(featured_json):
+ async with aiohttp.ClientSession() as session:
+ await utils.download_file(session, API_FEATURED, featured_json)
+
+ # 下载选择的整合包
+ modpack_id = await display_modpack_list(featured_json)
+ await download_modpack(modpack_id)
+
+
+async def search_modpack():
+ import string
+
+ search_json = os.path.join(cache_dir, "search_modpacks.json")
+ keyword = input(lang.t("feedtheforge.main.search_modpack"))
+ # 防止搜索为空或不是英文字母数字
+ if not keyword or keyword not in string.printable:
+ print(lang.t("feedtheforge.main.empty_search"))
+ utils.pause()
+
+ async with aiohttp.ClientSession() as session:
+ await utils.download_file(session, API_SEARCH + keyword, search_json)
+ # 下载选择的整合包
+ modpack_id = await display_modpack_list(search_json)
+ await download_modpack(modpack_id)
+
+async def apply_chinese_patch(lanzou_url):
+ """蓝奏云直链解析下载汉化并自动覆盖应用汉化"""
+ from feedtheforge.lanzou import LanzouDownloader
+ from zipfile import ZipFile
+ # 获取返回的json中downUrl的值为下载链接
+ data = json.loads(LanzouDownloader().get_direct_link(lanzou_url))
+ down_url = data.get("downUrl")
+ async with aiohttp.ClientSession() as session:
+ await utils.download_file(session, down_url, patch)
+
+ with ZipFile(patch, 'r') as zip_ref:
+ zip_ref.extractall(patch_folder)
+ os.remove(patch)
+ # 把汉化移动剪切到整合包临时目录完成汉化
+ for root, _, files in os.walk(patch_folder):
+ for file in files:
+ patch_file = os.path.join(root, file)
+ relative_path = os.path.relpath(patch_file, patch_folder)
+ target_path = os.path.join(modpack_path, "overrides", relative_path)
+ os.makedirs(os.path.dirname(target_path), exist_ok=True)
+ shutil.move(patch_file, target_path)
+ shutil.rmtree(patch_folder)
+
+
+async def download_modpack(modpack_id):
+ modpack_data = await load_modpack_data(modpack_id)
+ modpack_name = modpack_data["name"]
+ modpack_author = modpack_data["authors"][0]["name"]
+ versions = modpack_data["versions"]
+ version_list = [version["id"] for version in versions]
+
+ print(lang.t("feedtheforge.main.modpack_name", modpack_name=modpack_name))
+ print(lang.t("feedtheforge.main.version_list", version_list=version_list))
+ selected_version = input(lang.t("feedtheforge.main.enter_version"))
+ # 输入为空且有版本可下载(更保险),取最新版本
+ if not selected_version and version_list:
+ selected_version = str(max(int(version) for version in version_list))
+ print(lang.t("feedtheforge.main.default_version", selected_version=selected_version))
+ # id无对应整合包或不是数字
+ else:
+ print(lang.t("feedtheforge.main.invalid_modpack_version"))
+ utils.pause()
+
+
+ async with aiohttp.ClientSession() as session:
+ download_url = f"https://api.modpacks.ch/public/modpack/{modpack_id}/{selected_version}"
+ await utils.download_file(session, download_url, os.path.join(cache_dir, "download.json"))
+ await prepare_modpack_files(modpack_name, modpack_author, selected_version, session)
+
+ if current_language == "zh_CN":
+ async with aiohttp.ClientSession() as session:
+ # 切片[-27:]恰为模组文件名
+ await utils.download_file(session, I18NUPDATE_LINK, os.path.join(mod_path, I18NUPDATE_LINK[-27:]))
+ # 检查有无对应汉化
+ if str(selected_version) in all_patch:
+ install = input(lang.t("feedtheforge.main.has_chinese_patch"))
+ if install.lower() == "y":
+ await apply_chinese_patch(all_patch[selected_version])
+
+ utils.zip_modpack(modpack_name)
+
+
+async def prepare_modpack_files(modpack_name, modpack_author, modpack_version, session):
+ os.makedirs(modpack_path, exist_ok=True)
+ with open(os.path.join(cache_dir, "download.json"), "r", encoding="utf-8") as f:
+ data = json.load(f)
+ # 下面均为CurseForge整合包识别的固定格式
+ mc_version = data["targets"][1]["version"]
+ modloader_name = data["targets"][0]["name"]
+ modloader_version = data["targets"][0]["version"]
+
+ curse_files, non_curse_files = [], []
+ for file_info in data["files"]:
+ if "curseforge" in file_info:
+ curse_files.append({
+ "fileID": file_info["curseforge"]["file"],
+ "projectID": file_info["curseforge"]["project"],
+ "required": True
+ })
+ else:
+ non_curse_files.append(file_info)
+
+ modloader_id = f"{modloader_name}-{modloader_version}"
+ if modloader_name == "neoforge" and mc_version == "1.20.1":
+ modloader_id = f"{modloader_name}-{mc_version}-{modloader_version}"
+
+ manifest_data = {
+ "author": modpack_author,
+ "files": curse_files,
+ "manifestType": "minecraftModpack",
+ "manifestVersion": 1,
+ "minecraft": {
+ "version": mc_version,
+ "modLoaders": [{"id": modloader_id, "primary": True}]
+ },
+ "name": modpack_name,
+ "overrides": "overrides",
+ "version": modpack_version
+ }
+
+ with open(os.path.join(modpack_path, "manifest.json"), "w", encoding="utf-8") as f:
+ json.dump(manifest_data, f, indent=4)
+
+ # FIXME 生成 modlist.html 文件
+ modlist_file = os.path.join(modpack_path, "modlist.html")
+ with open(modlist_file, "w", encoding="utf-8") as f:
+ f.write("\n")
+ # for file_info in curse_files:
+ # mod_page_url = file_info.get("url", "#")
+ # f.write(f'- {file_info["fileID"]}
\n')
+ f.write("
\n")
+
+ os.makedirs(os.path.join(modpack_path, "overrides"), exist_ok=True)
+ await download_files(session, non_curse_files)
+
+
+async def fetch_modpack_list():
+ print(lang.t("feedtheforge.main.getting_list"))
+ try:
+ async with aiohttp.ClientSession() as session:
+ async with session.get(API_LIST) as response:
+ if response.status == 200:
+ modpacks_data = await response.json()
+ with open(packlist_path, "w", encoding="utf-8") as f:
+ json.dump(modpacks_data, f, indent=4)
+ except OSError:
+ print(lang.t("feedtheforge.main.getting_error"))
+ utils.pause()
+
+ # 从本地文件读取数据
+ with open(packlist_path, "r", encoding="utf-8") as f:
+ modpacks_data = json.load(f)
+
+ global all_pack_ids
+ all_pack_ids = [str(pack_id) for pack_id in modpacks_data.get("packs", [])]
+ print(all_pack_ids)
+
+async def main():
+ if not os.path.exists(cache_dir):
+ os.makedirs(cache_dir)
+
+ # 本地化中这里的字中间要有空格,不加空格VSCode终端正常,在cmd中字会重叠
+ title = lang.t("feedtheforge.start.title")
+ options = [
+ Option(lang.t("feedtheforge.start.featured_modpack"),
+ description=lang.t("feedtheforge.start.featured_modpack_desc")),
+ Option(lang.t("feedtheforge.start.search_modpack"),
+ description=lang.t("feedtheforge.start.search_modpack_desc")),
+ Option(lang.t("feedtheforge.start.enter_id"),
+ description=lang.t("feedtheforge.start.enter_id_desc")),
+ Option(lang.t("feedtheforge.start.clean_temp"),
+ description=lang.t("feedtheforge.start.clean_temp_desc")),
+ Option(lang.t("feedtheforge.start.exit"),
+ description=lang.t("feedtheforge.start.exit_desc"))
+ ]
+
+ option, index = pick(options, title, indicator="=>")
+
+ # 根据选择执行相应的操作
+ if index == 0:
+ await download_featured_modpack()
+ elif index == 1:
+ await search_modpack()
+ elif index == 2:
+ await fetch_modpack_list()
+ modpack_id = input(lang.t("feedtheforge.main.enter_id"))
+ if modpack_id not in all_pack_ids:
+ print(lang.t("feedtheforge.main.invalid_pack_id"))
+ return
+ await download_modpack(modpack_id)
+ elif index == 3:
+ utils.clean_temp()
+ utils.pause()
diff --git a/main.spec b/main.spec
index 3df87a9..646dfaf 100644
--- a/main.spec
+++ b/main.spec
@@ -1,39 +1,39 @@
-# -*- mode: python ; coding: utf-8 -*-
-
-
-a = Analysis(
- ['__main__.py'],
- pathex=[],
- binaries=[],
- datas=[],
- hiddenimports=['aiohttp','pick'],
- hookspath=[],
- hooksconfig={},
- runtime_hooks=[],
- excludes=[],
- noarchive=False,
- optimize=0,
-)
-pyz = PYZ(a.pure)
-
-exe = EXE(
- pyz,
- a.scripts,
- a.binaries,
- a.datas,
- [],
- name='FeedTheForge',
- debug=False,
- bootloader_ignore_signals=False,
- strip=False,
- upx=True,
- upx_exclude=[],
- runtime_tmpdir=None,
- console=True,
- disable_windowed_traceback=False,
- argv_emulation=False,
- target_arch=None,
- codesign_identity=None,
- entitlements_file=None,
- icon=['PyBuild/icon.ico'],
+# -*- mode: python ; coding: utf-8 -*-
+
+
+a = Analysis(
+ ['__main__.py'],
+ pathex=[],
+ binaries=[],
+ datas=[],
+ hiddenimports=['aiohttp','pick'],
+ hookspath=[],
+ hooksconfig={},
+ runtime_hooks=[],
+ excludes=[],
+ noarchive=False,
+ optimize=0,
+)
+pyz = PYZ(a.pure)
+
+exe = EXE(
+ pyz,
+ a.scripts,
+ a.binaries,
+ a.datas,
+ [],
+ name='FeedTheForge',
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=True,
+ upx_exclude=[],
+ runtime_tmpdir=None,
+ console=True,
+ disable_windowed_traceback=False,
+ argv_emulation=False,
+ target_arch=None,
+ codesign_identity=None,
+ entitlements_file=None,
+ icon=['PyBuild/icon.ico'],
)
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 92a116b..fecb45d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,2 @@
-aiohttp
+aiohttp
pick
\ No newline at end of file