Files
dl-organizer/CODE_IMPROVEMENTS.md
T

314 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> [!NOTE]
> Status: Historical snapshot. Current refactor results and validated baseline are tracked in `CHANGELOG.md` (updated 2026-02-16).
# VLM 代码改进清单
本文档记录对 Video Library Manager (VLM) 项目的代码审查发现的问题及对应解决方案。排除 AI/OpenAPI 相关问题。
---
## 高优先级(确信度 ≥ 0.9
### 1. 时间戳未统一使用 UTC
**问题描述**
多处使用 `datetime.now()` 未指定 timezone,与项目约定「timestamps in UTC」不一致,可能导致:
- 序列化为 ISO 时缺少 `+00:00` 后缀
- 多环境部署时依赖本地时区,行为不一致
**涉及文件**
| 文件 | 行号 |
|------|------|
| `planner.py` | 51 |
| `executor.py` | 89, 119, 471 |
| `state.py` | 104, 139, 173 |
| `quarantine.py` | 61, 529 |
**确信度**: 0.95
**解决方案**
1.`vlm/utils.py` 或现有模块中定义:
```python
from datetime import datetime, timezone
def utc_now() -> datetime:
"""Return current UTC time (timezone-aware)."""
return datetime.now(timezone.utc)
```
2. 全局替换所有 `datetime.now()``utc_now()``datetime.now(timezone.utc)`
3.`load_plan``load_rollback_log` 等反序列化时,对 naive datetime 做 `replace(tzinfo=timezone.utc)` 以保持向后兼容
---
### 2. 未使用的依赖 ffmpeg-python
**问题描述**
`pyproject.toml` 声明 `ffmpeg-python>=0.2.0`,但代码中未 import。scanner 使用 `subprocess` 直接调用 ffprobe。
**确信度**: 0.95
**解决方案**
`pyproject.toml` 的 dependencies 中移除 `ffmpeg-python`。若未来改用 ffmpeg-python 库再添加。
---
### 3. Parser 中 video extensions 硬编码
**问题描述**
`parser.py` 第 100、169 行使用固定扩展名列表 `['.mp4', '.mkv', ...]`,与 `config.video_extensions` 不一致。
- 用户在 config 中新增扩展(如 `.ts`),scan 能发现,但 parse 去扩展名时不会匹配
-`Movie (2020).ts` 可能得到错误的 title 解析
**确信度**: 0.9
**解决方案**
1. `parse_movie` / `parse_series` 增加可选参数 `extensions: list[str]`
2. CLI parse 命令调用时传入 `config.video_extensions`
3. 默认值使用与 config 相同的列表以保持向后兼容
---
### 4. 抽出统一的 I/O 层
**问题描述**
数据读取和转换分散在各 CLI 命令中,同一份 identities JSON 在 analyze、plan 等处有重复且略有不同的转换逻辑。新增字段时需多处同步,易遗漏。
**确信度**: 0.9
**解决方案**
新增 `vlm/io.py`,集中:
- `load_inventory_csv(path) -> list[VideoFile]`
- `save_inventory_csv(files, path, library_root)`
- `load_identities_json(path) -> dict`
- `save_identities_json(data, path)`
- `identities_to_plan_input(data) -> list[(VideoFile, Identity | None)]`
- `identities_to_analysis_input(data) -> (list[MovieIdentity], list[SeriesIdentity], list[VideoFile])`
CLI 只调用这些函数,不再直接解析和构造 dataclass。
---
### 5. Analyze 阶段 VideoFile metadata 丢失
**问题描述**
`identities.json` 不含 `size_bytes``resolution``codec` 等,CLI 构造 `VideoFile` 时用 0 或 None,导致 `compare_quality()` 无法有效比较,duplicate 报告信息不足。
**确信度**: 0.9
**解决方案**
1. **方案 A**analyze 命令同时接受 `--inventory`,从 inventory.csv 加载 metadata 并与 identities 按 path 合并
2. **方案 B**parse 输出时在 identities 中附带 size/resolution/codec(从 inventory 合并),避免 analyze 再读 inventory
---
## 中优先级(确信度 0.8–0.89)
### 6. 拆分 CLI 为 commands 子模块
**问题描述**
`cli.py` 约 1700 行,混合参数定义、业务逻辑、I/O、输出展示,维护和单测困难。
**确信度**: 0.85
**解决方案**
```
src/vlm/
cli.py # main、参数、ctx 传递、调用 commands
commands/
__init__.py
scan.py # scan_cmd(ctx, ...)
parse.py # parse_cmd(ctx, ...)
enrich.py
analyze.py
plan.py
execute.py
report.py # 或按子命令拆分
quarantine.py
state.py
config_cmd.py
```
每个 `*_cmd` 接收 `ctx` 和参数,CLI 只做装饰与调用。单测可直接测 `*_cmd` 函数。
---
### 7. Provider last_request_count 非正式接口
**问题描述**
`enrichment.py` 使用 `getattr(provider, "last_request_count", 1)` 统计 API 调用,依赖实现细节。
**确信度**: 0.85
**解决方案**
1.`providers/base.py``EnrichmentProvider` 协议中显式声明 `last_request_count: int` 属性
2. TMDBProvider 确保实现该属性
3. enrichment 通过协议访问,去掉 `getattr`
---
### 8. 异常捕获过宽
**问题描述**
约 20+ 处 `except Exception`,容易吞掉逻辑错误,难以区分可恢复错误与编程错误。
**涉及**`cli.py``enrichment.py``executor.py``quarantine.py` 等。
**确信度**: 0.8
**解决方案**
- 针对预期异常(`FileNotFoundError``json.JSONDecodeError``ValueError`)分别处理
- 保留顶层 `except Exception` 作为兜底,记录完整 traceback 后 `sys.exit(1)`
- 避免在业务逻辑深处宽泛捕获
---
### 9. Enrichment 主循环过长
**问题描述**
`enrich_identities_data` 主循环约 80 行,混合迭代、缓存、API 调用、统计、payload 合并,可读性和可测性差。
**确信度**: 0.8
**解决方案**
拆分为:
- `_process_single_record(record, media_type, ...) -> None`
- `_fetch_from_providers(record, media_type, providers, ...) -> tuple[dict, int, list, str]`
- `_update_stats(stats, ...) -> None`
- 主循环只负责迭代与调用上述函数
---
## 较低优先级(确信度 0.7–0.79)
### 10. Duplicate 检测 file_map 使用 filename 作为 key
**问题描述**
`analysis.py` 第 87 行:
```python
file_map = {file.filename: file for file in files}
```
同 filename 不同路径会互相覆盖(如 `/a/Movie.mkv``/b/Movie.mkv`),导致 identity 映射到错误 VideoFile。
**确信度**: 0.75
**解决方案**
- 使用 `str(file.path)` 作为 key
- 确保 identity 与 VideoFile 的关联方式一致(如通过 path 或 (path, filename) 建立映射)
---
### 11. MovieIdentity / SeriesIdentity 字段重复
**问题描述**
两个 dataclass 有约 10 个共同 enrichment 字段,新增时需改两处,合并逻辑需分支处理。
**确信度**: 0.7
**解决方案**
- **方案 A**:抽取 `EnrichmentMixin` 基类,`MovieIdentity` / `SeriesIdentity` 继承
- **方案 B(推荐)**:引入 `EnrichmentPayload` dataclass,两个 Identity 通过 `enrichment: EnrichmentPayload` 组合,侵入较小
---
### 12. Config 体积膨胀
**问题描述**
`Config` 约 50 个字段,enrichment/TMDB 相关占多数,职责混杂。
**确信度**: 0.7
**解决方案**
拆分 `EnrichmentConfig``TMDBConfig` 等子配置,通过嵌套或组合放入主 Config。
---
### 13. review_status 与 needs_review 语义重叠
**问题描述**
`review_status`pending/approved/rejected)与 `needs_review`bool)含义重叠,易混淆。
**确信度**: 0.75
**解决方案**
- 在 docstring 或文档中明确定义:`needs_review = (review_status == 'pending') and ...`
- 或在模型中合并为单一状态枚举,避免两个字段语义交叉
---
## 低优先级(确信度 < 0.7
### 14. config_init 与 ctx 一致性
**问题描述**
`config init` 未使用 `@pass_context`,与同组其他命令风格不一致,但当前不依赖 ctx,非功能性 bug。
**确信度**: 0.5
**解决方案**
若其他 config 子命令均用 `@pass_context`,可统一为 `config_init` 也接收 ctx 以保持风格一致;否则可保持现状。
---
### 15. CLI 延迟 import
**问题描述**
各命令在函数体内才 `import`,错误在首次执行该命令时才暴露,依赖关系不直观。
**确信度**: 0.6
**解决方案**
可接受;若希望启动时即发现依赖问题,可改为模块级 import,但会增加启动开销。
---
## 执行建议
| 阶段 | 项目 | 说明 |
|------|------|------|
| 第一批 | 1, 2, 3 | 改动小、风险低、收益明确 |
| 第二批 | 4, 5 | 需要一定重构,与 I/O 设计相关 |
| 第三批 | 6, 7, 8, 9 | 结构性改进,建议分步完成 |
| 第四批 | 10–15 | 按需和档期安排 |
---
*文档生成日期:2025-02-10*