Skip to content
This repository has been archived by the owner on Jan 15, 2024. It is now read-only.

Add extension creation guide #1113

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions website/src/.vitepress/config/navigation/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,28 @@ function defaultSidebar(): DefaultTheme.SidebarItem[] {
},
],
},
{
text: 'Creating extensions',
collapsed: true,
items: [
{
text: 'Prerequisites',
link: '/docs/guides/creating-extensions/prerequisites',
},
{
text: 'Writing an extension',
link: '/docs/guides/creating-extensions/writing',
},
{
text: 'Compiling and running',
link: '/docs/guides/creating-extensions/compiling',
},
{
text: 'Signing and releasing',
link: '/docs/guides/creating-extensions/signing',
},
],
},
{
text: 'Source migration',
link: '/docs/guides/source-migration',
Expand Down
134 changes: 134 additions & 0 deletions website/src/docs/guides/creating-extensions/compiling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
---
title: Compiling and running
titleTemplate: Creating extensions - Guides
description: Compile and run own Tachiyomi extension.
---

# Compiling and running

## Running

To make local development more convenient, you can use the following run configuration to launch **Tachiyomi** directly at the Browse panel:

![Browse panel](/docs/guides/creating-extensions/compiling/BrowsePanel.webp =801x394)

If you're running a Preview or debug build of **Tachiyomi**:

```bash
-W -S -n eu.kanade.tachiyomi.debug/eu.kanade.tachiyomi.ui.main.MainActivity -a eu.kanade.tachiyomi.SHOW_CATALOGUES
```

And for a release build of **Tachiyomi**:

```bash
-W -S -n eu.kanade.tachiyomi/eu.kanade.tachiyomi.ui.main.MainActivity -a eu.kanade.tachiyomi.SHOW_CATALOGUES
```
::: warning IMPORTANT
If you're deploying to Android 11 or higher, enable the "Always install with package manager" option in the run configurations.
Without this option enabled, you might face issues such as **Android Studio** running an older version of the extension without the modifications you might have done.
:::

## Debugging

### Android Debugger

You can leverage the **Android Debugger** to step through your extension while debugging.

You *cannot* simply use **Android Studio**'s `Debug 'module.name'` -> this will most likely result in an error while launching.

Instead, once you've built and installed your extension on the target device, use `Attach Debugger to Android Process` to start debugging **Tachiyomi**.

![Android Debugger](/docs/guides/creating-extensions/compiling/AndroidDebugger.webp =532x474)

### Logs

You can also elect to simply rely on logs printed from your extension, which
show up in the [`Logcat`](https://developer.android.com/studio/debug/am-logcat) panel of **Android Studio**.

### Inspecting network calls

One of the easiest way to inspect network issues (such as HTTP errors 404, 429, no chapter found etc.) is to use the [`Logcat`](https://developer.android.com/studio/debug/am-logcat) panel of **Android Studio** and filtering by the `OkHttpClient` tag.

To be able to check the calls done by OkHttp, you need to enable verbose logging in the app, that is not enabled by default and is only included in the Preview versions of **Tachiyomi**. To enable it, go to <nav to="advanced"> and confirm that **Verbose logging** is enabled. After enabling it, don't forget to restart the app.

Inspecting the Logcat allows you to get a good look at the call flow and it's more than enough in most cases where issues occurs. However, alternatively, you can also use an external tool like `mitm-proxy`. For that, refer to the subsequent sections.

On newer **Android Studio** versions, you can use its built-in Network Inspector inside the
App Inspection tool window. This feature provides a nice GUI to inspect the requests made in the app.

To use it, follow the [official documentation](https://developer.android.com/studio/debug/network-profiler) and select **Tachiyomi** package name in the process list.

### Using external network inspecting tools
If you want to take a deeper look into the network flow, such as taking a look into the request and response bodies, you can use an external tool like `mitm-proxy`.

#### Setup your proxy server
We are going to use [mitm-proxy](https://mitmproxy.org/) but you can replace it with any other Web Debugger (i.e. Charles, Burp Suite, Fiddler etc). To install and execute, follow the commands bellow.

```bash
Install the tool.
$ sudo pip3 install mitmproxy
Execute the web interface and the proxy.
$ mitmweb
```

Alternatively, you can also use the Docker image:

```bash
$ docker run --rm -it -p 8080:8080 \
-p 127.0.0.1:8081:8081 \
--web-host 0.0.0.0 \
mitmproxy/mitmproxy mitmweb
```

After installing and running, open your browser and navigate to `http://127.0.0.1:8081`.

#### OkHttp proxy setup
Since most of the manga sources are going to use HTTPS, we need to disable SSL verification in order to use the web debugger. For that, add this code to inside your source class:

```kotlin
package eu.kanade.tachiyomi.extension.en.mysource

import android.annotation.SuppressLint
import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.OkHttpClient
import java.net.InetSocketAddress
import java.net.Proxy
import java.security.SecureRandom
import java.security.cert.X509Certificate
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager

class MySource : HttpSource() {
private fun OkHttpClient.Builder.ignoreAllSSLErrors(): OkHttpClient.Builder {
val naiveTrustManager = @SuppressLint("CustomX509TrustManager")
object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) = Unit
override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) = Unit
}

val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply {
val trustAllCerts = arrayOf<TrustManager>(naiveTrustManager)
init(null, trustAllCerts, SecureRandom())
}.socketFactory

sslSocketFactory(insecureSocketFactory, naiveTrustManager)
hostnameVerifier { _, _ -> true }
return this
}

override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.ignoreAllSSLErrors()
.proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress("10.0.2.2", 8080)))
.build()
}
```

Note: `10.0.2.2` is usually the address of your loopback interface in the android emulator. If **Tachiyomi** tells you that it's unable to connect to 10.0.2.2:8080 you will likely need to change it (the same if you are using hardware device).

If all went well, you should see all requests and responses made by the source in the web interface of `mitmweb`.

## Building

APKs can be created in **Android Studio** via `Build > Build Bundle(s) / APK(s) > Build APK(s)` or `Build > Generate Signed Bundle / APK`.
23 changes: 23 additions & 0 deletions website/src/docs/guides/creating-extensions/prerequisites.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: Prerequisites
titleTemplate: Creating extensions - Guides
description: Prerequisites to creating your own Tachiyomi extensions.
---

# Prerequisites

Before you start, please note that the ability to use following technologies is **required** and that existing contributors will not actively teach them to you.

- Basic [Android development](https://developer.android.com/)
- [Kotlin](https://kotlinlang.org/)
- Web scraping
- [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML)
- [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors)
- [OkHttp](https://square.github.io/okhttp/)
- [JSoup](https://jsoup.org/)

## Tools

- [Android Studio](https://developer.android.com/studio)
- Emulator or phone with developer options enabled and a recent version of Tachiyomi installed
- [Icon Generator](https://as280093.github.io/AndroidAssetStudio/icons-launcher.html)
99 changes: 99 additions & 0 deletions website/src/docs/guides/creating-extensions/signing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: Signing and releasing
titleTemplate: Creating extensions - Guides
description: Signing and releasing the repository
---

# Signing and releasing

To setup the GitHub actions to automatically release new extensions on every push, you will need to setup a signing key.
To do that, you need to create one first.

## Creating a signing key

The following steps will guide you into creating a key.

1. On Android Studio, open **Build → Generate Signed Bundle or APK**. Under the new screen, choose the **APK** option and click **Next**.
2. Click on **Create new…** under the **Key store path** field.
3. Fill up the required fields in the **New Key Store** window.

The fields under the Certificate section are optional, but you will need to fill at least one of them.
4. Click **OK** and it will create the signing key at the path choosen at the **Key store path** field.
5. You can close the **Generate Signed Bundle or APK** window as it will not be used anymore.

::: tip
Make sure to remember the given values of **Password**, **Alias** and **Key Password** as they will be needed later.
:::

::: danger Caution
Make sure to not use any special characters such as `@` or `!` under any of the passwords, otherwise it will lead to the generation of broken keys.
:::

## Configuring the GitHub actions

This section will guide you into setting up the secrets needed for the build and also some options you need to change under the repository settings.

### Adding the secrets

All secrets depends on a generated signed key which was described in the previous section.

1. In your repository on GitHub, open the **Settings** tab and navigate to **Secrets and variables → Actions**.
2. Under the **Secrets** tab, you will need to create four secrets by following the table below.

- `ALIAS`: The value of the key alias;
- `KEY_PASSWORD`: The main password of the key;
- `KEY_STORE_PASSWORD`: The password of the key store;
- `SIGNING_KEY`: Base-64 representation of the key `.jks` file content;

#### Creating the Base-64 representation

Under a terminal window in the path of the key, you can use the following command to convert the key and copy the value.

```bash
base64 signingkey.jks | tr -d \\n | xclip -selection c
```

The previous command depends on `xclip` installed to copy the contents of the conversion to the clipboard, but you can replace that last part with any other similar tool.

:::warning Caution
Make sure your copied value doesn't have any trailing whitespace while pasting the value under GitHub.
:::

### Enabling Actions read and write permission

The workflows will need write permissions to commit to the `repo` branch.

1. Under the repository Settings on GitHub, navigate to **Actions → General**.
2. Choose the **Read and write permissions** option on the **Workflow permissions** section and click **Save**.

### Creating an empty `repo` branch

If your repository doesn't already have a `repo` branch, you will need to create it.

To create you can follow the standard flow of creating a `branch` named `repo` and then proceed by deleting all repository files (except the `.git`) folder and then commiting and pushing to GitHub.

### Changing the checks in the workflow

If your repository is forked or cloned from the official extensions repository, you will need to change two checks under the `.github/workflows/build_push.yml` file.

<!-- eslint-disable-next-line -->
```yml
- name: Upload APKs
uses: actions/upload-artifact@v4
if: "github.repository == 'tachiyomiorg/extensions'" # [!code --]
if: "github.repository == '[user]/[repository]'" # [!code ++]
```

<!-- eslint-disable-next-line -->
```yml
publish_repo:
name: Publish repo
needs:
- build_individual
if: "github.repository == 'tachiyomiorg/extensions'" # [!code --]
if: "github.repository == '[user]/[repository]'" # [!code ++]
```

### Finishing up

If all is configured correctly, GitHub should run the workflow whenever there's a new commit pushed to the `main` branch which will build the extensions APKs and release it through the `repo` branch by creating the needed structure.
Loading