MoviePilot 命名规范全解:从文件、类到 message/notification 语义域的代码约定指南
2026/9/23 15:26:46 网站建设 项目流程
  • 后端
  • AI Agent
  • MCP 服务
  • AI 技能

【免费下载链接】MoviePilot

NAS媒体库自动化管理工具

项目地址:https://gitcode.com/gh_mirrors/mo/MoviePilot
点击查看免费下载

导读

本文基于 MoviePilot 仓库docs/rules/07-naming-conventions.md整理而成,是该开源 NAS 媒体库自动化管理工具(涵盖下载、订阅、媒体服务器、消息通知、站点管理等模块)内部为所有新代码制定的统一命名约定。命名一致性是代码库无需注释即可传达意图的关键手段:阅读本文后,你将掌握 MoviePilot 中 Python 源文件、类、函数、变量、枚举、配置项、API 端点乃至消息/通知两个易混语义域的完整命名规则,理解每条约定背后的设计动机(如模块创建门禁、旧路径兼容策略),并能在贡献代码或二次开发时直接套用这些规范。文中所有示例与结论均可在当前仓库源码中找到对应实现。


一、总则:命名即沟通,规范即契约

MoviePilot 的命名约定遵循一条核心原则:一致的命名让代码库无需注释就能传达意图("Consistent naming is how the codebase communicates intent without comments")。所有新代码必须遵守以下约定,代码评审与架构测试以本规范为准绳。

命名规范共覆盖八大维度,每个维度都有明确的应用场景与反例对照:

  1. 文件与目录(Files)
  2. 类(Classes)
  3. 函数与方法(Functions and Methods)
  4. 变量与参数(Variables and Parameters)
  5. 枚举(Enums)
  6. 配置与设置(Configuration and Settings)
  7. API 端点与路由(API Endpoints and Routers)
  8. 消息/通知语义域边界(Message / Notification Domain Boundary)

其中「生产模块创建门禁」(Production Module Creation Gate)与「消息/通知语义域边界」是本规范中最具架构约束力的两条规则,分别对应docs/rules/07-naming-conventions.md中的两个强制决策流程。


二、文件与目录命名

2.1 基础文件命名规则

上下文约定示例
Python 源文件snake_case.pydownload.pyqbittorrent.pypackage.py
规范化能力包(canonical capability packages)中的新文件优先使用单个小写职责名词;在添加同级文件前,先尝试扩展现有 ownertorrent.pypackage.pyresources.py
多文件能力创建同名目录包,使用聚焦的单词子文件;禁止展平为<capability>_<role>.py平级文件transfer/workflow.pytransfer/execution.py
模块包目录snake_case/;包根不重复导出宿主实现qbittorrent/synologychat/transfer/
测试文件test_<domain>.pytest_download_chain.pytest_subscribe_endpoint.py
Alembic 迁移由 Alembic 自动生成,不要重命名20240101_add_column.py
Skill 目录<kebab-case>/transfer-failed-retry/moviepilot-cli/

这些约定在仓库中有大量真实对应物。例如测试目录tests/下可见 test_download_chain.py、test_subscribe_endpoint.py 等命名完全遵循test_<domain>.py;模块目录 app/modules/qbittorrent/ 为snake_case/包;而skills/目录下的 transfer-failed-retry、create-moviepilot-plugin 等则全部使用 kebab-case。

2.2 生产模块创建门禁(Production Module Creation Gate)

这是文件命名规则中最关键的一条强制决策流程。新增或拆分生产模块时必须按以下顺序决策,代码评审与架构测试以此为准:

  1. 优先扩展当前职责 owner。只因文件变长或需要一个私有辅助类,不得新建平级模块。
  2. 一个能力需要第二个生产文件时,必须在同一次变更中改为同名目录包,禁止继续增加<capability>_<role>.py平级文件。
  3. 包内子文件使用准确、可独立说明职责的单个小写名词,例如dependencies/profile.pydependencies/native.py;不得使用native_dependencies.pytransfer_execution.py这类重复能力名的文件。
  4. 包根__init__.py只允许包说明和确有外部契约证据的公开门面:不得复制实现,也不得为宿主代码重复导出子模块符号;宿主必须直接导入 owner 子模块。
  5. 旧路径兼容只能在确认真实插件消费者后,通过app/sdk/app/runtime/compat/做精确映射;不得为假设消费者保留旧源码、通配映射或双份导出。
  6. 新增文件前必须同步更新本规则对应的机器门禁;若现有门禁不能表达该命名约束,应在同一变更中补充 tests/test_architecture_dependencies.py。

这条门禁的落地依赖架构测试对命名约束的机器化检查。例如 tests/test_chain_base_boundary.py、tests/test_architecture_dependencies.py 等测试文件就是这类机器门禁的代表:它们以可执行测试的形式固化"哪些模块边界、哪些命名模式是允许的",防止后续变更悄悄引入违反规范的平级文件或重复导出。

反例与正例对照

反例(Wrong)正例(Correct)
transfer.py+transfer_execution.pytransfer/workflow.py+transfer/execution.py
dependencies.py+native_dependencies.pydependencies/profile.py+dependencies/native.py
包根为旧路径做宿主 re-export精确的 SDK/Compat 映射;宿主代码直接导入 owner 子模块

三、类命名(PascalCase)

上下文约定示例
Chain 类<Domain>ChainDownloadChainSearchChainSubscribeChain
Module 类<Backend>ModuleQbittorrentModuleEmbyModuleTelegramModule
Oper(数据访问)类<Model>OperSubscribeOperSystemConfigOperTransferHistoryOper
Helper 类<Domain>HelperTorrentHelperDirectoryHelperMessageHelper
Pydantic schema 模型PascalCase,名词聚焦MediaInfoTorrentInfoDownloadingTorrent
SQLAlchemy 模型类PascalCase单数名词SubscribeTransferHistorySystemConfig
枚举类PascalCaseMediaTypeEventTypeModuleType
Manager 类<Domain>ManagerModuleManagerPluginManagerEventManager
通用类PascalCaseMetaInfoContextChainBase

3.1 源码验证:三类核心类名的真实落点

  • Chain 类DownloadChain定义于 app/chain/download/facade.py,SearchChain定义于 app/chain/search/facade.py,SubscribeChain定义于 app/chain/subscribe/facade.py,三者均继承自ChainBase。这印证了规范中「<Domain>Chain」的命名模式,且 Chain 实现以目录包形式组织。
  • Module 类QbittorrentModule定义于 app/modules/qbittorrent/init.py,EmbyModule定义于 app/modules/emby/init.py,TelegramModule定义于 app/modules/telegram/module.py。注意 Module 类的命名使用完整后端名Qbittorrent而非QB),这正是反例表中class QBModule:被禁止的原因。
  • Oper 类SubscribeOper定义于 app/db/oper/subscribe.py,SystemConfigOper定义于 app/db/oper/systemconfig.py,TransferHistoryOper定义于 app/db/oper/transferhistory.py。它们均继承自DbOper,是 MoviePilot 数据访问层的标准形态。
  • Helper 类NotificationHelper定义于 app/application/notification.py,继承自ServiceBaseHelper[NotificationConf],对应规范中<Domain>Helper的约定。
  • SQLAlchemy 模型Message模型定义于 app/db/models/message.py,使用PascalCase单数名词。

从源码结构可以推断:Chain 类以<Domain>Chain统一后缀、Oper 类以<Model>Oper统一后缀、Manager 类以<Domain>Manager统一后缀,这套命名把"类名即职责定位"落实到极致——看到名字即可知道该类的分层归属(链、模块、数据访问、帮助器、管理)。


四、函数与方法命名(snake_case)

上下文约定示例
所有函数与方法snake_caseget_subscriberun_moduleon_config_changed
私有方法_snake_case(前导下划线)_submit_download_added_task_parse_result
事件处理方法on_<event_name>或描述性命名on_transfer_completehandle_config_changed
模块接口方法匹配_ModuleBase契约init_moduleinit_settingget_nameget_typeteststop
Oper 方法动词 + 名词getaddupdatedeletelist

4.1 设计要点解读

  • 事件处理方法使用on_<event_name>前缀是 MoviePilot 事件驱动架构的显性表达。事件处理函数必须一眼看出"它响应哪个事件"(如on_transfer_complete响应转移完成事件)。这也与docs/rules/04-design-patterns.md中事件驱动模式相呼应。
  • 模块接口方法必须严格匹配_ModuleBase契约,这是各模块(下载器、媒体服务器、消息渠道等)可被统一调度器加载的前提。方法名是契约的一部分,不得随意更改。
  • Oper 方法采用动词 + 名词的极简风格(getaddupdatedeletelist),与数据访问层的 CRUD 语义一一对应。

反例def GetSubscribe():(错误,应为def get_subscribe():);def handleConfigChanged():(错误,应为def on_config_changed():def handle_config_changed():)。


五、变量与参数命名

上下文约定示例
局部变量snake_casetorrent_infomedia_typedownload_dir
实例属性snake_caseself.download_historyself.config
常量(模块级)UPPER_SNAKE_CASEDEFAULT_EVENT_PRIORITYMIN_EVENT_CONSUMER_THREADS
私有变量_snake_case(前导下划线)_instance_lock
类型变量PascalCase搭配TypeVarT = TypeVar("T")

反例TORRENT_info = ...(错误,应为torrent_info = ...)。命名中不区分大小写混写,私有性统一由前导下划线表达。


六、枚举命名

上下文约定示例
枚举类名PascalCaseMediaTypeTorrentStatusEventType
枚举成员PascalCase(针对复杂枚举)MediaType.MOVIEEventType.TransferComplete
字符串枚举值匹配领域语言MediaType.MOVIE = '电影'TorrentStatus.TRANSFER = '可转移'
SystemConfigKey匹配配置键的字符串原值SystemConfigKey.RssUrls = "RssUrls"

6.1 源码验证:枚举值即领域语言

在 app/schemas/types.py 中可以找到规范的典型实现:

  • MessageType(app/schemas/types.py)的成员均为PascalCase,而字符串值使用中文领域语言:Download = "资源下载"Organize = "整理入库"Subscribe = "订阅"SiteMessage = "站点"Manual = "手动处理"Plugin = "插件"Agent = "智能体"Other = "其它"
  • NotificationChannel(app/schemas/types.py)的成员同样遵循PascalCase,字符串值为渠道领域名:Wechat = "微信"Feishu = "飞书"Telegram = "Telegram"Slack = "Slack"Discord = "Discord"DingTalk = "钉钉"SynologyChat = "SynologyChat"Web = "Web"等。
  • SystemConfigKey(app/schemas/types.py)定义于app/schemas/types.py中的"系统配置Key字典"区块,其成员名使用PascalCase,而值必须与持久化配置键的字符串完全一致,例如Downloaders = "Downloaders"MediaServers = "MediaServers"Notifications = "Notifications"Directories = "Directories"RssSites = "RssSites"AIAgentConfig = "AIAgentConfig"

这一约定的深层原因是:SystemConfigKey的枚举值是持久化到数据库的配置主键,一旦改名会破坏存量用户数据,因此成员名可以遵循代码命名规范,但值必须冻结为配置键原字符串。


七、配置与设置命名

上下文约定示例
Settings/ConfigModel字段UPPER_SNAKE_CASEAPI_TOKENLLM_MODELQB_HOST
SystemConfigKey枚举成员PascalCaseSystemConfigKey.RssUrlsSystemConfigKey.SubscribeFilter
环境变量名UPPER_SNAKE_CASEAI_AGENT_ENABLEDB_TYPE

关键实践:访问配置时必须通过SystemConfigKey枚举成员而非裸字符串。反例:configuration.get("RssUrls");正例:configuration.get(SystemConfigKey.RssUrls)

这条规则的价值在于:枚举将配置键集中管理,杜绝散落各处的魔法字符串,配合 IDE 的类型检查与自动补全,任何配置键的拼写错误都能在编译期暴露。同时它保证了配置键的持久化值与外部协议(DB、环境变量)冻结的一致性。


八、API 端点与路由命名

上下文约定示例
端点函数名snake_case动词前置get_subscribe_listadd_downloaddelete_history
URL 路径段kebab-casesnake_case,匹配既有模式/api/v1/subscribe/api/v1/transfer/history
Router tags匹配资源领域名"subscribe""download""media"

设计要点

  • 动词前置让 API 处理函数在路由注册处可读性最强,get_add_delete_update_前缀清晰表达 HTTP 语义与资源操作。
  • URL 路径段允许kebab-casesnake_case,但必须与项目既有模式保持一致——这是一个"向后兼容优先"的约定,避免新旧路由风格并存造成混乱。
  • Router tags 直接取资源领域名(subscribedownloadmedia),这使 OpenAPI 文档中相同领域的所有端点聚合在同一个 tag 下,便于 API 使用者检索。

九、Message / Notification 语义域边界(强制规则)

messagenotification在 MoviePilot 中是两个不同的语义域。新增或修改相关代码时必须按职责选名,不得混用。这是本规范中最易踩坑、也最具业务约束力的一条规则。

9.1 两个语义域的职责划分

语义域职责规范命名示例
notification通知渠道能力:渠道枚举、渠道配置、渠道发现、渠道管理、渠道能力描述NotificationChannelNotificationConfNotificationHelperNotificationChainNotificationActionChannelCapabilityManagerModuleType.Notificationchannel_manage
message各渠道发送或接收的消息:消息体、消息类型、消息链、消息历史、消息队列MessageMessageTypeIncomingMessageMessageChainMessageHistoryItemMessageOperpost_messagemessage_parser

9.2 判断规则

规则说明
渠道本身用notification渠道是能力提供方,如NotificationChannel枚举、NotificationConf渠道配置
消息内容与收发用message消息是被传输的内容,如发送体Message、接收体IncomingMessage、分类MessageType
渠道 × 消息的交叉概念按主导方判断按渠道控制消息开关的NotificationSwitch属渠道能力;消息历史清理MessageClearScope属消息
历史旧名不在源码保留NotificationMessageChannelNotificationTypeCommingMessage等旧名仅登记在app/runtime/compat/manifest.pySYMBOL_ALIASES,新代码一律使用规范名
持久化值与外部协议冻结枚举值、SystemConfigKey配置值、DB 表名、API 路径、外部平台字段(如 Jellyfin 的NotificationType)不随命名统一变更

9.3 源码验证:语义域的真实落点与旧名兼容

  • message 域MessageChain定义于 app/chain/message.py(继承ChainBase);MessageOper定义于 app/db/oper/message.py;Message(发送体)与IncomingMessage(接收体)均定义于 app/schemas/message.py(IncomingMessage在第 96 行,继承BaseModel)。

  • notification 域NotificationConf定义于 app/schemas/system.py;NotificationHelper定义于 app/application/notification.py,负责渠道能力的服务化封装。

  • 旧名兼容SYMBOL_ALIASES注册表位于 app/runtime/compat/manifest.py,其中_MESSAGE_NOTIFICATION_SYMBOL_ALIASES(app/runtime/compat/manifest.py)专门登记了消息/通知命名统一后的旧符号映射,例如:

    • MessageChannelNotificationChannelapp.schemas.types
    • NotificationTypeMessageTypeapp.schemas.types
    • NotificationMessageapp.schemas.message
    • CommingMessageIncomingMessageapp.schemas.message
    • NotificationHistoryItemMessageHistoryItem
    • NotificationClearScope/ClearBefore/ClearDataMessageClearScope/ClearBefore/ClearData
    • ChannelCapabilityChannelCapabilitiesChannelCapabilityManagerapp.schemas.notification

    该注册表由 app/runtime/compat/imports.py 等运行时兼容层消费,实现"旧名可导入但指向新符号"的精确映射。从源码结构可以推断:这套机制是仅为真实插件消费者保留的兼容通道,符合文档中"旧路径兼容只能在确认真实插件消费者后,通过app/sdk/app/runtime/compat/做精确映射"的约束。

9.4 反例对照

反例(Wrong,新代码中禁止)正例(Correct)
MessageChannel.TelegramNotificationChannel.Telegram
Notification(title=...)Message(title=...)

十、反模式速查(Anti-Patterns)

将上述全部约定浓缩为一张"错误 → 正确"对照表,供代码评审时快速比对:

错误(Wrong)正确(Correct)
class downloadchain:class DownloadChain:
class QBModule:class QbittorrentModule:
def GetSubscribe():def get_subscribe():
TORRENT_info = ...torrent_info = ...
def handleConfigChanged():def on_config_changed():def handle_config_changed():
configuration.get("RssUrls")configuration.get(SystemConfigKey.RssUrls)
class subscribe_oper:class SubscribeOper:
transfer.py+transfer_execution.pytransfer/workflow.py+transfer/execution.py
dependencies.py+native_dependencies.pydependencies/profile.py+dependencies/native.py
包根为旧路径做宿主 re-export精确的 SDK/Compat 映射;宿主代码直接导入 owner 子模块
MessageChannel.Telegram(新代码)NotificationChannel.Telegram
Notification(title=...)(新代码)Message(title=...)

十一、如何在贡献中落实这些规范

  1. 命名先行:在写第一行代码前,先判断新增文件属于哪个能力域,按「生产模块创建门禁」的决策顺序确定是扩展现有 owner 还是新建同名目录包。
  2. 用测试固化约束:若新增的命名约束无法被现有机器门禁表达,应在同一变更中补充 tests/test_architecture_dependencies.py 之类的架构测试,让规范可被 CI 自动校验。
  3. 严格遵守语义域:涉及消息/通知的代码,先判断职责是"渠道能力"(notification)还是"消息收发内容"(message),再决定命名域;绝不把旧名引入新代码。
  4. 尊重冻结契约:枚举值、SystemConfigKey配置值、DB 表名、API 路径等持久化或对外协议层面的命名不随代码重构变更;旧符号兼容一律走app/runtime/compat/manifest.pySYMBOL_ALIASES精确映射。

以上规范共同构成了 MoviePilot 代码库的"命名宪法"——它不仅是风格的统一,更是模块边界、数据访问分层、事件驱动架构与消息/通知语义域在命名层面上的制度化表达。遵循这些约定,任何新代码都能被团队与自动化门禁准确理解与校验。


本文依据docs/rules/07-naming-conventions.md(Last Updated: 2026-08-29)整理,并结合仓库源码与测试验证。规范细节以文档原文与仓库实际代码为准。

  • 后端
  • AI Agent
  • MCP 服务
  • AI 技能

【免费下载链接】MoviePilot

NAS媒体库自动化管理工具

项目地址:https://gitcode.com/gh_mirrors/mo/MoviePilot
点击查看免费下载

相关推荐

上一篇:ChampR:终极英雄联盟助手,一键生成推荐出装与符文
下一篇:2025 终极指南:Pixyll 打造极简响应式 Jekyll 博客

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询