- Updated AGENTS.md to reflect changes in CLI commands and module organization, including the addition of an enrichment step and new functional modules. - Introduced analysis.json, identities.json, inventory.csv, and plan.json to support enriched metadata and execution planning. - Added CODE_IMPROVEMENTS.md to document identified code issues and proposed solutions for future enhancements. - Updated README.md to include new enrichment features and configuration options. - Removed unused dependency on ffmpeg-python from pyproject.toml. These changes improve the overall functionality and maintainability of the Video Library Manager project.
8.1 KiB
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
解决方案
- 在
vlm/utils.py或现有模块中定义:
from datetime import datetime, timezone
def utc_now() -> datetime:
"""Return current UTC time (timezone-aware)."""
return datetime.now(timezone.utc)
- 全局替换所有
datetime.now()为utc_now()或datetime.now(timezone.utc) - 在
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
解决方案
parse_movie/parse_series增加可选参数extensions: list[str]- CLI parse 命令调用时传入
config.video_extensions - 默认值使用与 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) -> dictsave_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
解决方案
- 方案 A:analyze 命令同时接受
--inventory,从 inventory.csv 加载 metadata 并与 identities 按 path 合并 - 方案 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
解决方案
- 在
providers/base.py的EnrichmentProvider协议中显式声明last_request_count: int属性 - TMDBProvider 确保实现该属性
- 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 行:
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(推荐):引入
EnrichmentPayloaddataclass,两个 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