diff --git a/.gitignore b/.gitignore index 1a87dae..18f7df1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ __pycache__/ *.pyc -/Download/ /.venv/ /.ruff_cache/ -*.db -*.json /.idea/ -/Temp/ -!.github/workflows/*.yaml +/Volume/ +!/.github/ diff --git a/source/TUI/app.py b/source/TUI/app.py index 314eb02..c1ae377 100644 --- a/source/TUI/app.py +++ b/source/TUI/app.py @@ -16,7 +16,7 @@ __all__ = ["XHSDownloader"] class XHSDownloader(App): - CSS_PATH = ROOT.joinpath("static/XHS-Downloader.tcss") + CSS_PATH = ROOT.parent.joinpath("static/XHS-Downloader.tcss") SETTINGS = Settings(ROOT) def __init__(self): diff --git a/source/module/manager.py b/source/module/manager.py index d4701f1..70b27ac 100644 --- a/source/module/manager.py +++ b/source/module/manager.py @@ -69,9 +69,10 @@ class Manager: _print: bool, ): self.root = root - self.temp = root.joinpath("./temp") + self.temp = root.joinpath("Temp") self.path = self.__check_path(path) self.folder = self.__check_folder(folder) + self.compatible() self.blank_headers = HEADERS | { "user-agent": user_agent or USERAGENT, } @@ -119,6 +120,7 @@ class Manager: self.live_download = self.check_bool(live_download, True) self.author_archive = self.check_bool(author_archive, False) self.write_mtime = self.check_bool(write_mtime, False) + self.create_folder() def __check_path(self, path: str) -> Path: if not path: @@ -128,15 +130,13 @@ class Manager: return r if (r := self.__check_root_again(r)) else self.root def __check_folder(self, folder: str) -> Path: - folder = self.path.joinpath(folder or "Download") - folder.mkdir(exist_ok=True) - self.temp.mkdir(exist_ok=True) - return folder + # TODO: 待实现 + return self.path.joinpath(folder or "Download") @staticmethod def __check_root_again(root: Path) -> bool | Path: - if root.resolve().parent.is_dir(): - root.mkdir() + if root.parent.is_dir(): + root.mkdir(exist_ok=True) return root return False @@ -269,3 +269,15 @@ class Manager: cookie_string = sub(r";\s*$", "", cookie_string) # 删除末尾的分号和空格 cookie_string = sub(r";\s*;", ";", cookie_string) # 删除中间多余分号后的空格 return cookie_string.strip("; ") + + def create_folder( + self, + ): + self.folder.mkdir(exist_ok=True) + self.temp.mkdir(exist_ok=True) + + def compatible(self,): + if self.path == self.root and ( + old := self.path.parent.joinpath(self.folder.name) + ).exists() and not self.folder.exists(): + move(old, self.folder) diff --git a/source/module/recorder.py b/source/module/recorder.py index 8148920..87a3316 100644 --- a/source/module/recorder.py +++ b/source/module/recorder.py @@ -1,7 +1,7 @@ from asyncio import CancelledError from contextlib import suppress from typing import TYPE_CHECKING - +from shutil import move from aiosqlite import connect if TYPE_CHECKING: @@ -12,7 +12,9 @@ __all__ = ["IDRecorder", "DataRecorder", "MapRecorder"] class IDRecorder: def __init__(self, manager: "Manager"): - self.file = manager.root.joinpath("ExploreID.db") + self.name = "ExploreID.db" + self.file = manager.root.joinpath(self.name) + self.changed = False self.switch = manager.download_record self.database = None self.cursor = None @@ -56,6 +58,7 @@ class IDRecorder: return [i[0] for i in await self.cursor.fetchmany()] async def __aenter__(self): + self.compatible() await self._connect_database() return self @@ -64,6 +67,16 @@ class IDRecorder: await self.cursor.close() await self.database.close() + def compatible( + self, + ): + if ( + not self.changed + and (old := self.file.parent.parent.joinpath(self.name)).exists() + and not self.file.exists() + ): + move(old, self.file) + class DataRecorder(IDRecorder): DATA_TABLE = ( @@ -89,7 +102,9 @@ class DataRecorder(IDRecorder): def __init__(self, manager: "Manager"): super().__init__(manager) - self.file = manager.folder.joinpath("ExploreData.db") + self.name = "ExploreData.db" + self.file = manager.folder.joinpath(self.name) + self.changed = True self.switch = manager.record_data async def _connect_database(self): @@ -131,7 +146,8 @@ class DataRecorder(IDRecorder): class MapRecorder(IDRecorder): def __init__(self, manager: "Manager"): super().__init__(manager) - self.file = manager.root.joinpath("MappingData.db") + self.name = "MappingData.db" + self.file = manager.root.joinpath(self.name) self.switch = manager.author_archive async def _connect_database(self): diff --git a/source/module/settings.py b/source/module/settings.py index 2a30d09..b90053d 100644 --- a/source/module/settings.py +++ b/source/module/settings.py @@ -1,7 +1,7 @@ from json import dump, load from pathlib import Path from platform import system - +from shutil import move from .static import ROOT, USERAGENT __all__ = ["Settings"] @@ -35,33 +35,69 @@ class Settings: encode = "UTF-8-SIG" if system() == "Windows" else "UTF-8" def __init__(self, root: Path = ROOT): + """初始化Settings类 + + Args: + root: 设置文件的根目录路径,默认为ROOT + """ # 设置文件路径 - self.file = root.joinpath("./settings.json") + self.name = "settings.json" + self.root = root + self.path = root.joinpath(self.name) def run(self): + """运行设置管理 + + Returns: + dict: 设置参数字典 + """ + self.migration_file() # 如果文件存在则读取,否则创建新文件 - return self.read() if self.file.is_file() else self.create() + return self.read() if self.path.is_file() else self.create() def read(self) -> dict: + """读取设置文件 + + Returns: + dict: 读取的设置参数字典 + """ # 读取设置文件 - with self.file.open("r", encoding=self.encode) as f: + with self.path.open("r", encoding=self.encode) as f: return self.compatible(load(f)) def create(self) -> dict: + """创建新的设置文件 + + Returns: + dict: 默认设置参数字典 + """ # 创建新的设置文件 - with self.file.open("w", encoding=self.encode) as f: + with self.path.open("w", encoding=self.encode) as f: dump(self.default, f, indent=4, ensure_ascii=False) return self.default def update(self, data: dict): + """更新设置文件内容 + + Args: + data: 要更新的设置参数字典 + """ # 更新设置文件 - with self.file.open("w", encoding=self.encode) as f: + with self.path.open("w", encoding=self.encode) as f: dump(data, f, indent=4, ensure_ascii=False) def compatible( self, data: dict, ) -> dict: + """兼容性检查,确保所有默认配置都存在 + + Args: + data: 要检查的设置参数字典 + + Returns: + dict: 经过兼容性检查后的设置参数字典 + """ # 兼容性检查: 确保所有默认配置都存在 update = False for i, j in self.default.items(): @@ -71,3 +107,13 @@ class Settings: if update: self.update(data) return data + + def migration_file(self): + """迁移设置文件 + + 如果旧的设置文件存在且新路径下不存在,则移动旧文件到新路径 + """ + if ( + old := self.root.parent.joinpath(self.name) + ).exists() and not self.path.exists(): + move(old, self.path) diff --git a/source/module/static.py b/source/module/static.py index 36ebcc2..6164888 100644 --- a/source/module/static.py +++ b/source/module/static.py @@ -4,7 +4,8 @@ VERSION_MAJOR = 2 VERSION_MINOR = 6 VERSION_BETA = True __VERSION__ = f"{VERSION_MAJOR}.{VERSION_MINOR}.{'beta' if VERSION_BETA else 'stable'}" -ROOT = Path(__file__).resolve().parent.parent.parent +ROOT = Path(__file__).resolve().parent.parent.parent.joinpath("Volume") +ROOT.mkdir(exist_ok=True) PROJECT = f"XHS-Downloader V{VERSION_MAJOR}.{VERSION_MINOR} { 'Beta' if VERSION_BETA else 'Stable' }" diff --git a/static/Release_Notes.md b/static/Release_Notes.md index 63cb448..5ad24c8 100644 --- a/static/Release_Notes.md +++ b/static/Release_Notes.md @@ -4,12 +4,13 @@ 2. 修改服务器模式请求路径为 `/xhs/detail` 3. 修改服务器模式默认端口为 `5556` 4. 服务器模式新增 MCP 模式 -5. 优化配置文件参数容错机制 -6. 优化提取链接的正则表达式 -7. 修改服务器模式启动命令 -8. 支持更多作品链接格式 -9. 支持音乐图集作品下载 -10. 其他细节优化 +5. 配置与文件归集到单文件夹 +6. 优化配置文件参数容错机制 +7. 优化提取链接的正则表达式 +8. 修改服务器模式启动命令 +9. 支持更多作品链接格式 +10. 支持音乐图集作品下载 +11. 其他细节优化 *****