Files
kodjodevf-mangayomi-extensions/CONTRIBUTING-JS.md
2024-05-25 18:47:24 +01:00

206 lines
7.7 KiB
Markdown

# Contributing
This guide have some instructions and tips on how to create a new Mangayomi extension on JavaScript extension.
## Prerequisites
Before starting please have installed the recent desktop version of the mangayomi application preferably or if you want with a tablet too.
### Writing your extension
1. Open the app.
2. Go to extension tab :
![1](https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/screenshots/1.png)
3. then click `+` and you will see :
![2](https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/screenshots/2.png)
4. Fill in the fields with your new source that you would like to create,
![3](https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/screenshots/3.png)
NB: only the `ApiUrl` field is optional
then click on save
5. you will see your new source in the extension list
![4](https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/screenshots/4.png)
click to open settings
6. After click on edit code
![5](https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/screenshots/5.png)
7. Finally you can now write the extension
![6](https://raw.githubusercontent.com/kodjodevf/mangayomi-extensions/screenshots/6.png)
- This page contains three parts:
- Code editor: where you will write your code
- Fecth result: where you will test the different implemented methods by having a result in the expected format
- Console: which will show you the logs
Once extension is ready you can relocate your code into `mangayomi-extension` project in a `src` or `multisrc` package and create a Pull Request.
### Source
| Field | Description |
| ----- | ----------- |
| `name` | Name displayed in the "Sources" tab in Mangayomi. |
| `baseUrl` | Base URL of the source without any trailing slashes. |
| `apiUrl` | (Optional, defaults is empty) Api URL of the source with trailing slashes. |
| `lang` | An ISO 639-1 compliant language code (two letters in lower case in most cases, but can also include the country/dialect part by using a simple dash character). |
| `id` | Identifier of your source, automatically set in `Source`. It should only be manually overriden if you need to copy an existing autogenerated ID. |
| `isManga` | (Optional, defaults to `true`) specify source type (false for anime and true for manga)|
| `dateFormat` | (Optional, defaults is empty) |
| `iconUrl` | The extension icon URL |
| `version` | The extension version code. This must be incremented with any change to the code. |
| `dateFormatLocale` | (Optional, defaults is empty) |
| `isNsfw` | (Optional, defaults to `false`) Flag to indicate that a source contains NSFW content. |
### Extension call flow
#### Popular manga
a.k.a. the Browse source entry point in the app (invoked by tapping on the source name).
- The app calls `getPopular` which should return a JSON
```bash
{
'list': array of {'url':string,'name':string,'link':string},
hasNextPage: Boolean
}
```
- This method supports pagination. When user scrolls the manga list and more results must be fetched, the app calls it again with increasing `page` values(starting with `page=1`). This continues while `hasNextPage` is passed as `true` and `list` is not empty.
#### Latest manga
a.k.a. the Latest source entry point in the app (invoked by tapping on the "Latest" button beside the source name).
- Similar to popular manga, but should be fetching the latest entries from a source.
#### Search manga
- When the user searches inside the app, `search` will be called and the rest of the flow is similar to what happens with `getPopular`.
- `getFilterList` will be called to get all filters and filter types.
#### Manga Details
- When user taps on an manga, `getDetail` will be called and the results will be cached.
- `getDetail` is called to update an manga's details from when it was initialized earlier.
- `title` is a string containing title.
- `description` is a string containing description.
- `author` is a string containing author.
- `genre` contain array of all genres.
- `status` is an "integer" value.
You can refer to this example to see the correspondence:
```bash
0=>"ongoing", 1=>"complete", 2=>"hiatus", 3=>"canceled", 4=>"publishingFinished", 5=>unknow
```
- `chapters` or `episodes` contain all of all manga chapters or anime episodes.
- `name` is a string containing a chapter name.
- `url` is a string containing a chapter url.
- `scanlator` is a string containing a chapter scanlator.
- `dateUpload` is a string containing date **expressed in millisecondsSinceEpoch**.
- If you don't pass `dateUpload` and leave it null, the app will use the default date instead, but it's recommended to always fill it if it's available.
#### Chapter pages
- When user opens an chapter, `getPageList` will be called and it will return an array of string that are used by the reader.
#### Episode Videos
- When user opens an episode, `getVideoList` will be called and it will return a
```bash
array of {'url':string,'originalUrl':string,'quality':string}
```
that are used by the player.
## Example sources that can help you understand how to create your source
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/javascript/anime/src/de/aniworld.js)
of HTML parsing using HTML DOM selector.
- [Example](https://github.com/kodjodevf/mangayomi-extensions/blob/main/javascript/anime/src/en/allanime.js)
of Json API usage.
## Some functions already available and usable
### http client
Return Response
```bash
- Simple request
const client = new Client();
const res = await client.get("http://example.com");
console.log(res.body);
- With headers
const client = new Client();
const res = await client.get("http://example.com",{"Referer": "http://example.com"});
console.log(res.body);
- With body
const client = new Client();
const res = await client.post("http://example.com",{"Referer": "http://example.com"},{'name':'test'});
console.log(res.body);
```
### HTML DOM selector
Example:
```bash
const htmlString = `
<html lang="en">
<body>
<div><a href='https://github.com/kodjodevf'>author</a></div>
<div class="head">div head</div>
<div class="container">
<table>
<tbody>
<tr>
<td id="td1" class="first1">1</td>
<td id="td2" class="first1">2</td>
<td id="td3" class="first2">3</td>
<td id="td4" class="first2 form">4</td>
<td id="td5" class="second1">one</td>
<td id="td6" class="second1">two</td>
<td id="td7" class="second2">three</td>
<td id="td8" class="second2">four</td>
</tr>
</tbody>
</table>
</div>
<div class="end">end</div>
</body>
</html>`
const document = new Document(htmlString);
console.log(document.selectFirst("a").attr("href")); // https://github.com/kodjodevf
console.log(document.selectFirst("td").text); // 1
```
See [`dom_selector`](https://github.com/kodjodevf/mangayomi/blob/main/lib/eval/javascript/dom_selector.dart) to see available methods.
### String utils
- this.substringAfter(`string: pattern`)
- this.substringAfterLast(`string: pattern`)
- this.substringBefore(`string: pattern`)
- this.substringBeforeLast(`string: pattern`)
- this.substringBetween(`string: left`, `string: right`)
### Crypto utils
- unpackJs(`string: code`);
- deobfuscateJsPassword(`string: inputString`)
- encryptAESCryptoJS(`string: plainText`, `string: passphrase`)
- decryptAESCryptoJS(`string: encrypted`, `string: passphrase`)
- cryptoHandler(`string: text`, `string: iv`, `string: secretKeyString`, `Boolean: encrypt`)
## Help
If you need a help or have some questions, ask a community in our [Discord server](https://discord.com/invite/EjfBuYahsP).