-
Notifications
You must be signed in to change notification settings - Fork 54
Development Guideline v2
First of all, thank you for your interest in contributing! Please read this guideline carefully before you start working on your plugin.
Your plugin folder should have the following structure
.
├── index.js
├── info.json
└── README.md
Thanks to Duktape and duktape.cr, the plugins can be written in simple JavaScript. The index.js
file defines the logic of the plugin. Unfortunately, due to some technical limitations, there's no ES6 syntax support (duktape#2261), and you cannot require
other modules in your script (duktape.cr#55).
A type is a JavaScript object with required and optional properties. Here we define some types that will be used throughout this documentation.
Manga
A manga series in your plugin.
Key | Type | Description | Required |
---|---|---|---|
id | string | A unique ID of the manga | true |
title | string | true | |
description | string | ||
authors | Array<string> | ||
cover_url | string | ||
tags | Array<string> |
Chapter
A downloadable chapter in your plugin.
Key | Type | Description | Required |
---|---|---|---|
id | string | A unique ID of the chapter | true |
title | string | true | |
pages | number | Number of pages in the chapter | true |
manga_title | string | Title of the manga containing this chapter | true |
volume | string | The volume number | |
chapter | string | The chapter number | |
groups | Array<string> | A list of group names | |
language | string | ||
tags | Array<string> |
NOTE: The chapter ID must be universally unique within your plugin. You can't have two chapters (even in different manga) with the same ID.
Page
A downloadable page in a chapter.
Key | Type | Description | Required |
---|---|---|---|
url | string | true | |
filename | string | The unique filename of the page | true |
headers | object<string, string> | The headers to use when downloading the page |
Mango provides some helper functions for you to use in your plugin script. They can be accessed from the mango
object.
mango.get(url, [headers])
This function sends a GET request to url
, and optionally you can pass in a second parameter headers
. It returns an object with the following fields: body
, status_code
, and headers
.
Example:
mango.get('https://github.com');
// returns the following object
{
status_code: 200,
body: "<!DOCTYPE html><html lang=\"en\"> ... </html>",
headers: {
"content-type": "text/html; charset=utf-8",
"server":"github.com",
// ...
}
}
-
mango.post(url, body, [headers])
(Mango version > v0.18.0)
This function sends a POST request to url
. The request body
can be defined with the second parameter, and the third parameter headers
is optional. It returns an object with the following fields: body
, status_code
, and headers
.
Example:
mango.post('https://httpbin.org/anything', "Test Body", {"Header1": "Value1", "Header2": "Value2"});
// sends the following data
{
'data': 'Test Body'
'headers': {
'Accept-Encoding': 'gzip, deflate',
'Content-Length': '9',
'Header1': 'Value1',
'Header2': 'Value2',
'Host': 'httpbin.org',
'User-Agent': 'Crystal'
// ...
}
}
// receives the following data
{
"status_code": 200,
"body": {
'data': 'Test Body'
'headers': {
'Accept-Encoding': 'gzip, deflate',
'Content-Length': '9',
'Header1': 'Value1',
'Header2': 'Value2',
'Host': 'httpbin.org',
'User-Agent': 'Crystal'
},
'json': null,
'method': 'POST'
},
"headers": {
"Content-Type": "application/json",
"Server": "gunicorn/19.9.0",
// ...
}
}
mango.css(html, selector)
This function applies the CSS selector
and returns an array of matched HTML elements. An empty array is returned when no match is found.
Example:
mango.css('<ul><li>A</li><li>B</li><li>C</li></ul>', 'li');
// returns the following array
["<li>A</li>", "<li>B</li>", "<li>C</li>"]
mango.text(html)
This function returns the inner text of html
. An empty string is returned when the HTML contains no text.
Example:
mango.text('<a href="https://github.com">Click Me<a>');
// returns the following string
"Click Me"
mango.attribute(html, attr)
This function returns the attr
attribute of the outmost node. If no matched attribute is found, it returns undefined
.
Example:
mango.attribute('<a href="https://github.com">Click Me<a>', 'href');
// returns the following string
"https://github.com"
mango.storage(key, [val])
This function can be used to store and retrieve key/value pairs. The data is stored in storage.json
in your plugin folder, and Mango will not modify its content.
Usage:
// This stores the login token
mango.storage('login-token', '9fa35e3588c15bf4cc13ee1f4d5043ab');
// This retrieves the login token
var token = mango.storage('login-token');
// If the key does not exist, the function returns `undefined`
mango.storage('heyo'); // undefined
mango.settings(key)
This function retrieves the value for key
as defined in the settings
property in info.json
. It returns undefined
when the key doesn't exist, or when the value is null
. For example, in your plugin's README, you may ask the user to paste their credentials for a website into the settings
property, and in your script you can load the credentials with this function and perform authentication.
Usage:
var username = mango.settings('username');
var password = mango.settings('password');
if (!username || !password) {
// error handling
// ...
}
// Use the values defined in settings
var token = myAwesomeLoginFunction(username, password);
mango.raise(msg)
This function raises an exception with error message msg
in the upstream Crystal method.
This file defines the logic of your plugin. You must define the following functions in index.js
:
searchManga(query: string): Array<Manga>
listChapters(manga_id: string): Array<Chapter>
selectChapter(id: string): Chapter
nextPage(): Page
And optionally you can define the following functions
newChapters(manga_id: string, after_timestamp: number): Array<Chapter>
NOTE: The returned values from these functions must be JSON stringified.
We will now go through the functions in detail here.
This function is called by Mango when the user searches something on the download page of your plugin, and query
is a string containing the user's input. It should return a JSON stringified array of Manga
matching the query. If no matching manga is found, return a stringified empty array.
This function is called by Mango when the user chooses a manga to view its chapters, and manga_id
is the ID of the selected manga. It should return a JSON stringified array of Chapter
s within the manga.
This function is called by Mango when it starts to download the chapter with id
, and it should return a JSON stringified Chapter
matching the ID.
This function is called by Mango when it starts to download a page of the selected chapter, and it should return a JSON stringified Page
. You can optionally include the headers
field in the Page
object to instruct Mango to use the headers when downloading the page.
This function is optional. When it is defined, the users will be able to subscribe to some manga in your plugin. Mango will call this method periodically to check for new chapter in the manga, and any chapter returned will be added to the download queue.
The function should return a JSON stringified array of Chapter
s. manga_id
is the ID of the manga, and after_timestamp
is a unix timestamp in milliseconds. Your implementation of this function should check for new chapters released AFTER the specified timestamp. If no new chapters are available, return a JSON stringified empty array.
This JSON file contains the metadata of the plugin. It must contain the following fields:
-
id
: The ID of the plugin. It should contain only alphanumerical characters and underlines. In principle, it should be identical to the plugin folder name. -
title
: The human-readable title of the plugin. -
placeholder
: The placeholder text to be displayed on the input field on the plugin download page. -
wait_seconds
: The number of seconds to wait before downloading the next page.
Optionally you can include the following fields
-
api_version
: The API version to use. Since you are reading the development guideline for v2, you probably want to put2
in this field. If this field is undefined, Mango would treat it as a v1 plugin. -
settings
: A read-only object containing settings that can be modified by users. You can retrieve the content using themango.settings()
helper function. Each value in the object should be either a string or null.