# 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 or an array of map like `{ url:string,headers:map }` 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).
