create-moviepilot-plugin について
create-moviepilot-plugin は、MoviePilot の実行環境向けにローカルソースからプラグインを作成・改修する開発スキルです。ホストのバージョンに応じた構成や実装方式、検証を案内し、インストールや実行環境の変更はユーザーの許可範囲内に限定します。
主な機能
- 01バージョン別プラグイン構成
- 02バックエンドと UI モードの案内
- 03ローカルソースとパッケージメタデータの手順
- 04ホスト API と拡張ポイントの確認
- 05範囲を限定した編集と検証の案内
活用シーン
- 01MoviePilot ホスト向けプラグインの作成
- 02既存ローカルプラグインの改修
- 03プラグイン UI 実装方式の選択
MCP 対応
README には MCP インターフェースや MCP 固有の機能について記載されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jxxghp/ moviepilot --skill create-moviepilot-pluginインストール後、Claude Code で/create-moviepilot-pluginを入力して読み込まれたか確認します。
詳細
MoviePilot プラグインを作成する
このスキルを使用して、ローカルのプラグインソースから開発し、実行中の MoviePilot インスタンスにインストールできる MoviePilot プラグインを作成または改訂します。
正確な情報源
- ホストのプラグイン契約:
app/plugins/__init__.py、特に_PluginBase。 - ホストのプラグイン検出、ローカルソースの同期、インストール、再読み込み:
app/runtime/extensions/plugin_manager.pyおよびapp/adapters/external/market.py。 - ホストのプラグインエンドポイント、API 認証、静的ファイル、リモート、サイドバーナビゲーション:
app/api/endpoints/plugin.py。 - ローカル開発についての注記:
docs/development-setup.md。 - まず、ソースやバージョン、および対象プラグインリポジトリから、実際のホストの世代を特定します。MoviePilot V3 では
plugins.v3/とpackage.v3.json、V2 ではplugins.v2/とpackage.v2.json、レガシー V1 ではplugins/とpackage.jsonを使用します。V3 ホスト用に V2 のパスを生成しないでください。 MoviePilot-Plugins内またはそこから作業する場合は、そのREADME.md、docs/Repository_Guide.md、および対象世代のガイドを読みます。V3 ではdocs/Plugin_Development.md、V2 ではdocs/V2_Plugin_Development.mdを参照してください。シナリオ固有の拡張機能については、該当するdocs/faq/*.mdを読みます。- V3 では、現行のホストとガイドに記載されている公開
app.sdk.*エントリーポイントを優先します。V2 の例にある古い内部インポートをコピーしないでください。テンプレートを使用する前に、インストール済みホストで拡張機能のシグネチャを確認してください。
タスクの範囲
プラグインの作成や修正の依頼は、関連するローカルソースの編集と検証を許可します。この許可は再利用できますが、それ自体ではホスト設定の変更、プラグインのインストールや再読み込み、再起動、公開を許可するものではありません。ユーザーが依頼した場合、またはその範囲をすでに承認している場合に実行し、ホストの確認要件に従ってください。稼働中の監視対象プラグインソースを編集する前に、自動再読み込みの設定を確認してください。こうした編集は実行環境に即時に影響する可能性があるため、その実行時の影響が許可された範囲内である必要があります。
コードツールのワークフロー
execute_command(action="run")でrgと対象を絞ったグロブまたはパスを使用し、プラグインクラス、拡張ポイント、テスト、パッケージエントリー、ディレクトリの内容を探します。- 編集する前に、関連する実装と隣接するサンプルを読みます。
read_fileで内容が途中までしか表示されない場合は、関連するセクションをすべて確認できるまで、start_lineとend_lineの範囲を小さくして読み進めます。- Python または Node.js の依存関係 API を使用する前に、requirements、パッケージマニフェスト、ロックファイル、ローカルのパッケージソース、
.pyi/.d.ts宣言から、インストール済みまたはロック済みの正確なバージョンを確認します。これらで不十分な場合は、公式ドキュメントのドメインを指定してsearch_webを使用し、browse_webpageで該当バージョンのドキュメントを読みます。記憶を頼りに API シグネチャを推測したり、異なるメジャーバージョンの例を混在させたりしないでください。プロジェクト全体を範囲を限定せずに走査するのではなく、関連するパッケージディレクトリ、.venv、またはnode_modulesを直接rgで検索します。 - 編集ツールは変更範囲に応じて選択します。複数ファイルにまたがる単一の論理的な変更、新規ファイルの追加、またはファイルの削除には
apply_patchを使用します。*** Begin Patch/*** End Patchで囲んだ単一のパッチを送信し、*** Add File:、*** Update File:、*** Delete File:セクションを含めます。すべてのコンテキスト行と削除行は、現在の内容に完全に一致させます。 - 1 つのファイルに対する局所的な変更には
edit_fileを使用します。old_textは既定で 1 か所だけに一致する必要があります。replace_allを有効にするのではなく、必要に応じて周囲のコンテキストを追加します。ただし、すべての一致箇所を意図的に変更する場合は除きます。 - 単独の新規ファイルを 1 つ作成する場合は
write_fileを使用します。既存ファイルを全面的に書き換えるにはoverwrite=trueが必要です。以前に読み取った内容を置き換える場合は、まずread_file(include_metadata=true)を呼び出し、そのsha256をexpected_sha256に渡します。 - 短時間の検証、Git、診断用コマンドには
execute_command(action="run")を使用します。対話型または長時間実行するコマンドに限りaction="start"を使用し、その後は返されたセッション ID で続行します。 - 構造化されたコマンド結果を読み取ります。コマンドが完了し、成功したことを示すのは
execution_outcome="succeeded"かつexit_code=0の場合のみです。失敗時はoutputを確認し、ログが途中までしか表示されない場合はoutput_fileを確認します。タイムアウトまたは不明な結果が返っても副作用が取り消されたとは限りません。書き込みをむやみに再試行しないでください。 - バックグラウンドコマンドを再開する際は、
output_until_seqとoutput_until_offsetの両方をsince_seqとsince_offsetとして使用します。部分チャンクのページングではオフセットを 0 から開始します。last_seqは読み取り済み位置を示すものではありません。新しい出力を一定の短い間隔で待ち、ログが完全になるまで残りのページを読み取ってoutput_complete=trueを確認します。output_lostの欠落があれば報告します。start/write/kill の後にoutput_errorが発生した場合は、アクションを再実行するのではなく、適切なバイト数の制限を指定してもう一度読み取ります。 - シェルのリダイレクトやインラインスクリプトを使ってソースを編集したり、ファイルツールの権限エラーを回避したりしないでください。
- プラグインが Vue フェデレーションを使用している場合は、
MoviePilot-Frontend/docs/module-federation-guide.md、MoviePilot-Frontend/docs/federation-troubleshooting.md、MoviePilot-Frontend/src/utils/federationLoader.ts、MoviePilot-Frontend/src/pages/plugin-app.vueも読みます。 - リポジトリの担当範囲:
MoviePilotはランタイムの読み込み、API 登録、イベント、サービス、データ、権限を担当します。MoviePilot-Frontendはプラグイン UI のレンダリング、フェデレーションの読み込み、サイドバーページを担当します。MoviePilot-Pluginsはプラグインのソース、アイコン、パッケージインデックス、リリースメタデータを担当します。
事前確認
- ユーザーのリクエストを把握します。プラグインの目的、トリガーモード、設定、出力 UI、スケジューラー、API、コマンド、ワークフローアクション、エージェントツールのいずれが必要かを確認します。
- UI コードを書く前に、UI モード選択ゲートを実行します。
- ユーザーが JSON config/Vuetify JSON または Vue federation をすでに明示的に選択している場合は、その選択に従います。
- プラグインに UI があり、ユーザーがモードを選択していない場合は、以下の 2 つのモードから選ぶようユーザーに尋ね、回答を待ってから UI ファイルやスキーマを実装します。
- 片方が簡単そうだからという理由で、どちらかのモードを暗黙的に選んではいけません。
- 新しいプラグインを作成する前に、既存のプラグインを調べます。
- ローカルランタイムの例:
app/plugins/<plugin>/__init__.py - マーケット/ローカルのソース候補:実行中のインスタンスが利用可能な場合は、
operation_id=plugin.marketを指定してmoviepilot_apiを呼び出します。 - インストール済みプラグインの候補:
operation_id=plugin.installedを使用します。概要には、ローカルのプラグインリポジトリまたはプラグインマーケットのメタデータからソースを特定できる場合、repo_urlが含まれます。 - Vue federation の例には、該当する世代の現行プラグインと
MoviePilot-Frontend/examples/plugin-component/を使用します。
- ローカルランタイムの例:
- 対象のソースパスを決定します。
- 可能であれば、
operation_id=config.system.getを指定してPLUGIN_LOCAL_REPO_PATHSを照会します。 - ローカルのプラグインリポジトリが 1 つだけ設定されている場合は、そのパスを優先します。
- 複数設定されている場合は、ユーザーが指定したリポジトリを選びます。指定がなければ、どのリポジトリを使用するか尋ねます。
- 設定されていない場合は、適切なローカルディレクトリにソースを準備します。ローカルインストール/設定が許可されている場合は、
setting_key="PLUGIN_LOCAL_REPO_PATHS"、選択したソースパスをvalue、operation="replace"としてoperation_id=config.system.updateを指定し、登録します。local-pluginsのような相対パスは MoviePilot のルートを基準に解決されます。 選択したソースの下にプラグインを書き込みます。ユーザーがランタイムのみの実験を明示的に求めている場合を除き、app/plugins/に直接書き込んではいけません。
- 可能であれば、
- プラグイン ID を選びます。
- クラス名がプラグイン ID になります。例:
MyNotifier - ディレクトリ名はクラス名を小文字にしたものです。例:
mynotifier - ユーザーがそのプラグインの変更を明示的に求めている場合を除き、インストール済みまたはマーケット上のプラグインとの ID の衝突を避けます。
- プラグインがクローンに対応する可能性がある場合、データ/設定の名前空間に元のプラグイン ID をハードコードしてはいけません。
self.__class__.__name__を使用します。
- クラス名がプラグイン ID になります。例:
UI モード選択ゲート
MoviePilot プラグイン UI には、実装モードが 2 つだけあります。リクエストに設定、詳細ページ、ダッシュボード、サイドバーページ、その他のプラグイン UI が含まれていて、モードが明示されていない場合は、必ずユーザーに選択してもらいます。
次のような簡潔な質問をします。
这个插件 UI 用哪种方式实现?
1. JSON 配置:后端返回 Vuetify JSON,适合普通配置表单、简单详情页和轻量仪表板。
2. 联邦 UI:独立 Vue 远程组件,适合复杂交互、自定义布局、侧栏全页或多页面。
選択ルール:
- JSON config/Vuetify JSON:JSON コンポーネントスキーマを使って
get_form()、get_page()、get_dashboard()を実装します。フロントエンドのビルドやdist/assets/remoteEntry.jsは不要です。 - Federation UI/Vue remote component:
get_render_mode()を実装し、Vite federation を通じて Vue コンポーネントを公開し、フロントエンドのアセットをプラグインディレクトリにビルドします。サイドバーページが求められている場合に限り、get_sidebar_nav()を使用します。 - プラグインにユーザー向け UI が本当にない場合は、UI モードが不要であることを伝え、リクエストに必要なバックエンド拡張ポイントのみを実装します。
- 回答を待つ間にバックエンドのみの作業を進めてもよいのは、どちらの UI モードも制約したり、選択できなくしたりしない場合に限ります。
ローカルソースの配置
検証済みの V3 ホストでは、新しいローカルプラグインに次の配置を使用してください。V2 では、代わりに
package.v2.json と plugins.v2/ を使用し、その依存関係の契約に従ってください。
<local-plugin-repo>/
├── package.v3.json
└── plugins.v3/
└── <plugin_id_lower>/
├── __init__.py
├── pyproject.toml # only when extra runtime dependencies are necessary
└── ... # helper modules, schemas, static assets
Vue フェデレーションプラグインの場合、実行時に必要なのは、プラグインディレクトリ内のビルド済みリモートアセットです。
plugins.v3/<plugin_id_lower>/
├── __init__.py
├── dist/
│ └── assets/
│ ├── remoteEntry.js
│ └── ... # JS/CSS/assets referenced by remoteEntry
├── package.json # optional frontend build project metadata
├── vite.config.js # optional frontend build config
└── src/ # optional source, not required at runtime
実行時にフロントエンドのソースファイルに依存しないでください。保守性のためにソースをプラグインリポジトリに保持する場合も、remoteEntry.js に必要な dist/assets
ファイルをビルドして同梱してください。
ユーザーが明示的に必要としている場合に限り、レガシーの配置を使用してください。
<local-plugin-repo>/
├── package.json
└── plugins/
└── <plugin_id_lower>/
└── __init__.py
V2 で動作させるレガシーな package.json のエントリーには、"v2": true を含めてください。
現在の V3 向けの作業では、package.v3.json と plugins.v3/ を使用してください。
パッケージのメタデータ
プラグイン ID のパッケージエントリーを追加または更新してください。パッケージのバージョンとクラスの plugin_version を一致させてください。
{
"MyNotifier": {
"name": "通知示例",
"description": "根据用户配置发送示例通知。",
"labels": "消息通知",
"version": "1.0.0",
"icon": "mynotifier.png",
"author": "local",
"level": 1,
"system_version": ">=3.0.0",
"history": {
"v1.0.0": "初始版本"
}
}
}
ルール:
- パッケージオブジェクトのキーは、プラグインクラス名と一致する必要があります。
versionはplugin_versionと一致する必要があります。name、description、icon、author、labels、levelは、該当する場合、プラグインクラスの属性(plugin_name、plugin_desc、plugin_icon、plugin_author、plugin_label、auth_level)と一致させてください。historyには、公開済みの各バージョンにおけるユーザーが読める変更内容を記録してください。- 特定の MoviePilot バージョンで導入されたホスト機能(新しいバックエンド API、ヘルパー、イベント、Vue フェデレーションの動作、サイドバーのナビゲーション、ダッシュボードの動作、エージェントツールなど)にプラグインが依存する場合は、
system_versionを使用してください。 - GitHub Release アーカイブでプラグインを意図的に配布する場合に限り、
"release": trueを使用してください。 - 新しいプラグインのエントリーは通常、パッケージインデックスの末尾に追加し、マーケットプレイスで新しい項目として表示されるようにしてください。
system_versionには、使用する API を実際に提供する最小バージョンを設定してください。この例は、すべてのホスト機能が 3.0.0 に存在することを示すものではありません。- 必要がない限り、依存関係を追加しないでください。ホスト世代に対応するマニフェストの契約(現在の V3 では
pyproject.toml)を使用してください。依存関係の変更にはホストのインストールフローが必要です。ホットリロードだけでは依存関係はインストールされません。 - プラグインの依存関係は、共有の MoviePilot Python 環境にインストールされます。ユーザーが互換性リスクを明示的に受け入れていない限り、MoviePilot がすでに提供しているパッケージのバージョンを固定したり、ダウングレードしたりしないでください。
実装のひな形
_PluginBase の抽象メソッドをすべて実装してください。新しい関数とメソッドにはすべて中国語の docstring が必要です。公開クラス、公開メソッド、公開関数は、レビュー時の必須要件です。
from typing import Any, Dict, List, Optional, Tuple
from app.plugins import _PluginBase
class MyNotifier(_PluginBase):
"""通知示例插件。"""
plugin_name = "通知示例"
plugin_desc = "根据用户配置发送示例通知。"
plugin_icon = "mynotifier.png"
plugin_version = "1.0.0"
plugin_label = "消息通知"
plugin_author = "local"
plugin_config_prefix = "mynotifier_"
plugin_order = 100
auth_level = 1
_enabled = False
_message = ""
def init_plugin(self, config: dict = None) -> None:
"""根据插件配置初始化运行状态。"""
self.stop_service()
self._enabled = False
self._message = ""
if not config:
return
self._enabled = bool(config.get("enabled"))
self._message = str(config.get("message") or "")
def get_state(self) -> bool:
"""获取插件启用状态。"""
return self._enabled
@staticmethod
def get_command() -> List[Dict[str, Any]]:
"""返回插件远程命令列表。"""
return []
def get_api(self) -> List[Dict[str, Any]]:
"""返回插件 API 列表。"""
return []
def get_form(self) -> Tuple[Optional[List[dict]], Dict[str, Any]]:
"""返回插件配置表单与默认配置。"""
return [
{
"component": "VForm",
"content": [
{
"component": "VSwitch",
"props": {
"model": "enabled",
"label": "启用插件"
}
},
{
"component": "VTextField",
"props": {
"model": "message",
"label": "通知内容"
}
}
]
}
], {
"enabled": False,
"message": ""
}
def get_page(self) -> Optional[List[dict]]:
"""返回插件详情页面。"""
if not self._enabled:
return None
return [
{
"component": "VAlert",
"props": {
"type": "info",
"text": self._message or "插件已启用"
}
}
]
def stop_service(self) -> None:
"""停止插件后台服务并释放资源。"""
return None
拡張ポイント
要求されているプラグインが実際に必要とする拡張ポイントのみを使用してください。
- 設定:
get_form()は Vuetify のフォームスキーマとデフォルトデータを返します。init_plugin()で設定を読み込み、update_config()で内部変更を永続化します。 - データ:
save_data()、get_data()、del_data()、get_data_path()を使用します。 - 通知:メッセージモジュールを直接呼び出すのではなく、
post_message()を使用します。 - API:
get_api()からルート定義を返します。authを省略した場合、認証方式はデフォルトでapikeyになります。Vue コンポーネントの API では通常、auth: "bear"を使用し、フロントエンドから渡されるapiプロパティを通じて呼び出します。 - コマンド:
get_command()からスラッシュコマンドの定義を返し、MoviePilot イベントを通じてディスパッチします。 - サービス:
get_service()からスケジューラーサービスを返し、必ずstop_service()でクリーンアップします。 - 単発または遅延ジョブ(「今すぐ1回実行」や「イベント発生からN秒後に実行」など):専用の
BackgroundSchedulerを作成するのではなく、app.sdk.scheduler.add_plugin_once_job(plugin_id, job_id, self.method, name, delay_seconds=..., func_kwargs=...)を呼び出します。この方法ではプラグインの定期サービスはリセットされず、同じ ID の保留中ジョブは置き換えられ、プラグインの再読み込み時またはアンインストール時にクリアされます。プラグインインスタンスのバインド済みメソッドを渡し、プラグインの停止後に実行されてはならない保留中ジョブは、stop_service()でremove_plugin_once_job()を呼び出してキャンセルします。 - ダッシュボード:ホームページウィジェットには
get_dashboard_meta()とget_dashboard()を使用します。 - ワークフローアクション:
get_actions()を使用します。アクション関数は最初の引数としてActionContentを受け取り、(success, action_content)を返します。 - エージェントツール:
get_agent_tools()を使用します。各ツールクラスはapp.agent.tools.base.MoviePilotToolを継承する必要があります。 - カスタム Vue UI:Vue が選択された UI モードの場合は、
get_render_mode()を実装します。("vue", "<compiled-assets-path>")を返し、ビルド済みのフロントエンドアセットをプラグインディレクトリに含めます。 - エージェントアシスタントのアバター。Vue プラグインで
get_agent_pets()を実装すると、Web アシスタントの画面上のキャラクターを置き換えられます。詳しくは、以下の「エージェントアシスタントのアバター」を参照してください。
Vue Federation UI
Pre-Flight UI の選択で Vue Federation が選ばれている場合は、Vue Federation を使用します。 Vue プラグインでは、バックエンドメソッド、ビルド済みファイル、Federation の公開設定を連携させる必要があります。
バックエンドの要件:
from typing import Any, Dict, List, Tuple
@staticmethod
def get_render_mode() -> Tuple[str, str]:
"""声明插件使用 Vue 联邦组件渲染。"""
return "vue", "dist/assets"
def get_form(self) -> Tuple[List[dict], Dict[str, Any]]:
"""Vue 模式下返回默认配置模型。"""
return [], self._current_config()
def get_page(self) -> List[dict]:
"""Vue 模式下详情页由远程 Page 组件渲染。"""
return []
プラグインにメインレイアウトのサイドバーページが必要な場合は、以下も実装します。
def get_sidebar_nav(self) -> List[Dict[str, Any]]:
"""声明插件在主界面左侧导航栏中的全页入口。"""
if not self.get_state():
return []
return [
{
"nav_key": "main",
"title": "我的插件",
"icon": "mdi-puzzle",
"section": "system",
"permission": "manage",
"order": 10,
}
]
サイドバーのルール:
- サイドバー項目が集約されるのは、有効なプラグインのうち、
get_render_mode()が"vue"を返すものに限られます。 sectionはstart、discovery、subscribe、organize、systemのいずれかである必要があります。無効な値はsystemにフォールバックします。permissionにはsubscribe、discovery、search、manage、adminを指定できます。無効な値は無視されます。nav_keyのデフォルト値はmainです。また、/、?、#、スペースを含めることはできません。- 複数のサイドバー項目を指定できます。各項目には、安定した
nav_keyが必要です。
プラグインがエージェントアシスタントのアバターを提供する場合は、get_agent_pets() も実装します。詳しくは、以下の「エージェントアシスタントのアバター」を参照してください。
フロントエンドの Federation 要件:
federation({
name: 'MyPlugin',
filename: 'remoteEntry.js',
exposes: {
'./Page': './src/components/Page.vue',
'./Config': './src/components/Config.vue',
'./Dashboard': './src/components/Dashboard.vue',
'./AppPage': './src/components/AppPage.vue',
'./AppPageSettings': './src/components/AppPageSettings.vue',
},
shared: {
vue: { requiredVersion: false, generate: false },
vuetify: { requiredVersion: false, generate: false, singleton: true },
'vuetify/styles': { requiredVersion: false, generate: false, singleton: true },
},
format: 'esm',
})
ビルド要件:
- Federation ではトップレベルの await を使用するため、Vite の
build.targetをesnextに設定します。 cssCodeSplit: trueを使用し、可能な限りスコープ付きまたはコンポーネントローカルのスタイルを使用します。- フロントエンドプロジェクトでドキュメントに記載されているコマンドを使ってビルドし、
remoteEntry.jsとそれが参照するすべての JS、CSS、アセットファイルをdist/assets以下に配置します。 - プラグインの Python
requirements.txtにフロントエンドのランタイム依存関係を追加しないでください。フロントエンドの依存関係はフロントエンドのビルドプロジェクトに含めます。
コンポーネントの契約:
Pageはプラグイン詳細ダイアログを表示し、action、switch、closeを送出することがあります。Configはプラグイン設定を表示し、initialConfigとapiを受け取り、save、close、switchを送出します。DashboardはconfigとallowRefreshを受け取ります。AppPageはメインレイアウトのサイドバーページを表示し、api、pluginId、navKeyを受け取ります。- サイドバーの
nav_key=mainでは、フロントエンドは./AppPage、続いて./Pageを読み込みます。 - その他の
nav_keyでは、フロントエンドは./AppPage{PascalCase(nav_key)}、続いて./AppPage、最後に./Pageを読み込みます。例:settings -> AppPageSettings、my_tool -> AppPageMyTool。 - 単一の
AppPageでnavKeyに応じて分岐することも、特定のエントリー向けに個別のAppPage{PascalCase}ファイルを公開することもできます。
エージェントアシスタントのアバター:
def get_agent_pets(self) -> List[Dict[str, Any]]:
"""声明插件提供的 Agent 助手形象。"""
return [
{
"key": "girl",
"name": "看板娘",
"mode": "stage",
"component": "AgentPet",
"preview": "girl-preview.png",
"avatar": "girl-avatar.png",
}
]
- 宣言は、
get_render_mode()が"vue"を返す有効なプラグインに限り、GET /api/v1/plugin/agent_petsによって集約されます。 - 必須フィールドは
key([a-z0-9_-]{1,32}、プラグイン内で一意)とnameです。任意フィールドはdescription、mode(デフォルトはrenderer、またはstage)、component(フェデレーションの公開名。デフォルトはAgentPet)、api_version(デフォルトは1)、preview、avatar、bubbles(stageの場合のみ。hostまたはself)、random_actions(rendererの場合のみ)です。 previewとavatarは、remoteEntry.jsが置かれているディレクトリ(get_render_mode()が返すディレクトリ)からの相対パス、またはhttp(s):///data:URL です。("vue", "dist/assets")の場合は、girl-preview.pngをdist/assets/remoteEntry.jsの隣に置き、"girl-preview.png"と宣言します。"assets/girl-preview.png"と記述すると、dist/assets/assets/girl-preview.pngとして解決されます。相対パスには..、\、:を含めることはできません。- 無効なフィールドが1つでもある項目は警告とともに除外され、重複する
keyがある場合は最初の項目が保持されます。1つのプラグインで複数のアバターを宣言でき、それぞれに独自のmodeとcomponentを指定できます。また、参照するすべてのコンポーネントを公開する必要があります。 - アバターはキャラクターを表示するだけです。Agent パネルを乗っ取ったり、メッセージを送信したりしてはいけません。また、
agent.open({ draft })は入力ボックスに入力内容を設定するだけです。 - ユーザーは Agent パネルのヘッダーにある「更换形象」ボタンからアバターを選択します。管理者のデフォルト設定は
AI_AGENT_PET(<plugin_id>:<key>)です。読み込みの失敗、8秒を超えるタイムアウト、実行時エラーが発生した場合は、組み込みのロボットにフォールバックします。 - コンポーネントの props、
moviepilot:agentホスト API、agent.*イベント、renderer の action/intent マッピング、16KB のpet.storage上限、stage の例については、MoviePilot-Frontend/docs/module-federation-guide.mdの「Agent 助手形象(AgentPet)」セクション(5.11)を参照してください。
Vue API 呼び出し:
- フロントエンド向けのプラグイン API は
auth: "bear"で定義してください。 - 注入された API オブジェクトを使って呼び出します。例:
props.api.get(\plugin/${props.pluginId}/history`)`。 - ブラウザー側の呼び出しのために、
settings.API_TOKENを Vue コンポーネントに渡さないでください。
ローカルインストールと再読み込み
このフェーズは、許可されたインストール/ランタイムの範囲内でのみ実行してください。
- 設定済みのローカルプラグインリポジトリにファイルを書き込んだ後、
moviepilot_apiをoperation_id=plugin.marketとクエリフィールドquery="<PluginID>"、force_refresh=trueで呼び出し、ローカルソースが表示されることを確認します。 operation_id=plugin.installを、パスパラメーターplugin_id="<PluginID>"とquery.force=trueを指定して実行し、インストールまたは再インストールします。インストール処理では、ソースがapp/plugins/<plugin_id_lower>/にコピーされます。PLUGIN_AUTO_RELOADまたは開発モードが有効な場合、インストール済みのローカルプラグインの Python ソースの変更は自動的に同期され、再読み込みされます。有効でない場合は、ランタイムファイルの編集後にパスパラメーターplugin_idを指定してoperation_id=plugin.reloadを呼び出します。- 依存関係マニフェストを変更した場合は、
force=Trueを指定して再インストールします。再読み込みだけでは新しい依存関係はインストールされません。
検証
- 変更したファイルを読み直し、クラス名、ディレクトリ名、パッケージ ID、パッケージバージョンに一貫性があることを確認します。
- すべての公開クラス、公開メソッド、公開関数に中国語の docstring があることを確認します。
- 新たに記述したすべての関数またはメソッドに、プライベートなヘルパーコードであっても中国語の docstring があることを確認します。
- Vue フェデレーションプラグインでは、
get_render_mode()が("vue", "dist/assets")または実際にビルドされたアセットパスを返し、dist/assets/remoteEntry.jsが存在することを確認します。 - サイドバープラグインでは、プラグインが有効であり、
get_state()がTrueを返し、get_sidebar_nav()が有効な項目を返すこと、また、メイン以外のすべてのnav_key値に対応するAppPageの expose が存在するか、汎用のAppPageがそれらを処理することを確認します。 - フロントエンド向けの API ルートで
auth: "bear"を使用し、ブラウザーコードが提供されたapiプロパティを通じてそれらを呼び出すことを確認します。 - 外部 HTTP 呼び出しは MoviePilot のユーティリティ経由にし、テストでは実際のネットワーク呼び出しを避けます。
- プラグインに複雑なロジックがある場合は、対象世代向けのリポジトリのブートストラップを使用して、pytest ネイティブのテストを追加または更新します。V2 のバックエンドや名前空間を V3 にコピーせず、現在のテスト設定を確認してください。
- 変更箇所に対して許可されている範囲で最小限の検証を実行します。このリポジトリでは、
docs/rules/03-commands.mdに従います。プラグインのみのリポジトリでは、各リポジトリで文書化されている検証コマンドに従います。 - プラグインリポジトリで Python を変更する場合は、可能であればホストの Python 環境を使い、変更したプラグインファイルに対して少なくとも構文コンパイルを実行します。
- Vue フェデレーションを変更する場合は、利用可能であればフロントエンドプロジェクトで文書化されている型チェックとビルドのコマンドを実行し、ビルドされたアセットがプラグインディレクトリにコピーされたことを確認します。
Vue フェデレーションのトラブルシューティング
GET /api/v1/plugin/remotes?token=moviepilotに、URL が/plugin/file/<plugin_id_lower>/<dist_path>/remoteEntry.jsで終わるプラグインが含まれていることを確認します。GET /api/v1/plugin/sidebar_navに、有効なnav_key、section、permissionを持つ Vue プラグインのサイドバー項目が含まれていることを確認します。- コンソールに
Module name 'vue' does not resolve to a valid URLと表示される場合は、フェデレーションのshared設定を確認し、requiredVersion: falseを使用します。 - コンソールにトップレベル await が利用できないと表示される場合は、
build.targetをesnextに設定します。 - 動的インポートに失敗する場合は、リモートファイルのリクエストステータス、計算された
remoteEntry.jsのパス、およびインストール先のランタイムプラグインディレクトリにビルド済みアセットが実際に存在するかを確認します。 - サイドバーページが空白の場合は、現在の
nav_keyに対する expose 名の解決とフォールバック(AppPage{PascalCase}->AppPage->Page)を確認します。
最終報告
以下を報告します。
- プラグイン ID、ソースパス、インストール済みの場合はランタイムパス。
- ホスト/プラグインの世代と、変更したパッケージファイル(
package.v3.json、package.v2.json、またはレガシーのpackage.json)。 - 使用した UI モード(
vuetifyJSON またはvueフェデレーション)。Vue プラグインの場合は、公開したコンポーネントとビルド済みアセットのパスも報告します。 - プラグインをインストールまたは再読み込みしたかどうか。
- 実行した検証コマンド、または検証を実行しなかった理由。