- 后端
- 前端
- 音视频
【免费下载链接】Auto_Bangumi
AutoBangumi - 全自动追番工具
AutoBangumi の検索プロバイダー(検索源)設定は、WebUI の Torrent 検索と「検索ベースの購読」を支える中核機能です。本記事では、docs/ja/config/search-provider.mdで説明されているカスタム URL テンプレートと PT サイト(NexusPHP)モードの設定方法を、実際のソースコード(backend/src/module/conf/search_provider.py、backend/src/module/searcher/provider.py、WebUI のwebui/src/components/setting/config-search-provider.vue)の実装と照らし合わせながら解説します。読み終えると、%sプレースホルダの扱い、search_provider.jsonのデータ構造、NexusPHP のtorrentrss.phpテンプレート生成の仕組み、そして「即時保存」される設定の裏側まで、実践的に理解できます。
{width=700}
検索プロバイダーとは:何に使われるか
検索プロバイダーは、次の2つの場面で使われます。
- WebUI の Torrent 検索:ページ上部の検索バーからアニメタイトルを検索し、候補となる Torrent を一覧表示する機能。
- 検索ベースの購読:検索結果から番組を選んで購読し、後続エピソードを自動取得する機能。
後者の「検索ベースの購読」の実体は、backend/src/module/api/rss.pyの/rss/subscribeエンドポイントです。ここでは検索時に選んだサイト名(nyaa、dmhyなど)が、検索プロバイダー設定に記録されたparserへとマッピングされてから購読処理に渡されます。これは「サイト名と解析器タイプを分離する」という設計意図によるもので、コメントにも「サイト名が解析器タイプ mikan/tmdb にマッピングされ、既に解析器タイプの値はそのまま透過される」と記されています(PARSER_TYPES = {"mikan", "tmdb", "parser"})。
内蔵プロバイダー
内蔵プロバイダーはmikan、nyaa、dmhyの3つ(ドキュメント記載)で、ソースコード上ではanibtも含めて計4つがDEFAULT_PROVIDERとして定義されています(backend/src/module/conf/search_provider.py)。
DEFAULT_PROVIDER: dict[str, ProviderConfig] = { "mikan": {"url": "https://mikanani.me/RSS/Search?searchstr=%s", "parser": "mikan"}, "anibt": {"url": "https://anibt.net/rss/magnets.xml?q=%s", "parser": "tmdb"}, "nyaa": {"url": "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", "parser": "tmdb"}, "dmhy": {"url": "http://dmhy.org/topics/rss/rss.xml?keyword=%s", "parser": "tmdb"}, }既定プロバイダーは削除できませんが、URL テンプレートは編集できます。WebUI 側でもwebui/src/components/setting/config-search-provider.vueでdefaultProviderNames = ['mikan', 'anibt', 'nyaa', 'dmhy']として固定され、削除ボタンは既定プロバイダーには表示されず、編集時も名称欄が無効化されます。parserに注目すると、mikanだけが専用のmikan解析器を使い、それ以外はtmdb解析器(TMDB 連携でタイトル・ポスターを補完する方式)を使う点がポイントです。
設定の保存の仕組み:即時保存と search_provider.json
ドキュメントが強調している通り、検索プロバイダーの変更は即時保存です。追加・編集・削除の操作が行われた瞬間にconfig/search_provider.jsonへ書き込まれ、画面下部の保存して再起動ボタンは不要です。
ソースコードで言えば、backend/src/module/conf/search_provider.pyのsave_provider()が、JSON ファイルへの書き込みと同時にモジュール内のSEARCH_CONFIGグローバル変数を更新しています。そのため、保存直後から新しい URL テンプレートが検索処理に反映されます。
def save_provider(providers: dict) -> None: global SEARCH_CONFIG normalized = _normalize(providers) for site, config in normalized.items(): if isinstance(providers.get(site), str) and site in SEARCH_CONFIG: config["parser"] = SEARCH_CONFIG[site]["parser"] json_config.save(PROVIDER_PATH, normalized) SEARCH_CONFIG = normalized読み込み側(load_provider())にも重要な挙動があります。
- ファイルが存在しない初回起動時:
config/search_provider.jsonを自動作成し、DEFAULT_PROVIDERの4サイトを書き出します(backend/src/module/conf/search_provider.py)。 - 旧バージョンの設定ファイル(サイト名→URL 文字列のみの形式)は
_normalize()で自動的に{"url": ..., "parser": ...}形式へ移行されます。このときの既定ルールは「mikanサイトならmikan解析器、それ以外はtmdb解析器」です(backend/src/module/conf/search_provider.py)。 - 後に追加された内蔵プロバイダー(例:
anibt)は、既存の設定ファイルに自動的に補完されます。ユーザーがカスタマイズ済みのサイトはそのまま維持されます(テストbackend/src/test/test_search_provider.pyで検証済み)。
これらの後方互換・補完ロジックは、backend/src/test/test_search_provider.pyにて「旧形式の読み込み」「新形式の保存」「parser 欠落時のデフォルト補完」「URL のみ更新時の parser 保持」などが網羅的にテストされています。
カスタムURLテンプレートの追加
カスタム検索源を追加する場合、URL テンプレートは検索語を差し込む%sを必ず含む必要があります。
https://example.com/search?q=%sこの%sが実際にどのように置き換えられるかは、backend/src/module/searcher/provider.pyのsearch_url()に実装されています。
def search_url(site: str, keywords: list[str]) -> RSSItem: keyword = "+".join(keywords) search_str = re.sub(r"[\W_ ]", "+", keyword) providers = get_provider() if site in providers: url = providers[site]["url"].replace("%s", search_str) parser = providers[site]["parser"] rss_item = RSSItem(url=url, aggregate=False, parser=parser) return rss_item else: raise ValueError(f"Site {site} is not supported")実装上は3つのポイントがあります。
- 複数キーワードの結合:キーワードは
+で連結されます(WebUI の検索はスペース区切りで分割され、backend/src/module/api/search.pyの/search/bangumiがkeywords.split(" ")で受け取ります)。 - 特殊文字のサニタイズ:
re.sub(r"[\W_ ]", "+", keyword)で英数字以外を+に置換し、URL を壊す文字を除去します。 - 置換は
str.replaceを使用:テンプレート中の%sはリテラル文字列として扱われます。コメントにある通り、検索語自体に正規表現の特殊文字が含まれていても安全に置換できるためです。 - 未定義サイトの扱い:設定に存在しないサイト名が指定された場合は
ValueErrorを送出します。
WebUI 側でも、追加・編集ダイアログのvalidateUrl()がurl.includes('%s')で検証し、%sが無い場合は「URL にプレースホルダがありません」という警告を表示して保存をブロックします(webui/src/components/setting/config-search-provider.vue)。
search_provider.json の設定フォーマット
既定ファイルパスはconfig/search_provider.jsonです。現在の形式は、サイト名をキーとし、urlとparserを持つオブジェクトの集合です。
{ "mikan": { "url": "https://mikanani.me/RSS/Search?searchstr=%s", "parser": "mikan" }, "nyaa": { "url": "https://nyaa.si/?page=rss&q=%s&c=0_0&f=0", "parser": "tmdb" } }| パラメータ | 説明 |
|---|---|
url | 検索 URL テンプレート。必ず%sを含む必要がある |
parser | その検索源の結果を解析する解析器。旧形式(URL 文字列のみ)のエントリは自動的にデフォルト解析器が補われる |
補足として、parserに指定できる値はbackend/src/module/api/rss.pyのPARSER_TYPES = {"mikan", "tmdb", "parser"}で示される3種類です。parserはカスタム解析器プラグイン(parser/analyser配下)を指します。既存の検索源について URL だけを更新した場合、既に保存されているparserの選択はデフォルトにリセットされず保持されます(save_provider()の実装およびテストbackend/src/test/test_search_provider.pyで確認できます)。
旧形式(URL 文字列のみ)の互換性
旧バージョンでは{"mikan": "https://...%s"}のように URL 文字列だけで保存されていました。この形式は読み込み時に自動変換されます。
def _normalize(raw: dict) -> dict[str, ProviderConfig]: for site, value in raw.items(): if isinstance(value, str): normalized[site] = {"url": value, "parser": _default_parser(site)} else: normalized[site] = { "url": value.get("url", ""), "parser": value.get("parser") or _default_parser(site), }つまり、旧形式のまま保存されている設定ファイルでも、そのまま読み込めて動作します。移行を意識する必要はありません。
PTサイト(NexusPHP)モード
PT サイト(NexusPHP 系、例:Audiences.me)の検索源を追加する場合は、WebUI の追加ダイアログでNexusPHP モードを選択します。サイト URL・Passkey・任意のカテゴリ ID を入力すると、torrentrss.phpの RSS 検索テンプレートが自動生成されます。
{width=700}
URL テンプレートの生成ルール
生成処理はwebui/src/utils/nexusphp.tsのbuildNexusPhpSearchUrl()に実装されています。
export function buildNexusPhpSearchUrl( options: NexusPhpProviderOptions ): string { let base = options.baseUrl.trim().replace(/\/+$/, ''); if (!/^https?:\/\//i.test(base)) { base = `https://${base}`; } const passkey = encodeURIComponent(options.passkey.trim()); const catParams = parseCategoryIds(options.categoryId) .map((id) => `&cat${encodeURIComponent(id)}=1`) .join(''); return `${base}/torrentrss.php?rows=50&linktype=dl&passkey=${passkey}${catParams}&search=%s`; }生成されるテンプレートの構造を分解すると、次のようになります。
| パラメータ | 意味 |
|---|---|
rows=50 | RSS で取得する最大エントリ数 |
linktype=dl | エントリのリンクを.torrentダウンロード URL に直結させる(qBittorrent などで直接取り込める) |
passkey=... | URL エンコード済みの Passkey。認証に使用 |
cat<ID>=1 | カテゴリの絞り込み。cat=402のような裸の形式ではなく、cat402=1のように ID ごとにスイッチを立てる形式である必要がある(NexusPHP のtorrentrss.phpは/^(cat\|sou\|med\|...)\d+$/形式のパラメータしか認識せず、裸のcat=は静かに無視される) |
search=%s | 検索語プレースホルダ。実際の検索語はバックエンド側で置換される |
カテゴリ ID はカンマ区切りで複数指定でき(例:402,403)、入力値は「空 = 絞り込みなし」「各セグメントが純数字のみ」というバリデーションを受けます(isValidNexusPhpCategoryIds、webui/src/utils/nexusphp.ts)。また、入力した Passkey は URL に埋め込まれますが、ダイアログ内のプレビューではpasskey=abc1…のように最初の4文字以外がマスクされて表示される配慮があります(webui/src/components/setting/config-search-provider.vue)。
注意:新しいネイティブ NexusPHP の search パラメータ無効化
::: warning 一部の新しいネイティブ NexusPHP サイトではsearchパラメータが無効化されており、どの検索語を指定しても最新 Torrent を返すことがあります。追加する前に、サイト上で RSS 検索が実際にキーワードで絞り込まれることを確認してください。 :::
これは空の警告ではありません。webui/src/utils/nexusphp.tsのコメントにも、ネイティブ(新世代)NexusPHP ではtorrentrss.php側のソースコードで$searchstr = nullと固定され、search パラメータが受け付けられないサイトが存在することが明記されています。そのため、このモードが有効に機能するのは「search サポートを残しているサイト・フォーク」に限定されます。PT サイトを追加した後は、実際に検索して絞り込みが効くかどうかを必ず確認しましょう。
NexusPHP の RSS 解析:enclosure と linktype=dl
検索で返ってくるtorrentrss.phpの RSS は、汎用 RSS 解析パスを通って Torrent リストに変換されます。解析の振る舞いはbackend/src/test/test_nexusphp_rss.pyに回帰テストとして記録されており、次の2パターンがあります。
<enclosure>付きエントリ:.torrentのダウンロード URL(download.php?id=...&passkey=...)が enclosure に含まれている場合は、それをダウンロードリンクとして採用し、<link>(details.phpの詳細ページ)をページリンクとして扱う。linktype=dlで enclosure なし:<link>自体がダウンロード URL になっているため、それをそのままダウンロードリンクとして採用する。
どちらの場合も、ダウンロード URL には Passkey が含まれるため、qBittorrent 等のダウンロードクライアントがそのまま .torrent を取得できます。
検索の実行フロー:URLテンプレートからTorrentへ
設定した検索プロバイダーが、実際の検索でどのように使われるかを追ってみましょう。検索処理の中核はbackend/src/module/searcher/searcher.pyのSearchTorrent.analyse_keyword()です。
async def analyse_keyword(self, keywords: list[str], site: str = "mikan", limit: int = 100): rss_item = search_url(site, keywords) # 1. URLテンプレート + 検索語 -> RSSItem torrents = await self.search_torrents(rss_item) # 2. RSS を取得して Torrent リスト化 for torrent in torrents: if len(exist_list) >= limit: # 3. 上限(既定100件)まで break bangumi = await self.analyser.torrent_to_data( torrent=torrent, rss=rss_item, fetch_poster=False ) ... yield json.dumps(bangumi.dict(), separators=(",", ":")) # 4. SSE で逐次配信search_url()で URL テンプレートの%sに検索語を埋め込み、RSSItem(aggregate=False、parser付き)を生成します。RequestContent経由で RSS を取得し、汎用 RSS 解析器で Torrent 一覧に変換します(NexusPHP の PT サイトもここで処理されます)。- 各 Torrent を
RSSAnalyser.torrent_to_data()で Bangumi 情報へ解析。検索の応答性を保つため、この段階では Mikan 個別ページの取得やポスターの逐次ダウンロードは行わず、fetch_poster=Falseとしています。 - 解析結果は Server-Sent Events(SSE)として逐次クライアントへ配信されます(
/search/bangumiエンドポイント、backend/src/module/api/search.py)。
検索結果には、TMDB によるローカライズタイトルとポスターの補完処理も含まれます。_fetch_tmdb_preview()は、プロセス生存中に無制限に肥大化しないよう、最大512件の LRU 風キャッシュ(OrderedDict)で管理されています(backend/src/module/searcher/searcher.py)。タイトルがローカライズされる一方でポスター URL は言語非依存であるため、キャッシュは「パーサー言語」単位でキーが分けられています。
また、購読済み番組の続きを探す「シーズン検索」では、special_url()が番組情報からgroup_name、title_raw、season_raw、subtitle、source、dpiの各フィールドを拾い出して検索キーワードを構成します(backend/src/module/searcher/searcher.py)。ここでも同じsearch_url()が使われるため、カスタム検索源の設定がそのままシーズン検索にも効いてきます。
APIによる検索プロバイダー管理
WebUI の設定画面は、以下の REST API を介してバックエンドとやり取りしています(backend/src/module/api/search.py)。
| メソッド | パス | 機能 |
|---|---|---|
GET | /search/bangumi?site=...&keywords=... | 検索実行(SSE で逐次配信) |
GET | /search/provider | 検索プロバイダーのサイト名一覧を返す |
GET | /search/provider/config | 全プロバイダーの URL テンプレートを返す({site: url}形式) |
PUT | /search/provider/config | プロバイダー設定を保存(save_provider()を呼び出し、即時反映) |
なお、GET/PUT /search/provider/configは内部の{url, parser}形式のうち URL のみを公開し、parserはエンドポイントの契約を変えない範囲で内部に保持しています。設定画面ではこの API を利用し、一覧の読み込み・追加・編集・削除のたびにPUTで全量を送信する方式を取っています(webui/src/components/setting/config-search-provider.vue)。また追加・編集時には重複サイト名をチェックし、既存サイト名との衝突を防いでいます。
まとめと関連ファイル
検索プロバイダー設定は、%sを含む URL テンプレートとparserの2要素だけで構成され、config/search_provider.jsonへの即時保存によって再起動なしで反映される、シンプルかつ柔軟な仕組みです。PT サイトを追加する場合は NexusPHP モードがtorrentrss.phpテンプレートを自動生成してくれますが、サイト側がsearchパラメータに対応しているかどうかの事前確認が必須です。
さらに深く理解したい場合は、以下のファイルを参照してください。
- 設定ロジック:
backend/src/module/conf/search_provider.py - 検索 URL 生成:
backend/src/module/searcher/provider.py - 検索実行フロー:
backend/src/module/searcher/searcher.py - 検索 API:
backend/src/module/api/search.py - 設定画面(WebUI):
webui/src/components/setting/config-search-provider.vue - NexusPHP URL 生成:
webui/src/utils/nexusphp.ts - 設定ロジックのテスト:
backend/src/test/test_search_provider.py - NexusPHP RSS 解析のテスト:
backend/src/test/test_nexusphp_rss.py - 購読時の parser マッピング:
backend/src/module/api/rss.py
- 后端
- 前端
- 音视频
【免费下载链接】Auto_Bangumi
AutoBangumi - 全自动追番工具
相关推荐
ASAP核心技术揭秘:Delta Action模型如何让机器人动作精度提升300%?
ASAP核心技术揭秘:Delta Action模型如何让机器人动作精度提升300%? ASAP(GitHub 加速计划)是一个专注于机器人运动控制的开源项目,其
LunaTranslatorのショートカットキー完全ガイド:登録の仕組みから全機能キー、カスタム拡張まで
LunaTranslatorのショートカットキー完全ガイド:登録の仕組みから全機能キー、カスタム拡張まで 本記事は、視覚ノベル翻訳ツール LunaTransla
桌面应用OCR人工智能CloudCLI(Claude Code UI)完全ガイド:Claude Code・Cursor CLI・Codex をWeb/モバイルから操作するセルフホスト型UIの導入からプラグイン拡張まで
CloudCLI(Claude Code UI)完全ガイド:Claude Code・Cursor CLI・Codex をWeb/モバイルから操作するセルフホスト
人工智能AI 应用交互助手代码智能体前端后端MCP 服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考