mcpField
C
create-moviepilot-plugin

create-moviepilot-plugin

github.com/jxxghp開発12.0k56 回インストール今日更新
リポジトリ

create-moviepilot-plugin について

create-moviepilot-plugin は、MoviePilot の実行環境向けにローカルソースからプラグインを作成・改修する開発スキルです。ホストのバージョンに応じた構成や実装方式、検証を案内し、インストールや実行環境の変更はユーザーの許可範囲内に限定します。

主な機能

  1. 01バージョン別プラグイン構成
  2. 02バックエンドと UI モードの案内
  3. 03ローカルソースとパッケージメタデータの手順
  4. 04ホスト API と拡張ポイントの確認
  5. 05範囲を限定した編集と検証の案内

活用シーン

  1. 01MoviePilot ホスト向けプラグインの作成
  2. 02既存ローカルプラグインの改修
  3. 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 はプラグインのソース、アイコン、パッケージインデックス、リリースメタデータを担当します。

事前確認

  1. ユーザーのリクエストを把握します。プラグインの目的、トリガーモード、設定、出力 UI、スケジューラー、API、コマンド、ワークフローアクション、エージェントツールのいずれが必要かを確認します。
  2. UI コードを書く前に、UI モード選択ゲートを実行します。
    • ユーザーが JSON config/Vuetify JSON または Vue federation をすでに明示的に選択している場合は、その選択に従います。
    • プラグインに UI があり、ユーザーがモードを選択していない場合は、以下の 2 つのモードから選ぶようユーザーに尋ね、回答を待ってから UI ファイルやスキーマを実装します。
    • 片方が簡単そうだからという理由で、どちらかのモードを暗黙的に選んではいけません。
  3. 新しいプラグインを作成する前に、既存のプラグインを調べます。
    • ローカルランタイムの例:app/plugins/<plugin>/__init__.py
    • マーケット/ローカルのソース候補:実行中のインスタンスが利用可能な場合は、operation_id=plugin.market を指定して moviepilot_api を呼び出します。
    • インストール済みプラグインの候補:operation_id=plugin.installed を使用します。概要には、ローカルのプラグインリポジトリまたはプラグインマーケットのメタデータからソースを特定できる場合、repo_url が含まれます。
    • Vue federation の例には、該当する世代の現行プラグインと MoviePilot-Frontend/examples/plugin-component/ を使用します。
  4. 対象のソースパスを決定します。
    • 可能であれば、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/ に直接書き込んではいけません。
  5. プラグイン ID を選びます。
    • クラス名がプラグイン ID になります。例:MyNotifier
    • ディレクトリ名はクラス名を小文字にしたものです。例:mynotifier
    • ユーザーがそのプラグインの変更を明示的に求めている場合を除き、インストール済みまたはマーケット上のプラグインとの ID の衝突を避けます。
    • プラグインがクローンに対応する可能性がある場合、データ/設定の名前空間に元のプラグイン ID をハードコードしてはいけません。self.__class__.__name__ を使用します。

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 コンポーネントに渡さないでください。

ローカルインストールと再読み込み

このフェーズは、許可されたインストール/ランタイムの範囲内でのみ実行してください。

  1. 設定済みのローカルプラグインリポジトリにファイルを書き込んだ後、moviepilot_api を operation_id=plugin.market とクエリフィールド query="<PluginID>"、force_refresh=true で呼び出し、ローカルソースが表示されることを確認します。
  2. operation_id=plugin.install を、パスパラメーター plugin_id="<PluginID>" と query.force=true を指定して実行し、インストールまたは再インストールします。インストール処理では、ソースが app/plugins/<plugin_id_lower>/ にコピーされます。
  3. PLUGIN_AUTO_RELOAD または開発モードが有効な場合、インストール済みのローカルプラグインの Python ソースの変更は自動的に同期され、再読み込みされます。有効でない場合は、ランタイムファイルの編集後にパスパラメーター plugin_id を指定して operation_id=plugin.reload を呼び出します。
  4. 依存関係マニフェストを変更した場合は、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 モード(vuetify JSON または vue フェデレーション)。Vue プラグインの場合は、公開したコンポーネントとビルド済みアセットのパスも報告します。
  • プラグインをインストールまたは再読み込みしたかどうか。
  • 実行した検証コマンド、または検証を実行しなかった理由。

コメント