Skip to content

Commit 62c6888

Browse files
committed
v1.9.2: 优化文档和Docker部署配置
- 更新 Docker 部署文档,简化环境变量配置 - 修改 MT_WEB_HOST 默认值为 0.0.0.0 - 补充完整的 API Keys 环境变量列表 - 优化 README 和 INSTALLATION.md 文档结构 - 添加 Web UI 访问地址说明 - 删除 DOCKER_BUILD.md 和 CHANGELOG.md
1 parent 26bd5b7 commit 62c6888

13 files changed

Lines changed: 687 additions & 478 deletions

File tree

.github/workflows/docker-build-push.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ on:
2121
- 'false'
2222

2323
env:
24-
# Docker Hub 用户名(需要在 GitHub Secrets 中设置 DOCKERHUB_USERNAME
25-
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
26-
# 镜像名称(可以修改为你的 Docker Hub 仓库名)
24+
# Docker Hub 用户名(硬编码
25+
DOCKERHUB_USERNAME: hgmzhn
26+
# 镜像名称
2727
IMAGE_NAME: manga-translator
2828

2929
jobs:
@@ -48,7 +48,7 @@ jobs:
4848
if: github.event_name != 'pull_request'
4949
uses: docker/login-action@v3
5050
with:
51-
username: ${{ secrets.DOCKERHUB_USERNAME }}
51+
username: hgmzhn
5252
password: ${{ secrets.DOCKERHUB_TOKEN }}
5353

5454
- name: Extract metadata

README.md

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -98,48 +98,51 @@
9898
- 解压压缩包到任意目录
9999
- 双击 `app.exe`
100100

101-
#### 方式三:Docker部署(🐳 服务器推荐
101+
#### 方式三:Docker 部署(实验性
102102

103-
适合需要在服务器上部署或容器化运行的用户。
103+
**镜像地址**
104+
- CPU 版本:`hgmzhn/manga-translator:latest-cpu`
105+
- GPU 版本:`hgmzhn/manga-translator:latest-gpu`
104106

105-
**快速启动**
107+
**访问地址**(默认端口 8000):
108+
- 🌐 用户界面:`http://localhost:8000`
109+
- 🔧 管理界面:`http://localhost:8000/admin.html`
106110

107-
```bash
108-
# CPU版本
109-
docker run -d \
110-
--name manga-translator \
111-
-p 8000:8000 \
112-
-e MT_WEB_HOST=0.0.0.0 \
113-
your-registry/manga-translator:latest
114-
115-
# GPU版本
116-
docker run -d \
117-
--name manga-translator-gpu \
118-
--gpus all \
119-
-p 8000:8000 \
120-
-e MT_WEB_HOST=0.0.0.0 \
121-
-e MT_USE_GPU=true \
122-
your-registry/manga-translator:latest-gpu
123-
```
111+
> 📖 **详细安装教程**[Docker 部署文档](doc/INSTALLATION.md#安装方式四docker部署)
112+
> 📖 **使用教程**[命令行使用指南](doc/CLI_USAGE.md)
124113
125-
**访问服务**
126-
- 用户界面:`http://localhost:8000/`
127-
- 管理后台:`http://localhost:8000/admin`
128-
- API文档:`http://localhost:8000/docs`
114+
#### 方式四:从源码运行(开发者)
129115

130-
**详细配置**:查看 [Docker部署文档](doc/INSTALLATION.md#安装方式四docker部署)
131-
132-
#### 方式三:手动部署(开发者)
116+
适合开发者或想要自定义的用户。
133117

134118
1. **安装 Python 3.12**[下载](https://www.python.org/ftp/python/3.12.10/python-3.12.10-amd64.exe)
135-
2. **克隆仓库**`git clone https://github.com/hgmzhn/manga-translator-ui.git`
119+
2. **克隆仓库**
120+
```bash
121+
git clone https://github.com/hgmzhn/manga-translator-ui.git
122+
cd manga-translator-ui
123+
```
136124
3. **安装依赖**
137-
- **NVIDIA GPU**`py -3.12 -m pip install -r requirements_gpu.txt`
138-
- **AMD GPU**`py -3.12 -m pip install -r requirements_amd.txt` 并参考文件注释安装 AMD PyTorch
139-
- **CPU 版本**`py -3.12 -m pip install -r requirements_cpu.txt`
140-
4. **运行程序**`py -3.12 -m desktop_qt_ui.main`
141-
142-
**详细安装指南**[doc/INSTALLATION.md](doc/INSTALLATION.md)
125+
```bash
126+
# NVIDIA GPU
127+
pip install -r requirements_gpu.txt
128+
129+
# AMD GPU(仅 RX 7000/9000 系列)
130+
pip install -r requirements_amd.txt
131+
132+
# CPU 版本
133+
pip install -r requirements_cpu.txt
134+
```
135+
4. **运行程序**
136+
```bash
137+
# 桌面 UI
138+
python -m desktop_qt_ui.main
139+
140+
# Web UI(可选)
141+
python -m manga_translator web
142+
```
143+
144+
> 📖 **详细安装教程**[安装指南](doc/INSTALLATION.md)
145+
> 📖 **使用教程**[命令行使用指南](doc/CLI_USAGE.md)
143146
144147
---
145148

desktop_qt_ui/app_logic.py

Lines changed: 168 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ def __init__(self):
7373

7474
self.source_files: List[str] = [] # Holds both files and folders
7575
self.file_to_folder_map: Dict[str, Optional[str]] = {} # 记录文件来自哪个文件夹
76+
self.excluded_subfolders: set = set() # 记录被删除的子文件夹路径
77+
self.folder_tree_cache: Dict[str, dict] = {} # 缓存文件夹的完整树结构 {top_folder: tree_structure}
7678

7779
self.app_config = AppConfig()
7880
self.logger.info("主页面应用业务逻辑初始化完成")
@@ -750,11 +752,47 @@ def remove_file(self, file_path: str):
750752
# 如果是文件,清理 file_to_folder_map
751753
if norm_file_path in self.file_to_folder_map:
752754
del self.file_to_folder_map[norm_file_path]
755+
756+
# 如果是文件夹,清理排除列表中该文件夹下的所有子文件夹
757+
if os.path.isdir(norm_file_path):
758+
excluded_to_remove = set()
759+
for excluded_folder in self.excluded_subfolders:
760+
try:
761+
# 检查 excluded_folder 是否在被删除的文件夹内
762+
common = os.path.commonpath([norm_file_path, excluded_folder])
763+
if common == norm_file_path:
764+
excluded_to_remove.add(excluded_folder)
765+
except ValueError:
766+
continue
767+
self.excluded_subfolders -= excluded_to_remove
768+
753769
self.file_removed.emit(file_path)
754770
return
755771

756-
# 情况2:文件夹路径(通过单独添加文件自动分组的
772+
# 情况2:文件夹路径(可能是顶层文件夹或子文件夹
757773
if os.path.isdir(norm_file_path):
774+
# 检查是否是某个顶层文件夹的子文件夹
775+
parent_folder = None
776+
for folder in self.source_files:
777+
if os.path.isdir(folder):
778+
try:
779+
# 检查 norm_file_path 是否是 folder 的子文件夹
780+
common = os.path.commonpath([folder, norm_file_path])
781+
if common == os.path.normpath(folder) and norm_file_path != os.path.normpath(folder):
782+
parent_folder = folder
783+
break
784+
except ValueError:
785+
continue
786+
787+
if parent_folder:
788+
# 这是子文件夹,添加到排除列表
789+
self.excluded_subfolders.add(norm_file_path)
790+
# 发射删除信号让 FileListView 处理
791+
# FileListView 会自动更新树形结构和文件数量
792+
self.file_removed.emit(file_path)
793+
return
794+
795+
# 不是子文件夹,可能是通过单独添加文件自动分组的文件夹
758796
# 删除该文件夹下的所有文件
759797
files_to_remove = []
760798
for source_file in self.source_files:
@@ -833,11 +871,103 @@ def clear_file_list(self):
833871
# TODO: Add confirmation dialog
834872
self.source_files.clear()
835873
self.file_to_folder_map.clear() # 清空文件夹映射
874+
self.excluded_subfolders.clear() # 清空排除列表
836875
self.files_cleared.emit()
837876
self.logger.info("File list cleared by user.")
838877
# endregion
839878

840879
# region 核心任务逻辑
880+
def get_folder_tree_structure(self) -> dict:
881+
"""
882+
获取完整的文件夹树结构
883+
返回: {
884+
'files': [所有文件列表],
885+
'tree': {
886+
'folder_path': {
887+
'files': [该文件夹直接包含的文件],
888+
'subfolders': [子文件夹路径列表]
889+
}
890+
}
891+
}
892+
"""
893+
tree = {}
894+
all_files = []
895+
896+
# 处理每个顶层文件夹
897+
for source_path in self.source_files:
898+
if os.path.isdir(source_path):
899+
norm_folder = os.path.normpath(source_path)
900+
# 递归构建该文件夹的树结构
901+
folder_files = self._build_folder_tree(norm_folder, tree)
902+
all_files.extend(folder_files)
903+
elif os.path.isfile(source_path):
904+
# 单独添加的文件
905+
all_files.append(source_path)
906+
907+
return {
908+
'files': all_files,
909+
'tree': tree
910+
}
911+
912+
def _build_folder_tree(self, folder_path: str, tree: dict) -> List[str]:
913+
"""
914+
递归构建文件夹树结构
915+
返回该文件夹及其子文件夹中的所有文件列表
916+
"""
917+
# 检查是否被排除
918+
if folder_path in self.excluded_subfolders:
919+
return []
920+
921+
norm_folder = os.path.normpath(folder_path)
922+
923+
# 初始化该文件夹的树节点
924+
if norm_folder not in tree:
925+
tree[norm_folder] = {
926+
'files': [],
927+
'subfolders': []
928+
}
929+
930+
all_files = []
931+
image_extensions = {'.png', '.jpg', '.jpeg', '.bmp', '.webp'}
932+
933+
try:
934+
items = os.listdir(folder_path)
935+
subdirs = []
936+
files = []
937+
938+
for item in items:
939+
if item == 'manga_translator_work':
940+
continue
941+
942+
item_path = os.path.join(folder_path, item)
943+
norm_item_path = os.path.normpath(item_path)
944+
945+
if os.path.isdir(item_path):
946+
# 检查是否被排除
947+
if norm_item_path not in self.excluded_subfolders:
948+
subdirs.append(norm_item_path)
949+
tree[norm_folder]['subfolders'].append(norm_item_path)
950+
elif os.path.splitext(item)[1].lower() in image_extensions:
951+
files.append(norm_item_path)
952+
953+
# 排序
954+
subdirs.sort(key=self.file_service._natural_sort_key)
955+
files.sort(key=self.file_service._natural_sort_key)
956+
957+
# 添加该文件夹直接包含的文件
958+
tree[norm_folder]['files'] = files
959+
all_files.extend(files)
960+
961+
# 递归处理子文件夹
962+
for subdir in subdirs:
963+
subdir_files = self._build_folder_tree(subdir, tree)
964+
all_files.extend(subdir_files)
965+
966+
except Exception as e:
967+
self.logger.error(f"Error building tree for folder {folder_path}: {e}")
968+
969+
return all_files
970+
841971
def _resolve_input_files(self) -> List[str]:
842972
"""
843973
Expands folders in self.source_files into a list of image files.
@@ -860,13 +990,50 @@ def _resolve_input_files(self) -> List[str]:
860990
if self.file_service.validate_image_file(path):
861991
individual_files.append(path)
862992

993+
# 清理排除列表:移除不再属于任何 source_files 文件夹的排除项
994+
if self.excluded_subfolders:
995+
excluded_to_remove = set()
996+
for excluded_folder in self.excluded_subfolders:
997+
# 检查这个排除的文件夹是否还在某个 source_files 的文件夹内
998+
is_valid = False
999+
for folder in folders:
1000+
try:
1001+
common = os.path.commonpath([folder, excluded_folder])
1002+
if common == os.path.normpath(folder):
1003+
is_valid = True
1004+
break
1005+
except ValueError:
1006+
continue
1007+
if not is_valid:
1008+
excluded_to_remove.add(excluded_folder)
1009+
self.excluded_subfolders -= excluded_to_remove
1010+
8631011
# 对文件夹进行自然排序
8641012
folders.sort(key=self.file_service._natural_sort_key)
8651013

8661014
# 按文件夹分组处理
8671015
for folder in folders:
8681016
# 获取文件夹中的所有图片(递归查找所有子文件夹,已经使用自然排序)
8691017
folder_files = self.file_service.get_image_files_from_folder(folder, recursive=True)
1018+
1019+
# 过滤掉被排除的子文件夹中的文件
1020+
if self.excluded_subfolders:
1021+
filtered_files = []
1022+
for file_path in folder_files:
1023+
# 检查文件是否在被排除的子文件夹中
1024+
is_excluded = False
1025+
for excluded_folder in self.excluded_subfolders:
1026+
try:
1027+
common = os.path.commonpath([excluded_folder, file_path])
1028+
if common == excluded_folder:
1029+
is_excluded = True
1030+
break
1031+
except ValueError:
1032+
continue
1033+
if not is_excluded:
1034+
filtered_files.append(file_path)
1035+
folder_files = filtered_files
1036+
8701037
resolved_files.extend(folder_files)
8711038
# 记录这些文件来自这个文件夹
8721039
for file_path in folder_files:

0 commit comments

Comments
 (0)