### Install mitm-proxy Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Commands to install and execute mitm-proxy for external network inspection. ```console mitmproxy ``` -------------------------------- ### Run mitmweb Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Execute the mitmweb command to start the web interface and the proxy server. ```bash $ mitmweb ``` -------------------------------- ### SimpleDateFormat Configuration Examples Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Illustrates correct and incorrect ways to configure `SimpleDateFormat` for timezone handling and locale. ```kotlin // Wrong: 'Z' is treated as a literal character, timezone defaults to device local time SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT) // Correct: SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("UTC") } // Also correct (Z without quotes parses the timezone offset from the string): SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ROOT) ``` -------------------------------- ### Install mitmproxy Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Install the mitmproxy tool using pip. This is required for debugging network traffic. ```bash $ sudo pip3 install mitmproxy ``` -------------------------------- ### Theme Directory Structure Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Example directory structure for creating a new theme within the 'lib-multisrc' directory. The theme name should be lowercase ASCII letters and digits. ```console $ tree lib-multisrc// lib-multisrc// ├── build.gradle.kts └── src └── main └── java └── eu └── kanade └── tachiyomi └── multisrc └── └── .kt ``` -------------------------------- ### Custom Filter Implementation Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Example of creating a custom filter by extending Filter.Select. This is useful for creating filters with specific URI part transformations. ```kotlin open class UriPartFilter(displayName: String, private val vals: Array>) : Filter.Select(displayName, vals.map { it.first }.toTypedArray()) { fun toUriPart() = vals[state].second } ``` -------------------------------- ### Adapting fetchSearchManga for URL Queries Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Example of modifying the fetchSearchManga function to parse a URL query, extract an ID, and make an API call to retrieve manga details. ```kotlin if (query.startsWith("https://")) { val url = query.toHttpUrlOrNull() if (url != null && url.host == baseUrl.toHttpUrl().host) { val typeIndex = url.pathSegments.indexOfFirst { it == "detail" || it == "view" } if (typeIndex != -1 && typeIndex + 1 < url.pathSize) { val id = url.pathSegments[typeIndex + 1] return GET("$apiUrl/Book?select=id,judul,cover&type=not.ilike.*novel*&id=eq.$id", apiHeaders) } } } ``` -------------------------------- ### Create New Library: build.gradle.kts Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Configure the `build.gradle.kts` file for a new library by applying the `kei.plugins.library` plugin. If the new library depends on another, declare it using `implementation`. ```kotlin plugins { alias(kei.plugins.library) } ``` ```kotlin plugins { alias(kei.plugins.library) } dependencies { implementation(project(":lib:")) } ``` -------------------------------- ### Extension File Structure Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Illustrates the standard directory and file layout for a new extension. Ensure your code resides within the correct package structure. ```console $ tree src/// src/// ├── AndroidManifest.xml (optional) ├── build.gradle ├── res │   ├── mipmap-hdpi │   │   └── ic_launcher.png │   ├── mipmap-mdpi │   │   └── ic_launcher.png │   ├── mipmap-xhdpi │   │   └── ic_launcher.png │   ├── mipmap-xxhdpi │   │   └── ic_launcher.png │   └── mipmap-xxxhdpi │      └── ic_launcher.png └── src └── eu └── kanade └── tachiyomi └── extension └── └── ├── .kt ├── .kt (optional) ├── .kt (optional) └── .kt (optional) ``` -------------------------------- ### Extension build.gradle Configuration Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Defines essential properties for your extension's build script. Ensure `extClass` correctly points to your main source implementation. ```groovy ext { extName = '' extClass = '.' extVersionCode = 1 isNsfw = true } apply plugin: "kei.plugins.extension.legacy" ``` -------------------------------- ### Run mitmproxy with Docker Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use the Docker image to run mitmweb, mapping the necessary ports for web and proxy access. ```bash $ docker run --rm -it -p 8080:8080 \ -p 127.0.0.1:8081:8081 \ --web-host 0.0.0.0 \ mitmproxy/mitmproxy mitmweb ``` -------------------------------- ### Build APK from Command Line Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use the gradlew command to build a debug APK for a specific source. Ensure you have knowledge of gradlew and your OS. ```console // For a single apk, use this command $ ./gradlew src:::assembleDebug ``` -------------------------------- ### Configure Sparse Checkout (Cone Mode) Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Enable cone mode for faster pattern matching and a more responsive Git experience in large monorepos. This mode filters by file path. ```bash git sparse-checkout set --cone --sparse-index git sparse-checkout add common core gradle lib lib-multisrc utils git sparse-checkout add src// ``` -------------------------------- ### UrlActivity.kt for Handling URL Intents Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Processes incoming URL intents, extracts the URL, and launches Mihon's search action with the URL as a query. Avoid Kotlin Intrinsics. Finishes and exits after processing. ```kotlin class UrlActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val intentData = intent?.data?.toString() if (intentData != null) { val mainIntent = Intent().apply { action = "eu.kanade.tachiyomi.SEARCH" putExtra("query", intentData) putExtra("filter", packageName) } try { startActivity(mainIntent) } catch (e: Throwable) { Log.e("RiztranslationUrl", e.toString()) } } else { Log.e("RiztranslationUrl", "could not parse uri from intent $intent") } finish() exitProcess(0) } } ``` -------------------------------- ### Configure Theme Package in Gradle Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Add the `themePkg` property to your `build.gradle` file to use a theme in your extension. Note that `overrideVersionCode` is used instead of `extVersionCode` for theme-based extensions. ```groovy ext { extName = '' extClass = '.' themePkg = '' overrideVersionCode = 1 isNsfw = true } apply plugin: "kei.plugins.extension.legacy" ``` -------------------------------- ### Configure Git Remotes Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Set up upstream and origin remotes for the repository. This includes adding the upstream remote, optionally disabling push to upstream, and configuring fetch refspecs. ```bash git remote add upstream git remote set-url --push upstream no_pushing git config remote.upstream.fetch "+refs/heads/main:refs/remotes/upstream/main" git remote update git branch main -u upstream/main ``` -------------------------------- ### Theme build.gradle.kts Configuration Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Configuration for a theme's build.gradle.kts file. 'baseVersionCode' must be a positive integer and incremented for theme implementation changes. ```kotlin plugins { alias(kei.plugins.multisrc) } baseVersionCode = 1 ``` -------------------------------- ### Configure Sparse Checkout (Legacy Mode) Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use legacy non-cone mode for sparse checkout if simpler filters are preferred. This involves enabling sparse checkout and manually editing the filter file. ```bash git sparse-checkout set --no-cone vim .git/info/sparse-checkout ``` ```bash code .git/info/sparse-checkout ``` ```bash /* !/src/* !/multisrc-lib/* # allow a single source /src// # allow a multisrc theme /lib-multisrc/ # or type the source name directly ``` -------------------------------- ### Protobuf Parsing and Serialization Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use these helpers for decoding Protobuf data from responses and encoding data into request bodies. They utilize a shared proto instance and manage resources automatically. ```kotlin import keiyoushi.utils.parseAsProto import keiyoushi.utils.toRequestBodyProto import keiyoushi.utils.decodeProtoBase64 // From a Response (automatically closes the body): val dto = response.parseAsProto() // From a Response with a transform applied before decoding: val dto = response.parseAsProto { bytes -> bytes.drop(4).toByteArray() } // Decoding a Base64-encoded Protobuf string: val dto = base64String.decodeProtoBase64() // Creating a RequestBody for a POST request (defaults to application/protobuf): val requestBody = myRequestDto.toRequestBodyProto() ``` ```kotlin val bytes: ByteArray = ... val dto = bytes.decodeProto() ``` ```kotlin val myDto: MyProtoDto = ... val bytes: ByteArray = myDto.encodeProto() ``` -------------------------------- ### Useful Git Configurations Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Apply optional Git configurations to enhance workflow, such as pruning obsolete remote branches, enforcing fast-forward pulls, and creating a custom sync alias. ```bash git config remote.origin.prune true git config pull.ff only git config alias.sync-main '!git switch main && git fetch upstream && git reset --keep FETCH_HEAD' ``` -------------------------------- ### Package Name Variations for Launch Flags Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Specifies the package names to use in launch flags for different build types of the Mihon app. ```text Release build: app.mihon Preview build: app.mihon.debug ``` -------------------------------- ### Launch Flags for Local Development Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use these launch flags in your Android Studio Debug build configuration to launch the app directly into the Browse panel for local testing. Adjust the package name based on the build type. ```bash -W -S -n app.mihon.dev/eu.kanade.tachiyomi.ui.main.MainActivity -a eu.kanade.tachiyomi.SHOW_CATALOGUES ``` -------------------------------- ### Add Extension Library Dependency Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Declare a library module in your extension's `build.gradle` file. Gradle handles transitive dependencies automatically. ```groovy dependencies { implementation(project(':lib:')) } ``` ```groovy dependencies { implementation(project(':lib:dataimage')) } ``` -------------------------------- ### Filtering Lists with `firstInstance` helpers Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use `firstInstance` and `firstInstanceOrNull` as more efficient alternatives to `filterIsInstance().first()` and `filterIsInstance().firstOrNull()`. ```kotlin import keiyoushi.utils.firstInstance import keiyoushi.utils.firstInstanceOrNull val genreFilter = filters.firstInstanceOrNull() ``` -------------------------------- ### Partial Git Clone Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Clone the repository using a partial clone to save network traffic and disk space. This is recommended due to the large size of the repository. ```bash git clone --filter=blob:none --sparse cd extensions-source/ ``` -------------------------------- ### Testing URL Intent Filter with ADB Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Command to test the URL intent filter by opening a specified link using ADB. ```bash adb shell am start -d "" -a android.intent.action.VIEW ``` -------------------------------- ### Theme Main Class Declaration Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Declaration for the main theme class, which must be an abstract class extending HttpSource. It allows individual extensions to inherit and override properties and methods. ```kotlin package eu.kanade.tachiyomi.multisrc. import eu.kanade.tachiyomi.source.online.HttpSource abstract class ( override val name: String, override val baseUrl: String, override val lang: String, ) : HttpSource() { // Theme default implementation... } ``` -------------------------------- ### Generate Page List with Index Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use `mapIndexed` to create Page objects, ensuring the list is sorted correctly. The index passed to the Page object is ignored by the app. ```kotlin return document.select(".pages img").mapIndexed { index, img -> Page(index, imageUrl = img.attr("abs:src")) } ``` -------------------------------- ### Prefer Direct String Interpolation for Static URLs Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Avoid using HttpUrl.Builder for static URLs. Use direct string interpolation for simplicity and clarity when no dynamic query parameters are involved. ```kotlin // Unnecessary builder for a static URL: val url = "$baseUrl/manga".toHttpUrl().newBuilder().build() // Prefer: return GET("$baseUrl/manga", headers) ``` -------------------------------- ### SharedPreferences Access Helpers Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Provides `getPreferences` and `getPreferencesLazy` for accessing SharedPreferences, avoiding manual `Injekt` calls. ```kotlin import keiyoushi.utils.getPreferences import keiyoushi.utils.getPreferencesLazy // Eager: private val preferences = getPreferences() // Lazy (recommended for most cases): private val preferences by getPreferencesLazy() ``` -------------------------------- ### URL Extraction with `setUrlWithoutDomain` and `absUrl` Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Safely extract URLs from HTML by combining `element.absUrl("href")` with `setUrlWithoutDomain()` to handle both absolute and relative links correctly. ```kotlin // Risky - setUrlWithoutDomain cannot resolve all relative URLs: setUrlWithoutDomain(element.attr("href")) // Safe: setUrlWithoutDomain(element.absUrl("href")) ``` -------------------------------- ### Safe Date Parsing with SimpleDateFormat Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Safely parse date strings using `tryParse` with a pre-configured `SimpleDateFormat`. This utility handles null inputs and parsing failures by returning 0L. ```kotlin import keiyoushi.utils.tryParse // Declare dateFormat at class/file level - creating SimpleDateFormat is expensive: private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ROOT).apply { timeZone = TimeZone.getTimeZone("UTC") } chapter.date_upload = dateFormat.tryParse(dateStr) ``` -------------------------------- ### OkHttp Ignore SSL Errors Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Configure an OkHttpClient.Builder to ignore all SSL errors. This is useful for debugging HTTPS traffic. ```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 = emptyArray() override fun checkClientTrusted(certs: Array, authType: String) = Unit override fun checkServerTrusted(certs: Array, authType: String) = Unit } val insecureSocketFactory = SSLContext.getInstance("TLSv1.2").apply { val trustAllCerts = arrayOf(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() } ``` -------------------------------- ### Define Serializable JSON Models (DTOs) Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use regular `@Serializable` classes instead of `data class` for reduced bytecode. Employ camelCase for properties and `@SerialName` only when JSON keys differ or are invalid Kotlin identifiers. ```kotlin import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable // Bad: Using data class and snake_case variable names @Serializable data class MyDto(val manga_id: Int, val cover_img: String) ``` ```kotlin // Good: Regular class, camelCase variables mapped with @SerialName only when names differ, and private fields @Serializable class MyDto( @SerialName("manga_id") private val mangaId: Int, @SerialName("cover_img") private val coverImg: String, private val title: String, // No @SerialName needed if JSON key is "title" @SerialName("_count") private val count: Int, // Needed for invalid Kotlin identifiers ) { fun toSManga() = SManga.create().apply { url = mangaId.toString() thumbnail_url = coverImg this.title = title } } ``` -------------------------------- ### List with Placeholder Representation Source: https://github.com/keiyoushi/extensions-source/blob/main/core/src/test/resources/reactflight/collections.txt Represents a list containing a placeholder or variable. Useful for templating or dynamic content. ```text [["list","$W4"]] ``` -------------------------------- ### Object with Placeholders Representation Source: https://github.com/keiyoushi/extensions-source/blob/main/core/src/test/resources/reactflight/collections.txt Represents an object with properties defined using placeholders. Suitable for configuration or dynamic data structures. ```text {"items":"$Q1","tags":"$W2","nested":"$Q3"} ``` -------------------------------- ### Use Raw Multi-Dollar String for GraphQL Queries Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md When sending GraphQL requests, utilize Kotlin's raw multi-dollar string interpolation ( $$ """...""" ) to avoid manually escaping '$' symbols in JSON variables. ```kotlin val query = $$ """ query { user(id: "1") { name } } """ ``` -------------------------------- ### AndroidManifest.xml for URL Intent Filter Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Defines the activity and intent filters to capture specific URL schemes and hosts, directing them to the UrlActivity. ```xml ``` -------------------------------- ### Parse Date String to Milliseconds Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use SimpleDateFormat to parse a date string into milliseconds for SChapter.date_upload. Ensure SimpleDateFormat is a class constant or variable. Return 0L if parsing fails to use the default date. ```kotlin import keiyoushi.utils.tryParse chapter.date_upload = dateFormat.tryParse(dateStr) private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH) } ``` -------------------------------- ### String Array Representation Source: https://github.com/keiyoushi/extensions-source/blob/main/core/src/test/resources/reactflight/collections.txt Represents a simple array of strings. Used for lists of text items. ```text ["x","y"] ``` -------------------------------- ### Deserialize JSON using parseAs Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Use `parseAs` for deserializing JSON from various sources like `Response`, `String`, or `InputStream`. It utilizes a pre-configured `jsonInstance` for efficient parsing. ```kotlin import keiyoushi.utils.parseAs // From a Response (uses streaming and consumes the body): val dto = response.parseAs() ``` ```kotlin // From a String: val dto = jsonString.parseAs() ``` ```kotlin // With a transform applied before parsing (e.g., stripping JSONP callbacks): val dto = response.parseAs { it.substringAfter("callback(").dropLast(1) } ``` -------------------------------- ### Array of Pairs Representation Source: https://github.com/keiyoushi/extensions-source/blob/main/core/src/test/resources/reactflight/collections.txt Represents a list of key-value pairs. Useful for mapping or structured data. ```text [["a",1],["b",2]] ``` -------------------------------- ### Next.js Data Extraction Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Extract typed data from Next.js hydration payloads using `extractNextJs` for `Document` or `Response`, or `extractNextJsRsc` for raw RSC strings. ```kotlin import keiyoushi.utils.extractNextJs val data = response.extractNextJs() // Or with an explicit predicate: val data = document.extractNextJs { element -> element is JsonObject && "slug" in element } ``` ```kotlin val data = response.extractNextJsRsc() ``` -------------------------------- ### Serialize Object to JSON String or RequestBody Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Convert Kotlin objects to JSON strings using `toJsonString` or directly to an OkHttp `RequestBody` with `toJsonRequestBody` for API requests. ```kotlin import keiyoushi.utils.toJsonRequestBody import keiyoushi.utils.toJsonString // To a RequestBody for OkHttp (recommended for APIs): val body = myRequestDto.toJsonRequestBody() ``` ```kotlin // To a simple String: val jsonString = myRequestDto.toJsonString() ``` -------------------------------- ### Override Source ID Source: https://github.com/keiyoushi/extensions-source/blob/main/CONTRIBUTING.md Explicitly set the 'id' field to retain the old value when renaming a source. This prevents users from being forced to migrate. ```kotlin override val id: Long = ``` -------------------------------- ### Numeric Array Representation Source: https://github.com/keiyoushi/extensions-source/blob/main/core/src/test/resources/reactflight/collections.txt Represents a simple array of numbers. Commonly used for sequences or data points. ```text [10,20,30] ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.