mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-10-29 11:46:17 +08:00
标签在发布之前打上,之后的轮次不再检查该种子:订阅者处理中被取消 (重启、插件重载)的事件永远不会再投递,硬链接漏掉这一集。现在每个 进程按当前文件名为已打标签的种子补发一次,丢失的事件在下次启动时 重新投递,投递恢复为至少一次;没有事件总线时不查询文件。 test_renamed_tag_skips_before_file_and_offset_queries 改为在已发布的 稳态下断言。文档与 TorrentOrganized 说明同步更新。
72 lines
6.5 KiB
Markdown
72 lines
6.5 KiB
Markdown
# イベント
|
||
|
||
イベントは凍結された dataclass です。クラス変数 `kind` がイベント名です。ホストとプラグインは 1 つのイベントバスを共有します。
|
||
|
||
```python
|
||
from ab_sdk import subscribe
|
||
from ab_sdk.events import RssFailureEvent
|
||
|
||
@subscribe("rss_failure")
|
||
async def on_rss_failure(self, event: RssFailureEvent):
|
||
self.ctx.log.warning("購読 %s が失敗しました:%s", event.rss_name, event.error)
|
||
```
|
||
|
||
- 購読者は専用のキューで、発行順に非同期で実行されます。失敗やタイムアウトは発行側に影響せず、プラグインのブレーカーに数えられます。既定のタイムアウトは 30 秒です。大きなファイルのコピーのような長い処理には `@subscribe(kind, timeout=600)` を使います。
|
||
- `"*"` は全イベントを購読します。
|
||
- 発行は `self.ctx.bus.publish(event)` で行います。プラグインのイベントの `kind` は `<プラグイン id>.` で始める必要があります。ホストのイベントとの衝突を防ぐためです。
|
||
- フロントエンドコンポーネントは `host.events.on(kind, callback)` で同じバスを購読します。コールバックはイベントのフィールドを受け取ります([フロントエンドスロット](/ja/dev/plugins/frontend-slots) を参照)。
|
||
|
||
## システムイベント
|
||
|
||
AB が通知センターに送るイベントは、すべて `ab_sdk.events.SystemEvent` のサブクラスです。`severity`、`payload()`、`describe()`(既定の中国語タイトルと本文)、`i18n()`(フロントエンドの i18n キーと引数)を持ちます。AB はまず通知センターに書き込み、次にイベントバスへ発行し、最後に外部チャンネルへプッシュします。「通知」スイッチが影響するのは外部へのプッシュだけです。プラグインは常にイベントを受け取ります。
|
||
|
||
| kind | イベントクラス | 発行のタイミング |
|
||
| --- | --- | --- |
|
||
| `rss_failure` | `RssFailureEvent` | RSS 購読が正常から接続異常に変わったとき |
|
||
| `download_failure` | `DownloadFailureEvent` | リトライ後もトレントの追加に失敗したとき |
|
||
| `offset_review` | `OffsetReviewEvent` | 作品のシーズン / 話数オフセットの確認が必要なとき |
|
||
| `downloader_unavailable` | `DownloaderUnavailableEvent` | ダウンローダーに接続できない、認証情報が違う、IP が遮断された。各インスタンスが利用可能から利用不可に変わるたびに 1 回 |
|
||
| `update_available` | `UpdateAvailableEvent` | 新しいバージョンを検出したとき |
|
||
| `update_applied` / `update_failed` | `UpdateAppliedEvent` | オンライン更新が成功 / 失敗したとき |
|
||
| `llm_auth_failure` | `LLMAuthFailureEvent` | サブスクリプション型 LLM プロバイダーの認証情報が無効になったとき |
|
||
| `llm_plugin_install_failed` | `LLMPluginInstallFailedEvent` | LLM プラグインのインストールに失敗したとき |
|
||
| `rename_conflict` | `RenameConflictEvent` | メディアファイルのリネームで保存先が競合したとき |
|
||
| `rename_skipped` | `RenameSkippedEvent` | リネーム方式がファイル名を決められず、元の名前のままにしたとき |
|
||
| `plugin.loaded` | `PluginLoaded` | プラグインの読み込みに成功したとき |
|
||
| `plugin.disabled` | `PluginDisabled` | プラグインの読み込みに失敗した、またはブレーカーが作動したとき |
|
||
|
||
システムイベントの `kind` は 3.x の値のままです(通知センターはこれで保存と翻訳を行います)。そのため `.` のプレフィックスはありません。
|
||
|
||
## 整理イベント
|
||
|
||
| kind | イベントクラス | フィールド | 発行のタイミング |
|
||
| --- | --- | --- | --- |
|
||
| `file.renamed` | `FileRenamed` | `bangumi_id`、`old_path`、`new_path`、`file_kind`、`downloader_id` | ファイルが実際にリネームされたとき(バージョン置換後のリネームを含む) |
|
||
| `torrent.organized` | `TorrentOrganized` | `torrent_hash`、`bangumi_id`、`files`(`OrganizedFile(path, kind)` のタプル)、`downloader_id` | トレントの整理が完了したとき。リネーム方式が `none` でも発行され、`files` は元のパスです。選択したリネーム方式が未登録(プラグインが無効、または遮断済み)の間は発行されません |
|
||
|
||
- パスは**ダウンローダーから見た**絶対パスで、`/` で連結されます(Windows ダウンローダーの `\` も `/` に統一されます)。AB とダウンローダーで見えるディレクトリが異なる場合(別々のコンテナで動作している場合など)は、購読者側でパスを変換する必要があります。
|
||
- `bangumi_id` はトレントの `ab:<id>` タグに由来します。古いトレントでは `None` の場合があります。`downloader_id` はトレントがあるダウンローダーインスタンスの id です。インスタンスごとにパスの見え方が異なることがあります。
|
||
- `torrent.organized` の配信は**少なくとも 1 回**(at-least-once)です。ダウンローダーにある整理済みのトレントは、「リネーム済み」タグがあっても、AB を再起動するたびにもう一度発行されます。購読者は冪等にしてください。
|
||
- この 2 つのイベントはイベントバスにだけ発行されます。通知センターには入りません。
|
||
|
||
## 独自イベント
|
||
|
||
通常のイベントは `Event` を継承して定義します。通知できるイベントは `SystemEvent` を継承します。発行すると、ホストのイベントと同じ経路をたどります。通知センターへの書き込み(フロントエンドに翻訳がなければ `describe()` のタイトルと本文を表示)、イベントバスへの発行、外部チャンネルへのプッシュです。`dedup_key()` が同じイベントは、通知センターで 1 件にまとめられます。
|
||
|
||
```python
|
||
from dataclasses import dataclass
|
||
from typing import ClassVar
|
||
from ab_sdk.events import SystemEvent
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class SyncFailed(SystemEvent):
|
||
kind: ClassVar[str] = "my-plugin.sync_failed"
|
||
severity: ClassVar[str] = "warning"
|
||
reason: str
|
||
|
||
def describe(self) -> tuple[str, str]:
|
||
return ("同期に失敗しました", self.reason)
|
||
```
|
||
|
||
組み込みの `hardlink` の `hardlink.failed` も、この方法で発行しています。
|