mirror of
https://github.com/yuzono/anime-extensions.git
synced 2026-10-20 10:51:36 +00:00
Keiyoushi Network Import (Core) (#952)
* Reviews & cleanup Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> * Fix covers for AniList * Fix Miruro ratelimit on host * Lazy client in multi-src class & update playlistClient * Bumping versions * docs: update AGENTS.md and CONTRIBUTING.md for core utilities and versioning details --------- Co-authored-by: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Co-authored-by: Cuong-Tran <16017808+cuong-tran@users.noreply.github.com>
This commit is contained in:
18
AGENTS.md
18
AGENTS.md
@@ -6,14 +6,14 @@
|
||||
|
||||
## Project
|
||||
|
||||
Yuzono Anikku/Aniyomi anime extensions – Kotlin + Jsoup/OkHttp scrapers. Each extension is a Gradle module `src/<lang>/<source>`; shared CMS logic lives as themes in `lib-multisrc/<theme>`; shared helpers in `lib/` and `core/utils` (`keiyoushi.utils`).
|
||||
Yuzono Anikku/Aniyomi anime extensions – Kotlin + Jsoup/OkHttp scrapers. Each extension is a Gradle module `src/<lang>/<source>`; shared CMS logic lives as themes in `lib-multisrc/<theme>`; shared helpers in `lib/` and `core` (`keiyoushi.utils`, `keiyoushi.network`).
|
||||
|
||||
## Commands
|
||||
|
||||
- Build single extension: `./gradlew src:<lang>:<source>:assembleDebug` (e.g. `./gradlew src:en:anizone:assembleDebug`)
|
||||
- Build all: `./gradlew assembleDebug` (avoid – loads every module; prefer loading subset in `settings.gradle.kts`)
|
||||
- Lint/format check: `./gradlew spotlessCheck` (CI) / `./gradlew spotlessApply` (local)
|
||||
- Test/verify: compile via Android Studio before PR (required by `CONTRIBUTING.md#submitting-the-changes` ~ `1309`)
|
||||
- Test/verify: compile via Android Studio before PR (required by `CONTRIBUTING.md#submitting-the-changes` ~ `1369`)
|
||||
|
||||
## Structure
|
||||
|
||||
@@ -21,14 +21,14 @@ Yuzono Anikku/Aniyomi anime extensions – Kotlin + Jsoup/OkHttp scrapers. Each
|
||||
- `src/<lang>/<source>/src/eu/kanade/tachiyomi/animeextension/<lang>/<source>/` – source code (package must match)
|
||||
- `lib-multisrc/<theme>/build.gradle.kts` – theme base (`baseVersionCode`, `alias(kei.plugins.multisrc)`)
|
||||
- `lib-multisrc/<theme>/src/.../multisrc/<theme>/` – abstract theme class `extends AnimeHttpSource`
|
||||
- `lib/` – reusable libs (`lib-cookieinterceptor`, `lib-cryptoaes`, etc.)
|
||||
- `core/utils` – `parseAs`, `toJsonRequestBody`, `tryParse`, `extractNextJs`, `absUrl` – use these, no custom JSON/regex/date helpers
|
||||
- `lib/` – reusable libs (`lib-cryptoaes`, `lib-playlistutils`, extractors, etc.)
|
||||
- `core` – `keiyoushi.utils` (`parseAs`, `toJsonRequestBody`, `tryParse`, `extractNextJs`, `absUrl`) and `keiyoushi.network` (`rateLimit`, `addCookie`, `OkHttpClient.get`/`post`) – use these, no custom JSON/regex/date/interceptor helpers
|
||||
|
||||
## Conventions
|
||||
|
||||
See `CONTRIBUTING.md` for full rules. Critical for agents:
|
||||
|
||||
- Check `lib/` and `core/utils` (`keiyoushi.utils`) first – reuse existing libs/helpers instead of custom boilerplate (`parseAs`/`toJsonRequestBody`/`tryParse`/`extractNextJs`/`absUrl`, `lib-cookieinterceptor`/`lib-cryptoaes`/etc.). See `CONTRIBUTING.md#core-dependencies`.
|
||||
- Check `lib/` and `core` (`keiyoushi.utils` for parsing/prefs, `keiyoushi.network` for clients) first – reuse existing libs/helpers instead of custom boilerplate (`parseAs`/`toJsonRequestBody`/`tryParse`/`extractNextJs`/`absUrl`, `rateLimit`/`addCookie`, `lib-cryptoaes`/etc.). See `CONTRIBUTING.md#core-dependencies`.
|
||||
- Kotlin + Android; web scraping via CSS selectors, OkHttp, Jsoup.
|
||||
- Do not use `data class` for `@Serializable` DTOs unless needed; camelCase fields, `@SerialName` only when JSON key differs.
|
||||
- Use `response.parseAs<T>()`, `response.asJsoup()`, `SimpleDateFormat(...).tryParse()`, `element.absUrl("href")` + `setUrlWithoutDomain()`.
|
||||
@@ -37,7 +37,7 @@ See `CONTRIBUTING.md` for full rules. Critical for agents:
|
||||
|
||||
## Versioning – bump once per PR, theme bump propagates
|
||||
|
||||
Source of truth: `CONTRIBUTING.md:286-308` (individual) + `CONTRIBUTING.md:1061-1117` (themes) + `gradle/build-logic/src/main/kotlin/PluginExtensionLegacy.kt:61`:
|
||||
Source of truth: `CONTRIBUTING.md:287-309` (individual) + `CONTRIBUTING.md:1124-1176` (themes) + `gradle/build-logic/src/main/kotlin/PluginExtensionLegacy.kt:61`:
|
||||
|
||||
```kotlin
|
||||
versionCode = if (theme == null) extVersionCode else theme.baseVersionCode + overrideVersionCode
|
||||
@@ -48,17 +48,17 @@ Rules:
|
||||
- Individual extension (no theme): increment `extVersionCode` by **1** if code affecting users changed. Bump **once per PR** – do not increment multiple times across commits.
|
||||
- Theme (`lib-multisrc/<theme>/build.gradle.kts`): increment `baseVersionCode` by **1** when theme logic changes.
|
||||
- When `baseVersionCode` is bumped, **do not** bump `overrideVersionCode` for extensions using that theme in the same PR – the addition already bumps every extension's effective `versionCode`. Only bump `overrideVersionCode` when the individual extension itself changed independently of the theme.
|
||||
- Checklist mirrors this: `CONTRIBUTING.md#pull-request-checklist` ~ `1316-1317` and `.github/pull_request_template.md:3-4`.
|
||||
- Checklist mirrors this: `CONTRIBUTING.md#pull-request-checklist` ~ `1376-1377` and `.github/pull_request_template.md:3-4`.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Do not change `versionName` manually (generated `14.<versionCode>`).
|
||||
- Do not commit `web_hi_res_512.png` (delete after Icon Generator).
|
||||
- Do not push to `upstream` (`no_pushing` – fork workflow `CONTRIBUTING.md:168-196`). Use `origin` (your fork) for PRs.
|
||||
- Do not push to `upstream` (`no_pushing` – fork workflow `CONTRIBUTING.md:170-196`). Use `origin` (your fork) for PRs.
|
||||
- Never commit secrets, keystore, or `local.properties`.
|
||||
|
||||
## PR Instructions
|
||||
|
||||
- Follow `CONTRIBUTING.md#submitting-the-changes` ~ `1290-1324` checklist; test build in Android Studio.
|
||||
- Follow `CONTRIBUTING.md#submitting-the-changes` ~ `1353-1384` checklist; test build in Android Studio.
|
||||
- Title: keep concise; reference issues (`Closes #xyz`).
|
||||
- One version bump per module per PR as above.
|
||||
|
||||
@@ -29,15 +29,14 @@ or fixing it directly by submitting a Pull Request.
|
||||
- [Adding a lib dependency](#adding-a-lib-dependency)
|
||||
- [i18n library](#i18n-library)
|
||||
- [Creating a new lib](#creating-a-new-lib)
|
||||
- [keiyoushi.utils (core utilities)](#keiyoushiutils-core-utilities)
|
||||
- [Core utilities (keiyoushi.utils and keiyoushi.network)](#core-utilities-keiyoushiutils-and-keiyoushinetwork)
|
||||
- [JSON parsing - `parseAs`](#json-parsing---parseas)
|
||||
- [JSON serialization - `toJsonString` / `toJsonRequestBody`](#json-serialization---tojsonstring--tojsonrequestbody)
|
||||
- [JSON models (DTOs) and serialization](#json-models-dtos-and-serialization)
|
||||
- [Protobuf parsing and serialization - `parseAsProto` / `toRequestBodyProto`](#protobuf-parsing-and-serialization---parseasproto--torequestbodyproto)
|
||||
- [Date parsing - `tryParse` helpers](#date-parsing---tryparse-helpers)
|
||||
- [Date parsing - `tryParse`](#date-parsing---tryparse)
|
||||
- [HTTP requests - `OkHttpClient.get` / `post` / `put` / `head`](#http-requests---okhttpclientget--post--put--head)
|
||||
- [Custom cookies - `addCookie`](#custom-cookies---addcookie)
|
||||
- [WebView execution - `runWebView` / `getLocalStorage`](#webview-execution---runwebview--getlocalstorage)
|
||||
- [Filter helpers - `firstInstance` / `firstInstanceOrNull`](#filter-helpers---firstinstance--firstinstanceornull)
|
||||
- [Next.js data extraction - `extractNextJs` / `extractNextJsRsc`](#nextjs-data-extraction---extractnextjs--extractnextjsrsc)
|
||||
- [Extracting URLs - `setUrlWithoutDomain` + `absUrl`](#extracting-urls---seturlwithoutdomain--absurl)
|
||||
@@ -307,7 +306,7 @@ apply plugin: "kei.plugins.extension.legacy"
|
||||
| `extVersionCode` | The extension version code. This must be a positive integer and incremented with any change to the code. Do not bump for changes that do not affect users, such as changing a private function to a public function. |
|
||||
| `isNsfw` | Flag to indicate that a source contains NSFW content. Should always be set explicitly to either `true` or `false`. Falls back to `false` if not set. |
|
||||
|
||||
The extension's version name is generated automatically by concatenating `16` and `extVersionCode`. With the example used above, the version would be `16.1`.
|
||||
The extension's version name is generated automatically by concatenating `14` and the resolved version code. With the example used above, the version would be `14.1`.
|
||||
|
||||
### Core dependencies
|
||||
|
||||
@@ -425,11 +424,11 @@ dependencies {
|
||||
Place your code in the package `keiyoushi.lib.<mylibname>` or `aniyomi.lib.<mylibname>`. Document public API with KDoc so
|
||||
contributors can understand the lib without needing to read `CONTRIBUTING.md`.
|
||||
|
||||
#### keiyoushi.utils (core utilities)
|
||||
#### Core utilities (keiyoushi.utils and keiyoushi.network)
|
||||
|
||||
The `core/utils` module provides a set of shared extension functions that are available to all extensions
|
||||
The `core` module provides a set of shared extension functions that are available to all extensions
|
||||
without any extra Gradle dependency. Prefer using these helpers instead of implementing your own equivalents, as they provide standardized and maintained solutions.
|
||||
The utilities live in the `keiyoushi.utils` package and are imported individually.
|
||||
Parsing and preference helpers live in the `keiyoushi.utils` package, HTTP client helpers in `keiyoushi.network`, and they are imported individually.
|
||||
|
||||
##### JSON parsing - `parseAs`
|
||||
|
||||
@@ -563,6 +562,61 @@ Two common mistakes to avoid:
|
||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ROOT)
|
||||
```
|
||||
|
||||
##### HTTP requests - `OkHttpClient.get` / `post` / `put` / `head`
|
||||
|
||||
`keiyoushi.network` provides suspend helpers that build the `Request` and await the call in a single
|
||||
step, so you don't have to pair a `GET()`/`POST()` with `client.newCall(...).awaitSuccess()`.
|
||||
|
||||
```kotlin
|
||||
import keiyoushi.network.get
|
||||
import keiyoushi.network.post
|
||||
|
||||
// Inside the source class the headers are taken from the source automatically:
|
||||
val response = client.get("$baseUrl/api/popular")
|
||||
|
||||
// Anywhere else, pass them explicitly:
|
||||
val response = client.get(url, headers)
|
||||
|
||||
// POST and PUT take a RequestBody:
|
||||
val response = client.post("$baseUrl/api/search", searchDto.toJsonRequestBody())
|
||||
```
|
||||
|
||||
Every helper accepts either a `String` or an `HttpUrl`. The call is awaited with `awaitSuccess()` by
|
||||
default, which throws on a non-2xx response; pass `ensureSuccess = false` to get the `Response` back
|
||||
whatever the status code is.
|
||||
|
||||
`get` and `head` also take a `cacheControl` parameter that defaults to a 10 minute `max-age`. Pass
|
||||
your own `CacheControl` when a response should be cached for longer, or not at all.
|
||||
|
||||
##### Custom cookies - `addCookie`
|
||||
|
||||
`keiyoushi.network.addCookie` registers cookies on the client builder. It also writes them to the
|
||||
WebView `CookieManager`, so "Open in WebView" and Cloudflare challenge solving see the same values.
|
||||
|
||||
```kotlin
|
||||
import keiyoushi.network.addCookie
|
||||
|
||||
// Inside the source class the cookies apply to the source's own baseUrl host:
|
||||
override val client = network.client.newBuilder()
|
||||
.addCookie("player_type" to "hls")
|
||||
.build()
|
||||
|
||||
// For any other domain, pass it as a lambda:
|
||||
override val client = network.client.newBuilder()
|
||||
.addCookie({ "cdn.example.org" }, listOf("a" to "1", "b" to "2"))
|
||||
.build()
|
||||
|
||||
// Resolve the values lazily when they depend on a preference or a login:
|
||||
override val client = network.client.newBuilder()
|
||||
.addCookie { listOf("session" to preferences.getString(PREF_TOKEN_KEY, "")!!) }
|
||||
.build()
|
||||
```
|
||||
|
||||
Chain the call once per domain. The first registered domain that matches the request host - exactly
|
||||
or as a subdomain - is the one applied, and only cookies with the same names are replaced: anything
|
||||
else already on the request is preserved. That is why you should never set the `Cookie` header by
|
||||
hand.
|
||||
|
||||
##### Filter helpers - `firstInstance` / `firstInstanceOrNull`
|
||||
|
||||
Use these instead of `filterIsInstance<T>().first()` / `filterIsInstance<T>().firstOrNull()`.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package keiyoushi.network
|
||||
|
||||
import android.webkit.CookieManager
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import keiyoushi.utils.firstInstanceOrNull
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Interceptor
|
||||
@@ -11,7 +11,7 @@ import okhttp3.Response
|
||||
/**
|
||||
* Adds cookies to requests matching the current source [HttpSource.baseUrl].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
fun OkHttpClient.Builder.addCookie(
|
||||
cookies: List<Pair<String, String>>,
|
||||
): OkHttpClient.Builder = addCookie({ source.baseUrl.toHttpUrl().host }, cookies)
|
||||
@@ -19,7 +19,7 @@ fun OkHttpClient.Builder.addCookie(
|
||||
/**
|
||||
* Adds a cookie to requests matching the current source [HttpSource.baseUrl].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
fun OkHttpClient.Builder.addCookie(
|
||||
cookie: Pair<String, String>,
|
||||
): OkHttpClient.Builder = addCookie({ source.baseUrl.toHttpUrl().host }, cookie)
|
||||
@@ -27,7 +27,7 @@ fun OkHttpClient.Builder.addCookie(
|
||||
/**
|
||||
* Adds dynamically resolved cookies to requests matching the current source [HttpSource.baseUrl].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
fun OkHttpClient.Builder.addCookie(
|
||||
cookies: () -> List<Pair<String, String>>,
|
||||
): OkHttpClient.Builder = addCookie({ source.baseUrl.toHttpUrl().host }, cookies)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package keiyoushi.network
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
@@ -72,7 +72,7 @@ suspend fun OkHttpClient.get(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.get(
|
||||
url: HttpUrl,
|
||||
cacheControl: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
@@ -88,7 +88,7 @@ suspend fun OkHttpClient.get(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.get(
|
||||
url: String,
|
||||
cacheControl: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
@@ -149,7 +149,7 @@ suspend fun OkHttpClient.post(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.post(
|
||||
url: HttpUrl,
|
||||
body: RequestBody,
|
||||
@@ -165,7 +165,7 @@ suspend fun OkHttpClient.post(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.post(
|
||||
url: String,
|
||||
body: RequestBody,
|
||||
@@ -226,7 +226,7 @@ suspend fun OkHttpClient.put(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.put(
|
||||
url: String,
|
||||
body: RequestBody,
|
||||
@@ -242,7 +242,7 @@ suspend fun OkHttpClient.put(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.put(
|
||||
url: HttpUrl,
|
||||
body: RequestBody,
|
||||
@@ -304,7 +304,7 @@ suspend fun OkHttpClient.head(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.head(
|
||||
url: String,
|
||||
cacheControl: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
@@ -320,7 +320,7 @@ suspend fun OkHttpClient.head(
|
||||
* @param ensureSuccess If true, throws an exception if the response code is not 2xx.
|
||||
* @return The HTTP [Response].
|
||||
*/
|
||||
context(source: HttpSource)
|
||||
context(source: AnimeHttpSource)
|
||||
suspend fun OkHttpClient.head(
|
||||
url: HttpUrl,
|
||||
cacheControl: CacheControl = DEFAULT_CACHE_CONTROL,
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
alias(kei.plugins.multisrc)
|
||||
}
|
||||
|
||||
baseVersionCode = 7
|
||||
baseVersionCode = 8
|
||||
dependencies {
|
||||
implementation(project(":lib:playlistutils"))
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ import eu.kanade.tachiyomi.multisrc.anikototheme.AnikotoThemeFilters.addQueryPar
|
||||
import eu.kanade.tachiyomi.multisrc.anikototheme.dto.ResultResponse
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.get
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.LazyMutable
|
||||
import keiyoushi.utils.delegate
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
@@ -31,7 +31,6 @@ import keiyoushi.utils.useAsJsoup
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Protocol
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
@@ -66,9 +65,9 @@ abstract class AnikotoTheme(
|
||||
if (value == baseUrl) return
|
||||
preferences.edit().putString(PREF_DOMAIN_KEY, value).apply()
|
||||
docHeaders = headersBuilder().build()
|
||||
client = network.client.newBuilder()
|
||||
.rateLimit(rateLimit)
|
||||
.build()
|
||||
client = buildClient()
|
||||
playlistClient = buildPlaylistClient()
|
||||
playlistUtils = PlaylistUtils(playlistClient, headers)
|
||||
}
|
||||
|
||||
private val domainValues = domainEntries.map { "https://$it" }
|
||||
@@ -91,20 +90,23 @@ abstract class AnikotoTheme(
|
||||
|
||||
protected var docHeaders by LazyMutable { headersBuilder().build() }
|
||||
|
||||
override var client: OkHttpClient by LazyMutable {
|
||||
network.client.newBuilder()
|
||||
.rateLimit(rateLimit)
|
||||
.build()
|
||||
}
|
||||
// Don't eagerly initialize client in multi-src class since subclass overwrite rateLimit will get 0 instead
|
||||
override var client by LazyMutable { buildClient() }
|
||||
|
||||
internal val playlistClient by lazy {
|
||||
client.newBuilder()
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.protocols(listOf(Protocol.HTTP_1_1))
|
||||
.build()
|
||||
}
|
||||
// Don't eagerly initialize client in multi-src class since subclass overwrite rateLimit will get 0 instead
|
||||
// Derived from [client], so both are rebuilt by the [baseUrl] setter on a domain change
|
||||
internal var playlistClient by LazyMutable { buildPlaylistClient() }
|
||||
|
||||
internal val playlistUtils by lazy { PlaylistUtils(playlistClient, headers) }
|
||||
internal var playlistUtils by LazyMutable { PlaylistUtils(playlistClient, headers) }
|
||||
|
||||
private fun buildClient() = network.client.newBuilder()
|
||||
.rateLimit(rateLimit)
|
||||
.build()
|
||||
|
||||
private fun buildPlaylistClient() = client.newBuilder()
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.protocols(listOf(Protocol.HTTP_1_1))
|
||||
.build()
|
||||
|
||||
private val extractors by lazy { AnikotoExtractor(this) }
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
alias(kei.plugins.multisrc)
|
||||
}
|
||||
|
||||
baseVersionCode = 5
|
||||
baseVersionCode = 6
|
||||
|
||||
dependencies {
|
||||
api(project(":lib:megaupextractor"))
|
||||
|
||||
@@ -19,8 +19,8 @@ import eu.kanade.tachiyomi.multisrc.animekaitheme.dto.VideoData
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.LazyMutable
|
||||
import keiyoushi.utils.ParsedAnimeHttpLegacySource
|
||||
import keiyoushi.utils.addListPreference
|
||||
@@ -45,7 +45,6 @@ import org.jsoup.nodes.Element
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
|
||||
abstract class AnimeKaiTheme(
|
||||
@@ -72,10 +71,10 @@ abstract class AnimeKaiTheme(
|
||||
|
||||
protected var docHeaders by LazyMutable { headersBuilder().build() }
|
||||
|
||||
// Go back to deprecated rate limit method, as apps e.g. Dantotsu do not support kotlin.time yet
|
||||
// Don't eagerly initialize client in multi-src class since subclass overwrite rateLimit will get 0 instead
|
||||
override var client: OkHttpClient by LazyMutable {
|
||||
network.client.newBuilder()
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), permits = rateLimit, period = 1L, unit = TimeUnit.SECONDS)
|
||||
.rateLimit(rateLimit)
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -361,7 +360,7 @@ abstract class AnimeKaiTheme(
|
||||
|
||||
protected open fun updateDomainConfig() {
|
||||
client = network.client.newBuilder()
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), permits = rateLimit, period = 1L, unit = TimeUnit.SECONDS)
|
||||
.rateLimit(rateLimit)
|
||||
.build()
|
||||
docHeaders = headersBuilder().build()
|
||||
megaUpExtractor = MegaUpExtractor(client, docHeaders, context)
|
||||
|
||||
@@ -2,7 +2,7 @@ ext {
|
||||
extName = 'AniZone'
|
||||
extClass = '.AniZone'
|
||||
libVersion = 16
|
||||
extVersionCode = 13
|
||||
extVersionCode = 14
|
||||
isNsfw = false
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import aniyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.animeextension.all.anizone.AniZone.Companion.PREF_TITLE_LANG_KEY
|
||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilter
|
||||
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
|
||||
@@ -21,8 +20,8 @@ import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.firstInstance
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'Anikage'
|
||||
extClass = '.Anikage'
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.animesource.model.Track
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.addListPreference
|
||||
import keiyoushi.utils.addSwitchPreference
|
||||
@@ -33,7 +33,6 @@ import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import kotlin.collections.associate
|
||||
import kotlin.collections.isNotEmpty
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class Anikage :
|
||||
AnimeHttpLegacySource(),
|
||||
@@ -54,7 +53,7 @@ class Anikage :
|
||||
.set("Referer", "$baseUrl/")
|
||||
|
||||
override val client = network.client.newBuilder()
|
||||
.rateLimitHost(baseUrl.toHttpUrl(), 3, 1.seconds)
|
||||
.rateLimit(3)
|
||||
.build()
|
||||
|
||||
private val preferences by getPreferencesLazy()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'AniList'
|
||||
extClass = '.AniList'
|
||||
extVersionCode = 12
|
||||
extVersionCode = 13
|
||||
}
|
||||
|
||||
apply plugin: "kei.plugins.extension.legacy"
|
||||
|
||||
@@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
@@ -25,13 +25,11 @@ import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import kotlinx.serialization.json.putJsonArray
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class AniList :
|
||||
AnimeHttpLegacySource(),
|
||||
@@ -48,7 +46,7 @@ class AniList :
|
||||
override val supportsLatest = true
|
||||
|
||||
override val client = network.client.newBuilder()
|
||||
.rateLimitHost("https://api.jikan.moe".toHttpUrl(), 1, 1L, TimeUnit.SECONDS)
|
||||
.rateLimit(1) { it.host == "api.tenrai.org" }
|
||||
.build()
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
@@ -324,7 +322,7 @@ class AniList :
|
||||
|
||||
private fun getSingleEpisodeFromMal(malId: Int): List<SEpisode> {
|
||||
val animeData = client.newCall(
|
||||
GET("https://api.jikan.moe/v4/anime/$malId", headers),
|
||||
GET("https://api.tenrai.org/v1/anime/$malId", headers),
|
||||
).execute().parseAs<JikanAnimeDto>().data
|
||||
|
||||
return listOf(
|
||||
@@ -345,7 +343,7 @@ class AniList :
|
||||
var page = 1
|
||||
while (hasNextPage) {
|
||||
val data = client.newCall(
|
||||
GET("https://api.jikan.moe/v4/anime/$malId/episodes?page=$page", headers),
|
||||
GET("https://api.tenrai.org/v1/anime/$malId/episodes?page=$page", headers),
|
||||
).execute().parseAs<JikanEpisodesDto>()
|
||||
|
||||
if (data.pagination.lastPage == 1 && data.data.isEmpty()) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import okhttp3.OkHttpClient
|
||||
class CoverProviders(private val client: OkHttpClient, private val headers: Headers) {
|
||||
fun getMALCovers(malId: String): List<String> {
|
||||
return client.newCall(
|
||||
GET("https://api.jikan.moe/v4/anime/$malId/pictures", headers),
|
||||
GET("https://api.tenrai.org/v1/anime/$malId/pictures", headers),
|
||||
).execute().use { response ->
|
||||
if (!response.isSuccessful) return@use emptyList()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ ext {
|
||||
extName = 'Miruro.tv'
|
||||
extClass = '.Miruro'
|
||||
isNsfw = true
|
||||
extVersionCode = 10
|
||||
extVersionCode = 11
|
||||
}
|
||||
|
||||
apply plugin: "kei.plugins.extension.legacy"
|
||||
|
||||
@@ -26,7 +26,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.await
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimitHost
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.LazyMutable
|
||||
import keiyoushi.utils.addListPreference
|
||||
@@ -835,6 +835,7 @@ class Miruro :
|
||||
private val PREF_MIRROR_DEFAULT = DEFAULT_MIRROR_VALUES.first()
|
||||
private const val PREF_CACHED_MIRRORS_KEY = "cached_mirrors_json"
|
||||
private const val STATUS_PAGE_API_URL = "https://status.miruro.com/api"
|
||||
private val STATUS_PAGE_API_HOST = STATUS_PAGE_API_URL.toHttpUrl().host
|
||||
|
||||
private val BR_REGEX = Regex("<br\\s*/?>", RegexOption.IGNORE_CASE)
|
||||
private val CLOSE_P_REGEX = Regex("</p>", RegexOption.IGNORE_CASE)
|
||||
@@ -845,8 +846,8 @@ class Miruro :
|
||||
val SUB_TYPE_DISPLAY_ORDER = listOf("sub", "dub", "ssub", "h-sub")
|
||||
}
|
||||
|
||||
private val statusPageClient: OkHttpClient = network.client.newBuilder()
|
||||
.rateLimitHost("$STATUS_PAGE_API_URL/".toHttpUrl(), permits = 1, period = 2.seconds)
|
||||
private val statusPageClient = network.client.newBuilder()
|
||||
.rateLimit(1, 2.seconds) { it.host == STATUS_PAGE_API_HOST }
|
||||
.build()
|
||||
|
||||
// ============================== Pipe Search ==============================
|
||||
|
||||
@@ -2,7 +2,7 @@ ext {
|
||||
extName = 'Re:ANIME'
|
||||
extClass = '.ReAnime'
|
||||
libVersion = 16
|
||||
extVersionCode = 6
|
||||
extVersionCode = 7
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.addListPreference
|
||||
import keiyoushi.utils.get
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
@@ -40,7 +40,6 @@ import kotlinx.serialization.json.jsonPrimitive
|
||||
import kotlinx.serialization.json.putJsonObject
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.nanohttpd.protocols.http.NanoHTTPD
|
||||
@@ -106,11 +105,9 @@ class ReAnime :
|
||||
.add("Sec-Fetch-Site", "same-origin")
|
||||
.build()
|
||||
|
||||
override val client: OkHttpClient by lazy {
|
||||
network.client.newBuilder()
|
||||
.rateLimit(5)
|
||||
.build()
|
||||
}
|
||||
override val client = network.client.newBuilder()
|
||||
.rateLimit(5)
|
||||
.build()
|
||||
|
||||
private val playlistUtils by lazy { PlaylistUtils(network.client, headers) }
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'XAnime'
|
||||
extClass = '.XAnime'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
isNsfw = true
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Track
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.addListPreference
|
||||
import keiyoushi.utils.addSetPreference
|
||||
@@ -42,7 +42,6 @@ import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
@@ -57,9 +56,9 @@ class XAnime :
|
||||
|
||||
override val baseUrl: String get() = preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN)!!
|
||||
|
||||
private val cryptoClient: OkHttpClient = client.newBuilder()
|
||||
.rateLimit(5)
|
||||
private val cryptoClient = client.newBuilder()
|
||||
.addInterceptor(Crypto())
|
||||
.rateLimit(5)
|
||||
.build()
|
||||
|
||||
private val api = Queries(cryptoClient, { baseUrl }, headers)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
ext {
|
||||
extName = 'Fun Anime TV'
|
||||
extClass = '.FunAnimeTV'
|
||||
extVersionCode = 1
|
||||
extVersionCode = 2
|
||||
}
|
||||
|
||||
apply plugin: "kei.plugins.extension.legacy"
|
||||
|
||||
@@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
|
||||
import eu.kanade.tachiyomi.animesource.model.SEpisode
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.network.interceptor.rateLimit
|
||||
import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
@@ -30,7 +30,6 @@ import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.Response
|
||||
import java.security.MessageDigest
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class FunAnimeTV :
|
||||
AnimeHttpLegacySource(),
|
||||
@@ -54,12 +53,10 @@ class FunAnimeTV :
|
||||
set("User-Agent", "Dalvik/2.1.0 (Linux; U; Android 16; M2007J20CG Build/BP3A.250905.014)")
|
||||
}
|
||||
|
||||
override val client by lazy {
|
||||
network.client.newBuilder()
|
||||
.rateLimit(5, 1.seconds)
|
||||
.retryOnConnectionFailure(true)
|
||||
.build()
|
||||
}
|
||||
override val client = network.client.newBuilder()
|
||||
.rateLimit(5)
|
||||
.retryOnConnectionFailure(true)
|
||||
.build()
|
||||
|
||||
// ============================== Popular ===============================
|
||||
override fun popularAnimeRequest(page: Int): Request {
|
||||
|
||||
Reference in New Issue
Block a user