コンテンツにスキップ

JavaScript プラグイン

この仕様は、オンラインのソースから Mankai に漫画を提供するプラグインの JSON 形式と JavaScript API を説明します。

JavaScript プラグインは読み取り専用のソースです。アプリは URL または貼り付けられた JSON からマニフェストを読み込み、非表示の WebKit Web ビューでコールバックスクリプトを実行します。

マニフェストは JSON オブジェクトです。デコーダーが必須とするフィールドは id だけですが、利用する操作に必要なコールバックスクリプトも用意する必要があります。

type ScriptName =
| 'isOnline'
| 'getSuggestion'
| 'search'
| 'getList'
| 'getMangas'
| 'getMangaUpdates'
| 'getDetailedManga'
| 'getChapter'
| 'getImage'
type PluginCapability =
| 'onlineCheck'
| 'suggestions'
| 'list'
| 'listByGenre'
| 'listByStatus'
| 'search'
| 'searchByGenre'
| 'searchByStatus'
| 'searchByAuthor'
| 'mangaDetails'
| 'batchMangas'
| 'mangaUpdates'
| 'chapter'
| 'image'
interface JsPluginManifest {
id: string
name?: string
version?: string
description?: string
authors?: string[]
repository?: string
updatesUrl?: string
availableGenres?: Genre[]
scripts?: Record<ScriptName, string>
configs?: Config[]
getImageHeaders?: Record<string, string>
cooldown?: Cooldown
capabilities?: PluginCapability[]
}
フィールド 型 説明
id string 変更されない、全体で一意なプラグイン識別子です。永続ストレージの保存範囲の識別にも使います。
name string 表示名です。省略すると ID を使います。
version string アプリに表示され、更新の比較に使われるプラグインのバージョンです。
description string ソース設定に表示する短い説明です。
authors string[] プラグインの作者です。既定値は [] です。
repository string ソースコードまたはプロジェクトの URL です。
updatesUrl string 更新確認に使うマニフェストの URL です。
availableGenres Genre[] ソースが対応するジャンルです。既定値は [] です。
scripts Record<string, string> 下記のスクリプト名をキーとする JavaScript ソースです。未知のキーは無視されます。
configs Config[] ユーザーが変更でき、スクリプトから参照できる設定です。既定値は [] です。
getImageHeaders Record<string, string> 指定すると、Mankai はすべての画像 URL をこのヘッダー付きのネイティブリクエストで取得し、getImage スクリプトを呼び出しません。
cooldown Cooldown 任意のリクエスト間隔と画像の同時取得数の制限です。
capabilities PluginCapability[] プラグインが対応する操作です。既定では mangaUpdates 以外のすべての機能が有効です。

各スクリプトの値は、関数とエクスポートマーカーを含む文字列です。

async function isOnline() {
const response = await fetch('https://example.com/')
return response.ok
}
export{isOnline as default};

マーカーは export{functionName as default}; の形式にする必要があります。Mankai は実行前にマーカーを取り除き、指定された名前の関数を呼び出します。関数は同期でも非同期でも構いません。Mankai は結果を待機します。スクリプトには、エクスポートする関数に加えて補助関数を含めることもできます。

任意の capabilities フィールドには、以下の値を指定できます。

onlineCheck, suggestions, list, listByGenre, listByStatus, search, searchByGenre, searchByStatus, searchByAuthor, mangaDetails, batchMangas, mangaUpdates, chapter, image

キーと関数シグネチャは次のとおりです。

キー 関数シグネチャ 戻り値
isOnline isOnline() boolean
getSuggestion getSuggestion(query) string[]
search search(query, page, genre, status, isAuthor) Manga[]
getList getList(page, genre, status) Manga[]
getMangas getMangas(ids) Manga[]
getMangaUpdates getMangaUpdates(mangas) メタデータの差分と更新フラグを持つ MangaUpdate[]
getDetailedManga getDetailedManga(id) DetailedManga
getChapter getChapter(manga, chapter) 画像 URL の string[]
getImage getImage(url) Base64 画像データ、または画像プロキシリクエストのオブジェクト

各引数と戻り値の詳細を以下に説明します。

ソースに接続でき、利用可能な場合は true、それ以外は false を返します。例外を投げた場合や、真偽値以外を返した場合は、プラグイン呼び出しの失敗として扱われます。

検索候補として使える文字列の配列を返します。マニフェストのキーは単数形の getSuggestion です。

async function getSuggestion(query) {
const response = await fetch(
`https://example.com/suggestions?q=${encodeURIComponent(query)}`,
)
if (!response.ok) return []
return await response.json()
}
export{getSuggestion as default};

タイトルまたは作者で漫画を検索します。page は 1 から始まるページ番号です。genre はジャンルにある文字列のいずれかで、status は状態にある数値のいずれかです。isAuthor が true の場合は、タイトル欄ではなく作者欄を検索します。簡易情報を持つ Manga オブジェクトの配列を返します。

ソースからページ単位の一覧を返します。page、genre、status の引数は search と同じです。

指定された文字列 ID に対応する、漫画の簡易オブジェクトの配列を返します。一部の ID がソースに存在しなくなっている場合は、要求数より少ない項目を返しても構いません。

各リクエスト項目には、漫画の ID と、Mankai が現在把握している最新チャプターが含まれます。このコールバックで、プラグイン側から更新動作を制御できます。Mankai の標準の一括比較を使う場合は、mangaUpdates 機能を指定しないでください。

interface MangaUpdateRequest {
id: string
latestChapter: Chapter
}

MangaUpdate[] を返します。各結果には id と updates が必要です。保存済みの漫画を更新ありとするかどうかは、updates フラグだけで決まります。メタデータは、updates が false の結果も含め、返されたすべての結果で更新されます。

interface MangaUpdate extends Manga {
updates: boolean
}
async function getMangaUpdates(mangas) {
const response = await fetch('https://example.com/manga/updates', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(mangas),
})
if (!response.ok) return []
return await response.json()
}
export{getMangaUpdates as default};

たとえば { id: "completed-series", updates: false, status: 2 } を返すと、未読の更新として表示せずに状態を更新できます。

完全な DetailedManga オブジェクトを返します。Mankai は戻り値を JSON にシリアライズしてからデコードするため、JSON 文字列ではなく JavaScript オブジェクトを返してください。

chapter の画像 URL を表示順に返します。manga は getDetailedManga が返した完全なオブジェクトで、chapter は選択されたチャプターのオブジェクトです。Mankai は後から各画像 URL を getImage に渡すため、画像 URL は絶対 URL にしてください。

async function getChapter(manga, chapter) {
const response = await fetch(
`https://example.com/manga/${encodeURIComponent(manga.id)}/chapter/${encodeURIComponent(chapter.id)}`,
)
if (!response.ok) return []
return await response.json()
}
export{getChapter as default};

getImageHeaders がない場合、Mankai は表紙とチャプターのページに対してこのスクリプトを呼び出します。次のいずれかを返せます。

  1. 画像のバイト列を Base64 でエンコードした文字列です。data:image/...;base64, の接頭辞は含めないでください。

  2. Mankai にネイティブリクエストを指示するオブジェクトです。

    interface ImageProxyRequest {
    url: string
    headers: Record<string, string>
    }

例を示します。

async function getImage(url) {
return {
url,
headers: {
Referer: 'https://example.com/',
'User-Agent': 'Mozilla/5.0',
},
}
}
export{getImage as default};

マニフェストに getImageHeaders がある場合は、すべての画像 URL に対して同等のネイティブリクエストが自動的に行われます。

{
"getImageHeaders": {
"Referer": "https://example.com/",
"User-Agent": "Mozilla/5.0"
}
}

コールバックの戻り値は、以下の形式に対応しています。任意のプロパティは省略できます。

interface Chapter {
id: string
title?: string
locked?: boolean
}
interface Manga {
id: string
title?: string
cover?: string
status?: Status
latestChapter?: Chapter
meta?: string
}
interface ChapterGroup {
id?: string
title: string
chapters: Chapter[]
}
interface DetailedManga {
id: string
title?: string
cover?: string
status?: Status
readingDirection?: ReadingDirection
latestChapter?: Chapter
description?: string
externalLink?: string
updatedAt?: number // Unix timestamp in milliseconds
authors?: string[]
genres?: Genre[]
chapters?: ChapterGroup[]
remarks?: string
meta?: string
}

Mankai が認識する文字列は次のとおりです。

all, action, romance, yuri, boysLove, otokonoko, schoolLife, adventure,
harem, speculativeFiction, war, suspense, fanFiction, comedy, magic, horror,
historical, sports, mature, mecha
名前 値
any 0
onGoing 1
completed 2
名前 値
leftToRight 1
rightToLeft 2
vertical 3

Mankai はすべてのコールバックスクリプトに、以下の補助関数を注入します。

実行環境は、ネイティブのネットワーク処理を使う fetch を提供します。対応するリクエストオプションは次のとおりです。

interface FetchOptions {
method?: string // Defaults to "GET"
headers?: Record<string, string> | Headers
body?: string
}

返されるレスポンスには、以下のプロパティとメソッドがあります。

interface FetchResponse {
ok: boolean
status: number
statusText: string
url: string
headers: Headers
text(): Promise<string>
json(): Promise<any>
blob(): Promise<Blob>
arrayBuffer(): Promise<ArrayBuffer>
}

console.log の出力先は Mankai の JavaScript プラグインログに変更されています。開発中のプラグインの問題を調べる際に使えます。

console.log('searching', query)

これらの非同期補助関数は、OpenCC を使って中国語の文字を変換します。

const traditional = await s2t('简体转繁体')
const simplified = await t2s('繁體轉簡體')

s2t は簡体字から繁体字に変換し、t2s はその逆に変換します。

マニフェストで宣言された設定の現在値を、配列で返します。

interface ConfigValue {
key: string
value: unknown
}

例を示します。

function configValue(key) {
return getConfigs().find((config) => config.key === key)?.value
}
const username = configValue('username')

以下の補助関数は、プラグインの id ごとに分離されたキーバリューストアに文字列を保存します。

getValue(key: string): Promise<string | null>;
setValue(key: string, value: string): Promise<void>;
removeValue(key: string): Promise<boolean>;

3 つの補助関数すべてに await を使ってください。値はアプリの再起動やプラグインの更新後も保持され、プラグインを削除すると取り除かれます。

const token = await getValue('token')
if (!token) {
await setValue('token', 'new-token')
}
await removeValue('temporary-value')

設定項目は次の形式です。

interface Config {
key: string
name: string
description?: string
type:
'text' | 'password' | 'number' | 'slider' | 'boolean' | 'select' | 'color'
defaultValue: unknown
options?: string[]
min?: number
max?: number
step?: number
supportsOpacity?: boolean
}

options は select の設定で使います。min、max、step は slider の範囲と刻み幅を設定します。アプリは各設定を defaultValue で初期化し、ソース設定で行った変更を保存して、現在値を getConfigs() から公開します。

例を示します。

{
"configs": [
{
"key": "language",
"name": "Language",
"description": "Language used when requesting titles.",
"type": "select",
"defaultValue": "en",
"options": ["en", "zh-Hans", "zh-Hant"]
},
{
"key": "imageQuality",
"name": "Image quality",
"type": "slider",
"defaultValue": 80,
"min": 10,
"max": 100,
"step": 5
},
{
"key": "includeMature",
"name": "Include mature content",
"type": "boolean",
"defaultValue": false
}
]
}

間隔の値はミリ秒単位です。

interface Cooldown {
default?: number
getImage?: number
getImageConcurrency?: number
}
フィールド 説明
default 検索、一覧、詳細、チャプターの取得など、画像以外の操作の最小間隔です。
getImage 画像リクエストの最小間隔です。画像はほかの操作とは別にスケジュールされます。
getImageConcurrency 同時に処理できる画像リクエストの最大数です。1 未満の値では制限を設定しません。

これらのフィールドを使い、ソースのレート制限を守ってください。例を示します。

{
"cooldown": {
"default": 500,
"getImage": 100,
"getImageConcurrency": 2
}
}