diff --git a/.editorconfig b/.editorconfig index 79a8309183..ebf0322a0d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,8 @@ ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_name_count_to_use_star_import = 2147483647 ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 + +[*.properties] +charset = utf-8 +end_of_line = lf +insert_final_newline = true diff --git a/.github/scripts/create-repo.sh b/.github/scripts/create-repo.sh index ca905c2ff4..9bb13b3409 100755 --- a/.github/scripts/create-repo.sh +++ b/.github/scripts/create-repo.sh @@ -30,7 +30,10 @@ for APK in ${APKS[@]}; do LANG=$(echo $APK | grep -Po "tachiyomi-\K[^\.]+") ICON=$(echo "$BADGING" | grep -Po "application-icon-320.*'\K[^']+") - unzip -p $APK $ICON > icon/${FILENAME%.*}.png + unzip -p $APK $ICON > icon/${PKGNAME}.png + + # TODO: legacy icons; remove after a while + cp icon/${PKGNAME}.png icon/${FILENAME%.*}.png SOURCE_INFO=$(jq ".[\"$PKGNAME\"]" < ../output.json) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index d72c89c7ab..77cbcb99f2 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: CI_MODULE_GEN: true steps: - name: Clone repo - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 @@ -105,7 +105,7 @@ jobs: matrix: ${{ fromJSON(needs.prepare.outputs.multisrcMatrix) }} steps: - name: Checkout PR - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Set up JDK uses: actions/setup-java@v3 @@ -139,7 +139,7 @@ jobs: matrix: ${{ fromJSON(needs.prepare.outputs.individualMatrix) }} steps: - name: Checkout PR - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Set up JDK uses: actions/setup-java@v3 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 085e57ee59..5f15f39ada 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -28,7 +28,7 @@ jobs: CI_MODULE_GEN: true steps: - name: Clone repo - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@v1 @@ -72,7 +72,7 @@ jobs: matrix: ${{ fromJSON(needs.prepare.outputs.multisrcMatrix) }} steps: - name: Checkout master branch - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Set up JDK uses: actions/setup-java@v3 @@ -120,7 +120,7 @@ jobs: matrix: ${{ fromJSON(needs.prepare.outputs.individualMatrix) }} steps: - name: Checkout master branch - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 - name: Set up JDK uses: actions/setup-java@v3 @@ -169,11 +169,11 @@ jobs: - name: Set up JDK uses: actions/setup-java@v3 with: - java-version: 11 + java-version: 17 distribution: adopt - name: Checkout master branch - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 with: ref: master path: master @@ -187,7 +187,7 @@ jobs: java -jar ./Inspector.jar "apk" "output.json" "tmp" ./.github/scripts/create-repo.sh - name: Checkout repo branch - uses: actions/checkout@v3 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4 with: ref: repo path: repo diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index 299349cc62..a08bc857f4 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -43,17 +43,17 @@ jobs: }, { "type": "both", - "regex": ".*(hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|manhwahot|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|colamanhua|mangadig|hitomi\\.la|copymanga|neox|1manga\\.co|mangafox\\.fun|mangahere\\.onl|mangakakalot\\.fun|manganel(?!o)|mangaonline\\.fun|mangatoday|manga\\.town|onemanga\\.info|koushoku|ksk\\.moe|comikey|leercapitulo|c[uứ]u\\s*truy[eệ]n|reaper\\s*scans).*", + "regex": ".*(hq\\s*dragon|manga\\s*host|supermangas|superhentais|union\\s*mangas|yes\\s*mangas|manhuascan|manhwahot|tsuki\\s*mangas|manga\\s*yabu|mangas\\.in|mangas\\.pw|hentaikai|toptoon\\+?|colamanhua|mangadig|hitomi\\.la|copymanga|neox|1manga\\.co|mangafox\\.fun|mangahere\\.onl|mangakakalot\\.fun|manganel(?!o)|mangaonline\\.fun|mangatoday|manga\\.town|onemanga\\.info|koushoku|ksk\\.moe|comikey|leercapitulo|c[uứ]u\\s*truy[eệ]n|day\\s*comics?|reaper\\s*scans|constellar\\s*scans|mode\\s*scanlator|bakai|japscan).*", "ignoreCase": true, "labels": ["invalid"], - "message": "{match} will not be added back as it is too difficult to maintain. Read #3475 for more information." + "message": "{match} will not be added back as it is too difficult to maintain. Read [this](https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master/REMOVED_SOURCES.md) for more information." }, { "type": "both", - "regex": ".*(komiktap|gourmet\\s*scans|mangawow|mangagegecesi|knightnoscanlations|ahstudios|mangagecesi|nartag|xxx\\s*yaoi|yaoi\\s*fan\\s*clube|luminous|dragontea|manhwaid\\.org|reset(?:\\s*|-)scan|manga-flix\\.com|astra\\s*scans|manganoon|manga(?:-|\\s*)pro|coven\\s*scans?|shinobiscans|plot ?twist ?no ?fansub(?: ?scans?)?|plot-twistnf-scans(?:\\.com)?|mhscans|realm ?scans?).*", + "regex": ".*(komiktap|gourmet\\s*scans|mangawow|knightnoscanlations|mangasy|nartag|xxx\\s*yaoi|luminous|hunters\\s*scan|reset(?:\\s*|-)scan|astra\\s*scans|manga(?:-|\\s*)pro|shinobiscans|plot ?twist ?no ?fansub(?: ?scans?)?|plot-twistnf-scans(?:\\.com)?|mhscans|aresmanga|realm ?scans?|mono ?manga).*", "ignoreCase": true, "labels": ["invalid"], - "message": "{match} will not be added back as the scanlator team has requested it to be removed. Read #3475 for more information." + "message": "{match} will not be added back as the scanlator team has requested it to be removed. Read [this](https://github.com/tachiyomiorg/tachiyomi-extensions/blob/master/REMOVED_SOURCES.md) for more information." }, { "type": "both", diff --git a/.run/FlixScansGenerator.run.xml b/.run/FlixScansGenerator.run.xml new file mode 100644 index 0000000000..bbb4ce682e --- /dev/null +++ b/.run/FlixScansGenerator.run.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3e1db9c0a..e4f6f65168 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -257,6 +257,16 @@ dependencies { } ``` +#### i18n library + +[`lib-i18n`](https://github.com/tachiyomiorg/tachiyomi-extensions/tree/master/lib/i18n) is a library for handling internationalization in the sources. It allows loading `.properties` files with messages located under the `assets/i18n` folder of each extension, that can be used to translate strings under the source. + +```gradle +dependencies { + implementation(project(':lib-i18n')) +} +``` + #### Additional dependencies If you find yourself needing additional functionality, you can add more dependencies to your `build.gradle` file. @@ -297,7 +307,7 @@ a.k.a. the Browse source entry point in the app (invoked by tapping on the sourc - The app calls `fetchPopularManga` which should return a `MangasPage` containing the first batch of found `SManga` entries. - 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 `MangasPage.hasNextPage` is passed as `true` and `MangasPage.mangas` is not empty. - To show the list properly, the app needs `url`, `title` and `thumbnail_url`. You **must** set them here. The rest of the fields could be filled later (refer to Manga Details below). - - You should set `thumbnail_url` if is available, if not, `fetchMangaDetails` will be **immediately** called (this will increase network calls heavily and should be avoided). + - You should set `thumbnail_url` if is available, if not, `getMangaDetails` will be **immediately** called (this will increase network calls heavily and should be avoided). #### Latest Manga @@ -314,7 +324,7 @@ a.k.a. the Latest source entry point in the app (invoked by tapping on the "Late ##### Filters -The search flow have support to filters that can be added to a `FilterList` inside the `getFilterList` method. When the user changes the filters' state, they will be passed to the `searchRequest`, and they can be iterated to create the request (by getting the `filter.state` value, where the type varies depending on the `Filter` used). You can check the filter types available [here](https://github.com/tachiyomiorg/tachiyomi/blob/master/app/src/main/java/eu/kanade/tachiyomi/source/model/Filter.kt) and in the table below. +The search flow have support to filters that can be added to a `FilterList` inside the `getFilterList` method. When the user changes the filters' state, they will be passed to the `searchRequest`, and they can be iterated to create the request (by getting the `filter.state` value, where the type varies depending on the `Filter` used). You can check the filter types available [here](https://github.com/tachiyomiorg/tachiyomi/blob/master/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/model/Filter.kt) and in the table below. | Filter | State type | Description | | ------ | ---------- | ----------- | @@ -340,15 +350,15 @@ open class UriPartFilter(displayName: String, private val vals: Array - + diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 90524827b0..d3e951c3e0 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -9,6 +9,8 @@ android { minSdk = AndroidConfig.minSdk } + namespace = "eu.kanade.tachiyomi.extension" + sourceSets { named("main") { manifest.srcFile("AndroidManifest.xml") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bb80878998..88100fa5ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ coroutines_version = "1.6.4" serialization_version = "1.4.0" [libraries] -gradle-agp = { module = "com.android.tools.build:gradle", version = "7.4.1" } +gradle-agp = { module = "com.android.tools.build:gradle", version = "7.4.2" } gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin_version" } gradle-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlin_version" } gradle-kotlinter = { module = "org.jmailen.gradle:kotlinter-gradle", version = "3.13.0" } @@ -19,7 +19,6 @@ coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", ve coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines_version" } injekt-core = { module = "com.github.inorichi.injekt:injekt-core", version = "65b0440" } -rxandroid = { module = "io.reactivex:rxandroid", version = "1.2.1" } rxjava = { module = "io.reactivex:rxjava", version = "1.3.8" } jsoup = { module = "org.jsoup:jsoup", version = "1.15.1" } okhttp = { module = "com.squareup.okhttp3:okhttp", version = "5.0.0-alpha.11" } @@ -27,4 +26,3 @@ quickjs = { module = "app.cash.quickjs:quickjs-android", version = "0.9.2" } [bundles] common = ["kotlin-stdlib", "coroutines-core", "coroutines-android", "injekt-core", "rxjava", "kotlin-protobuf", "kotlin-json", "jsoup", "okhttp", "tachiyomi-lib", "quickjs"] -reactivex = ["rxandroid"] diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2a..943f0cbfa7 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb702f0..ac72c34e8a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787337..65dcd68d65 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index ac1b06f938..6689b85bee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/lib/cryptoaes/build.gradle.kts b/lib/cryptoaes/build.gradle.kts index 5eaa29645e..caabcb291e 100644 --- a/lib/cryptoaes/build.gradle.kts +++ b/lib/cryptoaes/build.gradle.kts @@ -10,6 +10,8 @@ android { minSdk = AndroidConfig.minSdk targetSdk = AndroidConfig.targetSdk } + + namespace = "eu.kanade.tachiyomi.lib.cryptoaes" } repositories { diff --git a/lib/cryptoaes/src/main/AndroidManifest.xml b/lib/cryptoaes/src/main/AndroidManifest.xml deleted file mode 100644 index 1ac16ea735..0000000000 --- a/lib/cryptoaes/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/lib/dataimage/build.gradle.kts b/lib/dataimage/build.gradle.kts index d02fbec564..d5fcea1dfa 100644 --- a/lib/dataimage/build.gradle.kts +++ b/lib/dataimage/build.gradle.kts @@ -10,6 +10,8 @@ android { minSdk = AndroidConfig.minSdk targetSdk = AndroidConfig.targetSdk } + + namespace = "eu.kanade.tachiyomi.lib.dataimage" } repositories { diff --git a/lib/dataimage/src/main/AndroidManifest.xml b/lib/dataimage/src/main/AndroidManifest.xml deleted file mode 100644 index 11d9e3ff5b..0000000000 --- a/lib/dataimage/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/lib/i18n/build.gradle.kts b/lib/i18n/build.gradle.kts new file mode 100644 index 0000000000..4a741aa0a5 --- /dev/null +++ b/lib/i18n/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + id("com.android.library") + kotlin("android") +} + +android { + compileSdk = AndroidConfig.compileSdk + + defaultConfig { + minSdk = AndroidConfig.minSdk + targetSdk = AndroidConfig.targetSdk + } + + namespace = "eu.kanade.tachiyomi.lib.i18n" +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(libs.kotlin.stdlib) +} diff --git a/lib/i18n/src/main/java/eu/kanade/tachiyomi/lib/i18n/Intl.kt b/lib/i18n/src/main/java/eu/kanade/tachiyomi/lib/i18n/Intl.kt new file mode 100644 index 0000000000..7899da8cae --- /dev/null +++ b/lib/i18n/src/main/java/eu/kanade/tachiyomi/lib/i18n/Intl.kt @@ -0,0 +1,89 @@ +package eu.kanade.tachiyomi.lib.i18n + +import java.io.InputStreamReader +import java.text.Collator +import java.util.Locale +import java.util.PropertyResourceBundle + +/** + * A simple wrapper to make internationalization easier to use in sources. + * + * Message files should be put in the `assets/i18n` folder, with the name + * `messages_{iso_639_1}.properties`, where `iso_639_1` should be using + * snake case and be in lowercase. + * + * To edit the strings, use the official JetBrain's + * [Resource Bundle Editor plugin](https://plugins.jetbrains.com/plugin/17035-resource-bundle-editor). + * + * Make sure to configure Android Studio to save Properties files as UTF-8 as well. + * You can refer to this [documentation](https://www.jetbrains.com/help/idea/properties-files.html#1cbc434e) + * on how to do so. + */ +class Intl( + private val language: String, + private val baseLanguage: String, + private val availableLanguages: Set, + private val classLoader: ClassLoader, + private val createMessageFileName: (String) -> String = { createDefaultMessageFileName(it) } +) { + + val chosenLanguage: String = when (language) { + in availableLanguages -> language + else -> baseLanguage + } + + private val locale: Locale = Locale.forLanguageTag(chosenLanguage) + + val collator: Collator = Collator.getInstance(locale) + + private val baseBundle: PropertyResourceBundle by lazy { createBundle(baseLanguage) } + + private val bundle: PropertyResourceBundle by lazy { + if (chosenLanguage == baseLanguage) baseBundle else createBundle(chosenLanguage) + } + + /** + * Returns the string from the message file. If the [key] is not present + * in the current language, the English value will be returned. If the [key] + * is also not present in English, the [key] surrounded by brackets will be returned. + */ + operator fun get(key: String): String = when { + bundle.containsKey(key) -> bundle.getString(key) + baseBundle.containsKey(key) -> baseBundle.getString(key) + else -> "[$key]" + } + + /** + * Uses the string as a format string and returns a string obtained by + * substituting the specified arguments, using the instance locale. + */ + fun format(key: String, vararg args: Any?) = get(key).format(locale, *args) + + fun languageDisplayName(localeCode: String): String = + Locale.forLanguageTag(localeCode) + .getDisplayName(locale) + .replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() } + + /** + * Creates a [PropertyResourceBundle] instance from the language specified. + * The expected message file will be loaded from the `res/raw`. + * + * The [PropertyResourceBundle] is used directly instead of [java.util.ResourceBundle] + * because the later has issues with UTF-8 files in Java 8, which would need + * the message files to be saved in ISO-8859-1, making the file readability bad. + */ + private fun createBundle(lang: String): PropertyResourceBundle { + val fileName = createMessageFileName(lang) + val fileContent = classLoader.getResourceAsStream(fileName) + + return PropertyResourceBundle(InputStreamReader(fileContent, "UTF-8")) + } + + companion object { + fun createDefaultMessageFileName(lang: String): String { + val langSnakeCase = lang.replace("-", "_").lowercase() + + return "assets/i18n/messages_$langSnakeCase.properties" + } + } +} diff --git a/lib/randomua/build.gradle.kts b/lib/randomua/build.gradle.kts new file mode 100644 index 0000000000..39942da283 --- /dev/null +++ b/lib/randomua/build.gradle.kts @@ -0,0 +1,24 @@ +plugins { + id("com.android.library") + kotlin("android") + id("kotlinx-serialization") +} + +android { + compileSdk = AndroidConfig.compileSdk + + defaultConfig { + minSdk = AndroidConfig.minSdk + targetSdk = AndroidConfig.targetSdk + } + + namespace = "eu.kanade.tachiyomi.lib.randomua" +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(libs.bundles.common) +} diff --git a/lib/randomua/src/main/java/eu/kanade/tachiyomi/lib/randomua/RandomUserAgentInterceptor.kt b/lib/randomua/src/main/java/eu/kanade/tachiyomi/lib/randomua/RandomUserAgentInterceptor.kt new file mode 100644 index 0000000000..0d36add8af --- /dev/null +++ b/lib/randomua/src/main/java/eu/kanade/tachiyomi/lib/randomua/RandomUserAgentInterceptor.kt @@ -0,0 +1,121 @@ +package eu.kanade.tachiyomi.lib.randomua + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.NetworkHelper +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Response +import uy.kohesive.injekt.injectLazy +import java.io.IOException + +private class RandomUserAgentInterceptor( + private val userAgentType: UserAgentType, + private val customUA: String?, + private val filterInclude: List, + private val filterExclude: List, +) : Interceptor { + + private var userAgent: String? = null + + private val json: Json by injectLazy() + + private val network: NetworkHelper by injectLazy() + + private val client = network.client + + override fun intercept(chain: Interceptor.Chain): Response { + try { + val originalRequest = chain.request() + + val newUserAgent = getUserAgent() + ?: return chain.proceed(originalRequest) + + val originalHeaders = originalRequest.headers + + val modifiedHeaders = originalHeaders.newBuilder() + .set("User-Agent", newUserAgent) + .build() + + return chain.proceed( + originalRequest.newBuilder() + .headers(modifiedHeaders) + .build() + ) + } catch (e: Exception) { + throw IOException(e.message) + } + } + + private fun getUserAgent(): String? { + if (userAgentType == UserAgentType.OFF) { + return customUA?.ifBlank { null } + } + + if (!userAgent.isNullOrEmpty()) return userAgent + + val uaResponse = client.newCall(GET(UA_DB_URL)).execute() + + if (!uaResponse.isSuccessful) { + uaResponse.close() + return null + } + + val userAgentList = uaResponse.use { json.decodeFromString(it.body.string()) } + + return when (userAgentType) { + UserAgentType.DESKTOP -> userAgentList.desktop + UserAgentType.MOBILE -> userAgentList.mobile + else -> error("Expected UserAgentType.DESKTOP or UserAgentType.MOBILE but got UserAgentType.${userAgentType.name} instead") + } + .filter { + filterInclude.isEmpty() || filterInclude.any { filter -> + it.contains(filter, ignoreCase = true) + } + } + .filterNot { + filterExclude.any { filter -> + it.contains(filter, ignoreCase = true) + } + } + .randomOrNull() + .also { userAgent = it } + } + + companion object { + private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json" + } +} + +/** + * Helper function to add a latest random user agent interceptor. + * The interceptor will added at the first position in the chain, + * so the CloudflareInterceptor in the app will be able to make usage of it. + * + * @param userAgentType User Agent type one of (DESKTOP, MOBILE, OFF) + * @param customUA Optional custom user agent used when userAgentType is OFF + * @param filterInclude Filter to only include User Agents containing these strings + * @param filterExclude Filter to exclude User Agents containing these strings + */ +fun OkHttpClient.Builder.setRandomUserAgent( + userAgentType: UserAgentType, + customUA: String? = null, + filterInclude: List = emptyList(), + filterExclude: List = emptyList(), +) = apply { + interceptors().add(0, RandomUserAgentInterceptor(userAgentType, customUA, filterInclude, filterExclude)) +} + +enum class UserAgentType { + MOBILE, + DESKTOP, + OFF +} + +@Serializable +private data class UserAgentList( + val desktop: List, + val mobile: List +) diff --git a/lib/randomua/src/main/java/eu/kanade/tachiyomi/lib/randomua/RandomUserAgentPreference.kt b/lib/randomua/src/main/java/eu/kanade/tachiyomi/lib/randomua/RandomUserAgentPreference.kt new file mode 100644 index 0000000000..a92e67fa95 --- /dev/null +++ b/lib/randomua/src/main/java/eu/kanade/tachiyomi/lib/randomua/RandomUserAgentPreference.kt @@ -0,0 +1,70 @@ +package eu.kanade.tachiyomi.lib.randomua + +import android.content.SharedPreferences +import android.widget.Toast +import androidx.preference.EditTextPreference +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import okhttp3.Headers + + + /** + * Helper function to return UserAgentType based on SharedPreference value + */ +fun SharedPreferences.getPrefUAType(): UserAgentType { + return when (getString(PREF_KEY_RANDOM_UA, "off")) { + "mobile" -> UserAgentType.MOBILE + "desktop" -> UserAgentType.DESKTOP + else -> UserAgentType.OFF + } +} + +/** + * Helper function to return custom UserAgent from SharedPreference + */ +fun SharedPreferences.getPrefCustomUA(): String? { + return getString(PREF_KEY_CUSTOM_UA, null) +} + +/** + * Helper function to add Random User-Agent settings to SharedPreference + * + * @param screen, PreferenceScreen from `setupPreferenceScreen` + */ +fun addRandomUAPreferenceToScreen( + screen: PreferenceScreen, +) { + ListPreference(screen.context).apply { + key = PREF_KEY_RANDOM_UA + title = TITLE_RANDOM_UA + entries = RANDOM_UA_ENTRIES + entryValues = RANDOM_UA_VALUES + summary = "%s" + setDefaultValue("off") + }.also(screen::addPreference) + + EditTextPreference(screen.context).apply { + key = PREF_KEY_CUSTOM_UA + title = TITLE_CUSTOM_UA + summary = CUSTOM_UA_SUMMARY + setOnPreferenceChangeListener { _, newValue -> + try { + Headers.Builder().add("User-Agent", newValue as String).build() + true + } catch (e: IllegalArgumentException) { + Toast.makeText(screen.context, "User Agent invalid:${e.message}", Toast.LENGTH_LONG).show() + false + } + } + }.also(screen::addPreference) +} + +const val TITLE_RANDOM_UA = "Random User-Agent (Requires Restart)" +const val PREF_KEY_RANDOM_UA = "pref_key_random_ua_" +val RANDOM_UA_ENTRIES = arrayOf("OFF", "Desktop", "Mobile") +val RANDOM_UA_VALUES = arrayOf("off", "desktop", "mobile") + +const val TITLE_CUSTOM_UA = "Custom User-Agent (Requires Restart)" +const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua_" +const val CUSTOM_UA_SUMMARY = "Leave blank to use application default user-agent (IGNORED if Random User-Agent is enabled)" + diff --git a/lib/synchrony/build.gradle.kts b/lib/synchrony/build.gradle.kts index 38ccda93fe..d53ef825ef 100644 --- a/lib/synchrony/build.gradle.kts +++ b/lib/synchrony/build.gradle.kts @@ -5,12 +5,13 @@ plugins { android { compileSdk = AndroidConfig.compileSdk - namespace = "eu.kanade.tachiyomi.lib.synchrony" defaultConfig { minSdk = AndroidConfig.minSdk targetSdk = AndroidConfig.targetSdk } + + namespace = "eu.kanade.tachiyomi.lib.synchrony" } repositories { diff --git a/lib/textinterceptor/build.gradle.kts b/lib/textinterceptor/build.gradle.kts index e8086827ca..aa5a1762cf 100644 --- a/lib/textinterceptor/build.gradle.kts +++ b/lib/textinterceptor/build.gradle.kts @@ -10,6 +10,8 @@ android { minSdk = AndroidConfig.minSdk targetSdk = AndroidConfig.targetSdk } + + namespace = "eu.kanade.tachiyomi.lib.textinterceptor" } repositories { diff --git a/lib/textinterceptor/src/main/AndroidManifest.xml b/lib/textinterceptor/src/main/AndroidManifest.xml deleted file mode 100644 index 6d52001ade..0000000000 --- a/lib/textinterceptor/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/multisrc/build.gradle.kts b/multisrc/build.gradle.kts index dbc098f91f..511012eff4 100644 --- a/multisrc/build.gradle.kts +++ b/multisrc/build.gradle.kts @@ -12,6 +12,8 @@ android { targetSdk = AndroidConfig.targetSdk } + namespace = "eu.kanade.tachiyomi.lib.themesources" + kotlinOptions { freeCompilerArgs += "-opt-in=kotlinx.serialization.ExperimentalSerializationApi" } diff --git a/multisrc/overrides/a3manga/default/AndroidManifest.xml b/multisrc/overrides/a3manga/default/AndroidManifest.xml index 9a92f3a79e..0489d5a185 100644 --- a/multisrc/overrides/a3manga/default/AndroidManifest.xml +++ b/multisrc/overrides/a3manga/default/AndroidManifest.xml @@ -1,7 +1,5 @@ - - + - + - \ No newline at end of file + diff --git a/multisrc/overrides/bilibili/bilibilimanga/AndroidManifest.xml b/multisrc/overrides/bilibili/bilibilimanga/AndroidManifest.xml index 5603a61fe4..6b86dbfce3 100644 --- a/multisrc/overrides/bilibili/bilibilimanga/AndroidManifest.xml +++ b/multisrc/overrides/bilibili/bilibilimanga/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + - \ No newline at end of file + diff --git a/multisrc/overrides/flixscans/flixscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/flixscans/flixscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..96992a1858 Binary files /dev/null and b/multisrc/overrides/flixscans/flixscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/flixscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/flixscans/flixscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..bf4daa4174 Binary files /dev/null and b/multisrc/overrides/flixscans/flixscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/flixscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/flixscans/flixscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..8beccb9bc6 Binary files /dev/null and b/multisrc/overrides/flixscans/flixscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/flixscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/flixscans/flixscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d09cf7a560 Binary files /dev/null and b/multisrc/overrides/flixscans/flixscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/flixscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/flixscans/flixscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9067ecd9cd Binary files /dev/null and b/multisrc/overrides/flixscans/flixscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/flixscans/res/web_hi_res_512.png b/multisrc/overrides/flixscans/flixscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..8ac2f0947f Binary files /dev/null and b/multisrc/overrides/flixscans/flixscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/flixscans/flixscans/src/FlixScansNet.kt b/multisrc/overrides/flixscans/flixscans/src/FlixScansNet.kt new file mode 100644 index 0000000000..b148a2283e --- /dev/null +++ b/multisrc/overrides/flixscans/flixscans/src/FlixScansNet.kt @@ -0,0 +1,5 @@ +package eu.kanade.tachiyomi.extension.en.flixscans + +import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans + +class FlixScansNet : FlixScans("Flix Scans", "https://flixscans.net", "en", cdnUrl = "https://media.flixscans.net/") diff --git a/multisrc/overrides/flixscans/galaxymanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..508b1a1d1f Binary files /dev/null and b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/galaxymanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..8a52388887 Binary files /dev/null and b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..0ced75903a Binary files /dev/null and b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..e5e17f45dd Binary files /dev/null and b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2e68f0aab3 Binary files /dev/null and b/multisrc/overrides/flixscans/galaxymanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/galaxymanga/res/web_hi_res_512.png b/multisrc/overrides/flixscans/galaxymanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..e37dad0c0a Binary files /dev/null and b/multisrc/overrides/flixscans/galaxymanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/flixscans/galaxymanga/src/GalaxyManga.kt b/multisrc/overrides/flixscans/galaxymanga/src/GalaxyManga.kt new file mode 100644 index 0000000000..ea536d9ad8 --- /dev/null +++ b/multisrc/overrides/flixscans/galaxymanga/src/GalaxyManga.kt @@ -0,0 +1,7 @@ +package eu.kanade.tachiyomi.extension.ar.galaxymanga + +import eu.kanade.tachiyomi.multisrc.flixscans.FlixScans + +class GalaxyManga : FlixScans("جالاكسي مانجا", "https://flixscans.com", "ar") { + override val versionId = 2 +} diff --git a/multisrc/overrides/flixscans/manganoon/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/flixscans/manganoon/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..57f68e7839 Binary files /dev/null and b/multisrc/overrides/flixscans/manganoon/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/manganoon/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/flixscans/manganoon/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..54fb858234 Binary files /dev/null and b/multisrc/overrides/flixscans/manganoon/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/manganoon/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/flixscans/manganoon/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b780a4629e Binary files /dev/null and b/multisrc/overrides/flixscans/manganoon/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/manganoon/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/flixscans/manganoon/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6b3f3691ec Binary files /dev/null and b/multisrc/overrides/flixscans/manganoon/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/manganoon/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/flixscans/manganoon/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..574a70fad6 Binary files /dev/null and b/multisrc/overrides/flixscans/manganoon/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/flixscans/manganoon/res/web_hi_res_512.png b/multisrc/overrides/flixscans/manganoon/res/web_hi_res_512.png new file mode 100644 index 0000000000..16b34084ad Binary files /dev/null and b/multisrc/overrides/flixscans/manganoon/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/fmreader/kisslove/src/KissLove.kt b/multisrc/overrides/fmreader/kisslove/src/KissLove.kt index 130b4fe482..2e31fa7df3 100644 --- a/multisrc/overrides/fmreader/kisslove/src/KissLove.kt +++ b/multisrc/overrides/fmreader/kisslove/src/KissLove.kt @@ -1,9 +1,80 @@ package eu.kanade.tachiyomi.extension.ja.kisslove import eu.kanade.tachiyomi.multisrc.fmreader.FMReader -import eu.kanade.tachiyomi.source.model.Page -import org.jsoup.nodes.Document +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import org.jsoup.nodes.Element +import java.util.Calendar -class KissLove : FMReader("KissLove", "https://klmanga.com", "ja") { - override fun pageListParse(document: Document): List = base64PageListParse(document) +class KissLove : FMReader("KissLove", "https://klz9.com", "ja") { + override fun latestUpdatesRequest(page: Int) = + GET("$baseUrl/manga-list.html?page=$page&sort=last_update") + + override fun chapterListRequest(manga: SManga): Request { + val mangaId = MID_URL_REGEX.find(manga.url) + ?.groupValues?.get(1) + ?: throw Exception("Could not find manga id") + + val xhrUrl = "$baseUrl/app/manga/controllers/cont.listChapter.php".toHttpUrl().newBuilder() + .addQueryParameter("slug", mangaId) + .build() + + return GET(xhrUrl, headers) + } + + override fun chapterFromElement(element: Element, mangaTitle: String): SChapter { + return SChapter.create().apply { + element.select(chapterUrlSelector).first()!!.let { + setUrlWithoutDomain("$baseUrl/${it.attr("href")}") + name = it.attr("title") + } + + date_upload = element.select(chapterTimeSelector) + .let { if (it.hasText()) parseChapterDate(it.text()) else 0 } + } + } + + private fun parseChapterDate(date: String): Long { + val value = date.split(' ')[dateValueIndex].toInt() + val chapterDate = Calendar.getInstance().apply { + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } + + when (date.split(' ')[dateWordIndex]) { + "mins", "minutes" -> chapterDate.add(Calendar.MINUTE, value * -1) + "hours" -> chapterDate.add(Calendar.HOUR_OF_DAY, value * -1) + "days" -> chapterDate.add(Calendar.DATE, value * -1) + "weeks" -> chapterDate.add(Calendar.DATE, value * 7 * -1) + "months" -> chapterDate.add(Calendar.MONTH, value * -1) + "years" -> chapterDate.add(Calendar.YEAR, value * -1) + else -> return 0 + } + + return chapterDate.timeInMillis + } + + override fun pageListRequest(chapter: SChapter): Request { + val request = super.pageListRequest(chapter) + val response = client.newCall(request).execute() + val document = response.asJsoup() + + val chapterId = document.selectFirst("#chapter") + ?.`val`() + ?: throw Exception("Could not find chapter id") + + val xhrUrl = "$baseUrl/app/manga/controllers/cont.listImg.php".toHttpUrl().newBuilder() + .addQueryParameter("cid", chapterId) + .build() + + return GET(xhrUrl, headers) + } + + companion object { + private val MID_URL_REGEX = "-([^.]+).html".toRegex() + } } diff --git a/multisrc/overrides/fmreader/rawlh/src/WeLoveManga.kt b/multisrc/overrides/fmreader/rawlh/src/WeLoveManga.kt index a6c8e05cb7..b9431823c4 100644 --- a/multisrc/overrides/fmreader/rawlh/src/WeLoveManga.kt +++ b/multisrc/overrides/fmreader/rawlh/src/WeLoveManga.kt @@ -1,14 +1,12 @@ package eu.kanade.tachiyomi.extension.ja.rawlh -import android.util.Base64 import eu.kanade.tachiyomi.multisrc.fmreader.FMReader import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SManga import okhttp3.Request -import org.jsoup.nodes.Attribute import org.jsoup.nodes.Document import org.jsoup.nodes.Element -import java.nio.charset.Charset class WeLoveManga : FMReader("WeLoveManga", "https://weloma.art", "ja") { // Formerly "RawLH" @@ -17,13 +15,9 @@ class WeLoveManga : FMReader("WeLoveManga", "https://weloma.art", "ja") { override val chapterUrlSelector = "" override fun pageListParse(document: Document): List { fun Element.decoded(): String { - val attr = this.attributes().map(Attribute::key).maxByOrNull(kotlin.String::length) ?: "src" - return if (!this.attr(attr).contains(".")) { - Base64.decode(this.attr(attr), Base64.DEFAULT).toString(Charset.defaultCharset()) - } else { - this.attr("abs:$attr") - } + return this.attr("data-src").trimEnd() } + return document.select(pageListImageSelector).mapIndexed { i, img -> Page(i, document.location(), img.decoded()) } @@ -31,4 +25,20 @@ class WeLoveManga : FMReader("WeLoveManga", "https://weloma.art", "ja") { // Referer needs to be chapter URL override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headersBuilder().set("Referer", page.url).build()) + + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + element.select(headerSelector).let { + setUrlWithoutDomain(it.attr("abs:href")) + title = it.text() + } + thumbnail_url = element + .select("div.content.img-in-ratio") + .first()!! + .attr("style") + .let { BACKGROUND_IMAGE_REGEX.find(it)?.groups?.get(1)?.value } + } + + companion object { + val BACKGROUND_IMAGE_REGEX = Regex("""url\(['"]?(.*?)['"]?\)""") + } } diff --git a/multisrc/overrides/fmreader/welovemangaone/src/WeLoveMangaOne.kt b/multisrc/overrides/fmreader/welovemangaone/src/WeLoveMangaOne.kt index f1c09a21c5..8c07321f42 100644 --- a/multisrc/overrides/fmreader/welovemangaone/src/WeLoveMangaOne.kt +++ b/multisrc/overrides/fmreader/welovemangaone/src/WeLoveMangaOne.kt @@ -3,6 +3,10 @@ package eu.kanade.tachiyomi.extension.ja.welovemangaone import eu.kanade.tachiyomi.multisrc.fmreader.FMReader import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request import org.jsoup.nodes.Element import java.util.Calendar @@ -10,6 +14,18 @@ class WeLoveMangaOne : FMReader("WeLoveMangaOne", "https://welovemanga.one", "ja override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/manga-list.html?page=$page&sort=last_update") + override fun chapterListRequest(manga: SManga): Request { + val mangaId = MID_URL_REGEX.find(manga.url) + ?.groupValues?.get(1) + ?: throw Exception("Could not find manga id") + + val xhrUrl = "$baseUrl/app/manga/controllers/cont.Listchapter.php".toHttpUrl().newBuilder() + .addQueryParameter("mid", mangaId) + .build() + + return GET(xhrUrl, headers) + } + override fun chapterFromElement(element: Element, mangaTitle: String): SChapter { return SChapter.create().apply { element.let { @@ -41,4 +57,24 @@ class WeLoveMangaOne : FMReader("WeLoveMangaOne", "https://welovemanga.one", "ja return chapterDate.timeInMillis } + + override fun pageListRequest(chapter: SChapter): Request { + val request = super.pageListRequest(chapter) + val response = client.newCall(request).execute() + val document = response.asJsoup() + + val chapterId = document.selectFirst("#chapter") + ?.`val`() + ?: throw Exception("Could not find chapter id") + + val xhrUrl = "$baseUrl/app/manga/controllers/cont.listImg.php".toHttpUrl().newBuilder() + .addQueryParameter("cid", chapterId) + .build() + + return GET(xhrUrl, headers) + } + + companion object { + private val MID_URL_REGEX = "(\\d+)/".toRegex() + } } diff --git a/multisrc/overrides/grouple/default/AndroidManifest.xml b/multisrc/overrides/grouple/default/AndroidManifest.xml index 87f96afd91..294993e552 100644 --- a/multisrc/overrides/grouple/default/AndroidManifest.xml +++ b/multisrc/overrides/grouple/default/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + - + - \ No newline at end of file + diff --git a/multisrc/overrides/heancms/perfscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/heancms/perfscan/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..acb868c34f Binary files /dev/null and b/multisrc/overrides/heancms/perfscan/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/heancms/perfscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/heancms/perfscan/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..164f6611a0 Binary files /dev/null and b/multisrc/overrides/heancms/perfscan/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/heancms/perfscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/heancms/perfscan/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..4cf6669f30 Binary files /dev/null and b/multisrc/overrides/heancms/perfscan/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/heancms/perfscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/heancms/perfscan/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..74c2a6dc5a Binary files /dev/null and b/multisrc/overrides/heancms/perfscan/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/heancms/perfscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/heancms/perfscan/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..fee5511710 Binary files /dev/null and b/multisrc/overrides/heancms/perfscan/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/heancms/perfscan/res/web_hi_res_512.png b/multisrc/overrides/heancms/perfscan/res/web_hi_res_512.png new file mode 100644 index 0000000000..dfdba5083f Binary files /dev/null and b/multisrc/overrides/heancms/perfscan/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/heancms/perfscan/src/PerfScan.kt b/multisrc/overrides/heancms/perfscan/src/PerfScan.kt new file mode 100644 index 0000000000..23479e70d9 --- /dev/null +++ b/multisrc/overrides/heancms/perfscan/src/PerfScan.kt @@ -0,0 +1,15 @@ +package eu.kanade.tachiyomi.extension.fr.perfscan + +import eu.kanade.tachiyomi.multisrc.heancms.HeanCms +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient + +class PerfScan : HeanCms("Perf Scan", "https://perf-scan.fr", "fr") { + override val client: OkHttpClient = super.client.newBuilder() + .rateLimitHost(apiUrl.toHttpUrl(), 1, 2) + .build() + + override val coverPath: String = "" + override val useNewQueryEndpoint = true +} diff --git a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt index 8257e9a7c9..77122a85bd 100644 --- a/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt +++ b/multisrc/overrides/heancms/reaperscans/src/ReaperScans.kt @@ -21,7 +21,8 @@ class ReaperScans : HeanCms( // Site changed from Madara to HeanCms. override val versionId = 2 - override val fetchAllTitles = true + override val slugStrategy = SlugStrategy.FETCH_ALL + override val useNewQueryEndpoint = true override val coverPath: String = "" diff --git a/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt b/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt index 8aa9f7310d..60343a7534 100644 --- a/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt +++ b/multisrc/overrides/heancms/yugenmangas/src/YugenMangas.kt @@ -2,25 +2,30 @@ package eu.kanade.tachiyomi.extension.es.yugenmangas import eu.kanade.tachiyomi.multisrc.heancms.Genre import eu.kanade.tachiyomi.multisrc.heancms.HeanCms -import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import okhttp3.HttpUrl.Companion.toHttpUrl import java.text.SimpleDateFormat import java.util.TimeZone import java.util.concurrent.TimeUnit -class YugenMangas : HeanCms( - "YugenMangas", - "https://yugenmangas.net", - "es", - "https://api.yugenmangas.net", -) { +class YugenMangas : + HeanCms( + "YugenMangas", + "https://yugenmangas.net", + "es", + "https://api.yugenmangas.net", + ) { // Site changed from Madara to HeanCms. override val versionId = 2 + override val slugStrategy = SlugStrategy.ID + override val useNewQueryEndpoint = true + override val client = super.client.newBuilder() .connectTimeout(60, TimeUnit.SECONDS) .readTimeout(90, TimeUnit.SECONDS) - .rateLimit(1, 1) + .rateLimitHost(apiUrl.toHttpUrl(), 2, 3) .build() override val coverPath: String = "" diff --git a/multisrc/overrides/libgroup/hentailib/AndroidManifest.xml b/multisrc/overrides/libgroup/hentailib/AndroidManifest.xml index 543d49e63c..29cb390940 100644 --- a/multisrc/overrides/libgroup/hentailib/AndroidManifest.xml +++ b/multisrc/overrides/libgroup/hentailib/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + - + 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/blmanhwaclub/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 3a5842f3e8..0000000000 Binary files a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/blmanhwaclub/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 11d0f98266..0000000000 Binary files a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 6f10b3b000..0000000000 Binary files a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 31b191f20f..0000000000 Binary files a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b6872a5cd1..0000000000 Binary files a/multisrc/overrides/madara/blmanhwaclub/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/blmanhwaclub/res/web_hi_res_512.png b/multisrc/overrides/madara/blmanhwaclub/res/web_hi_res_512.png deleted file mode 100644 index 93eea3093d..0000000000 Binary files a/multisrc/overrides/madara/blmanhwaclub/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/blmanhwaclub/src/BlManhwaClub.kt b/multisrc/overrides/madara/blmanhwaclub/src/BlManhwaClub.kt deleted file mode 100644 index f08f396f2b..0000000000 --- a/multisrc/overrides/madara/blmanhwaclub/src/BlManhwaClub.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.blmanhwaclub - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class BlManhwaClub : Madara( - "BL Manhwa Club", - "https://blmanhwa.club", - "pt-BR", - SimpleDateFormat("dd MMM yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/bokugentranslation/src/BokugenTranslation.kt b/multisrc/overrides/madara/bokugentranslation/src/BokugenTranslation.kt index f45bd0d4b4..4e04436099 100644 --- a/multisrc/overrides/madara/bokugentranslation/src/BokugenTranslation.kt +++ b/multisrc/overrides/madara/bokugentranslation/src/BokugenTranslation.kt @@ -14,7 +14,6 @@ import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat import java.util.Locale import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit class BokugenTranslation : Madara( "BokugenTranslation", @@ -23,8 +22,7 @@ class BokugenTranslation : Madara( dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), ) { private var loadWebView = true - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(uaIntercept) + override val client: OkHttpClient = super.client.newBuilder() .addInterceptor { chain -> val request = chain.request() val url = request.url.toString() @@ -56,8 +54,6 @@ class BokugenTranslation : Madara( } chain.proceed(request) } - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) .rateLimit(1, 1) .build() diff --git a/multisrc/overrides/madara/cerisescans/src/CeriseScan.kt b/multisrc/overrides/madara/cerisescans/src/CeriseScan.kt new file mode 100644 index 0000000000..90e0fe3e5a --- /dev/null +++ b/multisrc/overrides/madara/cerisescans/src/CeriseScan.kt @@ -0,0 +1,36 @@ +package eu.kanade.tachiyomi.extension.pt.cerisescans + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.OkHttpClient +import okhttp3.Request +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class CeriseScan : Madara( + "Cerise Scan", + "https://cerisescan.com/home1", + "pt-BR", + SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), +) { + + // Name changed from 'Cerise Scans' to 'Cerise Scan' + override val id: Long = 8629915907358523454 + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() + + override val useNewChapterEndpoint = true + + override fun mangaDetailsRequest(manga: SManga): Request { + return GET(baseUrl + manga.url.replace("/home1", ""), headers) + } + + override fun chapterListRequest(manga: SManga): Request { + return GET(baseUrl + manga.url.replace("/home1", ""), headers) + } +} diff --git a/multisrc/overrides/madara/cerisescans/src/CeriseScans.kt b/multisrc/overrides/madara/cerisescans/src/CeriseScans.kt deleted file mode 100644 index 53d0ebbcec..0000000000 --- a/multisrc/overrides/madara/cerisescans/src/CeriseScans.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.cerisescans - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class CeriseScans : Madara( - "Cerise Scans", - "https://cerisescans.com", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/cocorip/res/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/cocorip/res/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..c5077ea895 Binary files /dev/null and b/multisrc/overrides/madara/cocorip/res/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/cocorip/res/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/cocorip/res/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..f3fb3ab36b Binary files /dev/null and b/multisrc/overrides/madara/cocorip/res/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/cocorip/res/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/cocorip/res/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..36e0034926 Binary files /dev/null and b/multisrc/overrides/madara/cocorip/res/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/cocorip/res/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/cocorip/res/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..02bb580dc6 Binary files /dev/null and b/multisrc/overrides/madara/cocorip/res/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/cocorip/res/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/cocorip/res/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3a8a486e67 Binary files /dev/null and b/multisrc/overrides/madara/cocorip/res/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/cocorip/res/web_hi_res_512.png b/multisrc/overrides/madara/cocorip/res/web_hi_res_512.png new file mode 100644 index 0000000000..50f591bdcc Binary files /dev/null and b/multisrc/overrides/madara/cocorip/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/cocorip/src/CocoRip.kt b/multisrc/overrides/madara/cocorip/src/CocoRip.kt new file mode 100644 index 0000000000..d9f79856cb --- /dev/null +++ b/multisrc/overrides/madara/cocorip/src/CocoRip.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.es.cocorip + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class CocoRip : Madara("Coco Rip", "https://cocorip.net", "es", SimpleDateFormat("dd/MM/yyyy", Locale("es"))) { + override val mangaDetailsSelectorDescription = "div.summary__content" +} diff --git a/multisrc/overrides/madara/coffeemangatop/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/coffeemangatop/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..bd0218e9ce Binary files /dev/null and b/multisrc/overrides/madara/coffeemangatop/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/coffeemangatop/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/coffeemangatop/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..a9af109203 Binary files /dev/null and b/multisrc/overrides/madara/coffeemangatop/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/coffeemangatop/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/coffeemangatop/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..067e5bfd10 Binary files /dev/null and b/multisrc/overrides/madara/coffeemangatop/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/coffeemangatop/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/coffeemangatop/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4ddb41385c Binary files /dev/null and b/multisrc/overrides/madara/coffeemangatop/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/coffeemangatop/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/coffeemangatop/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7bc1fae4e1 Binary files /dev/null and b/multisrc/overrides/madara/coffeemangatop/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/coffeemangatop/res/web_hi_res_512.png b/multisrc/overrides/madara/coffeemangatop/res/web_hi_res_512.png new file mode 100644 index 0000000000..dae6bccb46 Binary files /dev/null and b/multisrc/overrides/madara/coffeemangatop/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/coffeemangatop/src/CoffeeMangaTop.kt b/multisrc/overrides/madara/coffeemangatop/src/CoffeeMangaTop.kt new file mode 100644 index 0000000000..76b8b2ae0c --- /dev/null +++ b/multisrc/overrides/madara/coffeemangatop/src/CoffeeMangaTop.kt @@ -0,0 +1,64 @@ +package eu.kanade.tachiyomi.extension.en.coffeemangatop + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import java.text.SimpleDateFormat +import java.util.Locale + +class CoffeeMangaTop : Madara( + "CoffeeManga.top (unoriginal)", + "https://coffeemanga.top", + "en", + dateFormat = SimpleDateFormat("MMM dd, HH:mm", Locale.ENGLISH), +) { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = "search?page=$page" + + override fun popularMangaRequest(page: Int): Request = + GET("$baseUrl/popular-manga?page=$page", headers) + + override fun latestUpdatesRequest(page: Int): Request = + GET("$baseUrl/latest-manga?page=$page", headers) + + // Copied from IsekaiScan.top (unoriginal) + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val chaptersWrapper = document.select("div[id^=manga-chapters-holder]") + + var chapterElements = document.select(chapterListSelector()) + + if (chapterElements.isEmpty() && !chaptersWrapper.isNullOrEmpty()) { + val mangaId = chaptersWrapper.attr("data-id") + val xhrHeaders = headersBuilder() + .add("X-Requested-With", "XMLHttpRequest") + .build() + val xhrRequest = GET("$baseUrl/ajax-list-chapter?mangaID=$mangaId", xhrHeaders) + val xhrResponse = client.newCall(xhrRequest).execute() + + chapterElements = xhrResponse.asJsoup().select(chapterListSelector()) + xhrResponse.close() + } + + countViews(document) + return chapterElements.map(::chapterFromElement) + } + + // Copied from IsekaiScan.top (unoriginal) + override fun pageListParse(document: Document): List { + val stringArray = document.select("p#arraydata").text().split(",").toTypedArray() + return stringArray.mapIndexed { index, url -> + Page( + index, + document.location(), + url, + ) + } + } +} diff --git a/multisrc/overrides/madara/coloredmanga/src/ColoredManga.kt b/multisrc/overrides/madara/coloredmanga/src/ColoredManga.kt new file mode 100644 index 0000000000..be16c5d063 --- /dev/null +++ b/multisrc/overrides/madara/coloredmanga/src/ColoredManga.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.en.coloredmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ColoredManga : Madara( + "Colored Manga", + "https://coloredmanga.com", + "en", + dateFormat = SimpleDateFormat("dd-MMM", Locale.ENGLISH), +) { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/comicarab/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/comicarab/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..e9f3b50c33 Binary files /dev/null and b/multisrc/overrides/madara/comicarab/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicarab/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/comicarab/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..cf530cf62f Binary files /dev/null and b/multisrc/overrides/madara/comicarab/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicarab/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/comicarab/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b8be692b5c Binary files /dev/null and b/multisrc/overrides/madara/comicarab/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicarab/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/comicarab/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3bca922d90 Binary files /dev/null and b/multisrc/overrides/madara/comicarab/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicarab/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/comicarab/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2382dc7e93 Binary files /dev/null and b/multisrc/overrides/madara/comicarab/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicarab/res/web_hi_res_512.png b/multisrc/overrides/madara/comicarab/res/web_hi_res_512.png new file mode 100644 index 0000000000..41b33859ca Binary files /dev/null and b/multisrc/overrides/madara/comicarab/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/comicarab/src/ComicArab.kt b/multisrc/overrides/madara/comicarab/src/ComicArab.kt new file mode 100644 index 0000000000..6dc77be66a --- /dev/null +++ b/multisrc/overrides/madara/comicarab/src/ComicArab.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.ar.comicarab + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ComicArab : Madara( + "كوميك العرب", + "https://comicarab.com", + "ar", + dateFormat = SimpleDateFormat("dd MMMM، yyyy", Locale("ar")), +) { + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/overrides/madara/comicscans/src/ComicScans.kt b/multisrc/overrides/madara/comicscans/src/ComicScans.kt new file mode 100644 index 0000000000..910b11d707 --- /dev/null +++ b/multisrc/overrides/madara/comicscans/src/ComicScans.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.comicscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ComicScans : Madara("Comic Scans", "https://www.comicscans.org", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/comicznetv2/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/comicznetv2/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..2a512df276 Binary files /dev/null and b/multisrc/overrides/madara/comicznetv2/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicznetv2/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/comicznetv2/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..d1b00a2858 Binary files /dev/null and b/multisrc/overrides/madara/comicznetv2/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicznetv2/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/comicznetv2/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2647035d8d Binary files /dev/null and b/multisrc/overrides/madara/comicznetv2/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicznetv2/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/comicznetv2/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9d70ba7551 Binary files /dev/null and b/multisrc/overrides/madara/comicznetv2/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicznetv2/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/comicznetv2/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..687733af37 Binary files /dev/null and b/multisrc/overrides/madara/comicznetv2/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/comicznetv2/res/web_hi_res_512.png b/multisrc/overrides/madara/comicznetv2/res/web_hi_res_512.png new file mode 100644 index 0000000000..081e777620 Binary files /dev/null and b/multisrc/overrides/madara/comicznetv2/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/comicznetv2/src/ComiczNetV2.kt b/multisrc/overrides/madara/comicznetv2/src/ComiczNetV2.kt new file mode 100644 index 0000000000..c04d7bf89a --- /dev/null +++ b/multisrc/overrides/madara/comicznetv2/src/ComiczNetV2.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.all.comicznetv2 + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ComiczNetV2 : Madara("Comicz.net v2", "https://v2.comiz.net", "all") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/cookietoon/src/CookieToon.kt b/multisrc/overrides/madara/cookietoon/src/CookieToon.kt deleted file mode 100644 index 7ce3fac90d..0000000000 --- a/multisrc/overrides/madara/cookietoon/src/CookieToon.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.cookietoon - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class CookieToon : Madara( - "CookieToon", - "https://cookietoon.online", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/cronosscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/cronosscan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 84879466d2..0000000000 Binary files a/multisrc/overrides/madara/cronosscan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/cronosscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/cronosscan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index f148ae0265..0000000000 Binary files a/multisrc/overrides/madara/cronosscan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/cronosscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/cronosscan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1070c8d17a..0000000000 Binary files a/multisrc/overrides/madara/cronosscan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/cronosscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/cronosscan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a66bd4b927..0000000000 Binary files a/multisrc/overrides/madara/cronosscan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/cronosscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/cronosscan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 2f60627045..0000000000 Binary files a/multisrc/overrides/madara/cronosscan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/cronosscan/res/web_hi_res_512.png b/multisrc/overrides/madara/cronosscan/res/web_hi_res_512.png deleted file mode 100644 index 9199c25d33..0000000000 Binary files a/multisrc/overrides/madara/cronosscan/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/cronosscan/src/CronosScan.kt b/multisrc/overrides/madara/cronosscan/src/CronosScan.kt deleted file mode 100644 index 8ed0a6aac0..0000000000 --- a/multisrc/overrides/madara/cronosscan/src/CronosScan.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.cronosscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class CronosScan : Madara( - "Cronos Scan", - "https://cronosscan.net", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/default/AndroidManifest.xml b/multisrc/overrides/madara/default/AndroidManifest.xml index ad44865c88..5d4316fb8e 100644 --- a/multisrc/overrides/madara/default/AndroidManifest.xml +++ b/multisrc/overrides/madara/default/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - \ No newline at end of file + diff --git a/multisrc/overrides/madara/default/additional.gradle b/multisrc/overrides/madara/default/additional.gradle index 06abd18c65..214a0df0ed 100644 --- a/multisrc/overrides/madara/default/additional.gradle +++ b/multisrc/overrides/madara/default/additional.gradle @@ -1,3 +1,4 @@ dependencies { - implementation(project(':lib-cryptoaes')) -} \ No newline at end of file + implementation(project(":lib-cryptoaes")) + implementation(project(":lib-randomua")) +} diff --git a/multisrc/overrides/madara/doodmanga/src/Doodmanga.kt b/multisrc/overrides/madara/doodmanga/src/Doodmanga.kt index 232fdfd2ac..682f5c672b 100644 --- a/multisrc/overrides/madara/doodmanga/src/Doodmanga.kt +++ b/multisrc/overrides/madara/doodmanga/src/Doodmanga.kt @@ -7,16 +7,12 @@ import okhttp3.OkHttpClient import org.jsoup.nodes.Document import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit class Doodmanga : Madara("Doodmanga", "https://www.doodmanga.com", "th", SimpleDateFormat("dd MMMMM yyyy", Locale("th"))) { override val filterNonMangaItems = false - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(uaIntercept) + override val client: OkHttpClient = super.client.newBuilder() .addInterceptor(ScrambledImageInterceptor) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) .build() override val pageListParseSelector = "div.text-center > p > img, div.text-center > img, div.text-center > script" diff --git a/multisrc/overrides/madara/dragontea/src/DragonTea.kt b/multisrc/overrides/madara/dragontea/src/DragonTea.kt index d8502af460..fb784bb81f 100644 --- a/multisrc/overrides/madara/dragontea/src/DragonTea.kt +++ b/multisrc/overrides/madara/dragontea/src/DragonTea.kt @@ -1,20 +1,15 @@ package eu.kanade.tachiyomi.extension.en.dragontea -import android.graphics.Bitmap -import android.graphics.BitmapFactory -import android.graphics.Canvas -import android.graphics.Rect +import android.util.Base64 +import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.Page -import okhttp3.Interceptor -import okhttp3.MediaType.Companion.toMediaType +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import okhttp3.OkHttpClient -import okhttp3.Protocol -import okhttp3.Response -import okhttp3.ResponseBody.Companion.toResponseBody import org.jsoup.nodes.Document -import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Locale @@ -25,7 +20,6 @@ class DragonTea : Madara( dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.US), ) { override val client: OkHttpClient = super.client.newBuilder() - .addInterceptor(::begonepeconIntercept) .rateLimit(1) .build() @@ -41,86 +35,54 @@ class DragonTea : Madara( } } - private val begonepeconSelector: String = "div.begonepecon" - - private val peconholderSelector: String = "div.peconholder" + private val pageIndexRegex = Regex("""image-(\d+)[a-z]+""", RegexOption.IGNORE_CASE) override fun pageListParse(document: Document): List { - countViews(document) - - val hasSplitImages = document - .select(begonepeconSelector) - .firstOrNull() != null - - if (!hasSplitImages) { - return super.pageListParse(document) + val dataId = document.selectFirst(".entry-header.header")?.attr("data-id")?.toInt() + ?: return super.pageListParse(document) + val elements = document.select(".reading-content .page-break img") + val pageCount = elements.size + + val idKey = "8" + ((dataId + 1306) * 3 - pageCount).toString() + elements.forEach { + val decryptedId = decryptAesJson(it.attr("id"), idKey).jsonPrimitive.content + it.attr("id", decryptedId) } - return document.select("div.page-break, li.blocks-gallery-item, $begonepeconSelector") - .mapIndexed { index, element -> - val imageUrl = if (element.select(peconholderSelector).firstOrNull() == null) { - element.select("img").first()?.let { it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src") } - } else { - element.select("img").joinToString("|") { it.absUrl(if (it.hasAttr("data-src")) "data-src" else "src") } + BEGONEPECON_SUFFIX - } - Page(index, document.location(), imageUrl) - } - } - - private fun begonepeconIntercept(chain: Interceptor.Chain): Response { - if (!chain.request().url.toString().endsWith(BEGONEPECON_SUFFIX)) { - return chain.proceed(chain.request()) + val orderedElements = elements.sortedBy { + pageIndexRegex.find(it.attr("id"))?.groupValues?.get(1)?.toInt() ?: 0 } + val dtaKey = "13" + orderedElements.joinToString("") { it.attr("id").takeLast(1) } + (((dataId + 88) * 2) - pageCount - 4).toString() - val imageUrls = chain.request().url.toString() - .removeSuffix(BEGONEPECON_SUFFIX) - .split("%7C") - - var width = 0 - var height = 0 - - val imageBitmaps = imageUrls.map { imageUrl -> - val request = chain.request().newBuilder().url(imageUrl).build() - val response = chain.proceed(request) - - val bitmap = BitmapFactory.decodeStream(response.body.byteStream()) + val srcKey = (dataId + 20).toString() + orderedElements.joinToString("") { + decryptAesJson(it.attr("dta"), dtaKey).jsonPrimitive.content.takeLast(2) + } + (pageCount * 2).toString() - width += bitmap.width - height = bitmap.height - - bitmap + return orderedElements.mapIndexed { i, element -> + val src = decryptAesJson(element.attr("data-src"), srcKey).jsonPrimitive.content + Page(i, document.location(), src) } + } - val result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - val canvas = Canvas(result) - - var left = 0 - - imageBitmaps.forEach { bitmap -> - val srcRect = Rect(0, 0, bitmap.width, bitmap.height) - val dstRect = Rect(left, 0, left + bitmap.width, bitmap.height) - - canvas.drawBitmap(bitmap, srcRect, dstRect, null) + private fun decryptAesJson(ciphertext: String, key: String): JsonElement { + val cipherData = json.parseToJsonElement(ciphertext).jsonObject - left += bitmap.width - } + val unsaltedCiphertext = Base64.decode(cipherData["ct"]!!.jsonPrimitive.content, Base64.DEFAULT) + val salt = cipherData["s"]!!.jsonPrimitive.content.decodeHex() + val saltedCiphertext = SALTED + salt + unsaltedCiphertext - val output = ByteArrayOutputStream() - result.compress(Bitmap.CompressFormat.PNG, 100, output) + return json.parseToJsonElement(CryptoAES.decrypt(Base64.encodeToString(saltedCiphertext, Base64.DEFAULT), key)) + } - val responseBody = output.toByteArray().toResponseBody(PNG_MEDIA_TYPE) + private fun String.decodeHex(): ByteArray { + check(length % 2 == 0) { "Must have an even length" } - return Response.Builder() - .code(200) - .protocol(Protocol.HTTP_1_1) - .request(chain.request()) - .message("OK") - .body(responseBody) - .build() + return chunked(2) + .map { it.toInt(16).toByte() } + .toByteArray() } companion object { - private const val BEGONEPECON_SUFFIX = "?begonepecon" - private val PNG_MEDIA_TYPE = "image/png".toMediaType() + private val SALTED = "Salted__".toByteArray(Charsets.UTF_8) } } diff --git a/multisrc/overrides/madara/drakescans/src/DrakeScans.kt b/multisrc/overrides/madara/drakescans/src/DrakeScans.kt index 1fb045ebad..64976b23ac 100644 --- a/multisrc/overrides/madara/drakescans/src/DrakeScans.kt +++ b/multisrc/overrides/madara/drakescans/src/DrakeScans.kt @@ -8,7 +8,7 @@ class DrakeScans : Madara( "Drake Scans", "https://drakescans.com", "en", - SimpleDateFormat("MM/dd/yyyy", Locale.US), + SimpleDateFormat("dd/MM/yyyy", Locale.US), ) { override val mangaDetailsSelectorTag = "" diff --git a/multisrc/overrides/madara/elitemanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/elitemanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..4424b05927 Binary files /dev/null and b/multisrc/overrides/madara/elitemanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/elitemanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/elitemanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..428955ee26 Binary files /dev/null and b/multisrc/overrides/madara/elitemanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/elitemanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/elitemanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2499371a1b Binary files /dev/null and b/multisrc/overrides/madara/elitemanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/elitemanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/elitemanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..04113d6f08 Binary files /dev/null and b/multisrc/overrides/madara/elitemanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/elitemanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/elitemanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..af819ae32c Binary files /dev/null and b/multisrc/overrides/madara/elitemanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/elitemanga/res/web_hi_res_512.png b/multisrc/overrides/madara/elitemanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..eee33c1774 Binary files /dev/null and b/multisrc/overrides/madara/elitemanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/elitemanga/src/EliteManga.kt b/multisrc/overrides/madara/elitemanga/src/EliteManga.kt new file mode 100644 index 0000000000..5f396483e3 --- /dev/null +++ b/multisrc/overrides/madara/elitemanga/src/EliteManga.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.elitemanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class EliteManga : Madara("Elite Manga", "https://www.elitemanga.org", "en") { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/estufadecristal/src/EstufaDeCristal.kt b/multisrc/overrides/madara/estufadecristal/src/EstufaDeCristal.kt deleted file mode 100644 index d1b598411c..0000000000 --- a/multisrc/overrides/madara/estufadecristal/src/EstufaDeCristal.kt +++ /dev/null @@ -1,28 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.estufadecristal - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.SChapter -import okhttp3.OkHttpClient -import okhttp3.Response -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class EstufaDeCristal : Madara( - "Estufa de Cristal", - "https://scanestufadecristal.site", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true - - override fun chapterListParse(response: Response): List { - return super.chapterListParse(response).reversed() - } -} diff --git a/multisrc/overrides/madara/factmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/factmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..f53594f273 Binary files /dev/null and b/multisrc/overrides/madara/factmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/factmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/factmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..00e99ac3e6 Binary files /dev/null and b/multisrc/overrides/madara/factmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/factmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/factmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..af56ddefd8 Binary files /dev/null and b/multisrc/overrides/madara/factmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/factmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/factmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..83f3d845a2 Binary files /dev/null and b/multisrc/overrides/madara/factmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/factmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/factmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..186131840e Binary files /dev/null and b/multisrc/overrides/madara/factmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/factmanga/res/web_hi_res_512.png b/multisrc/overrides/madara/factmanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..08868e5731 Binary files /dev/null and b/multisrc/overrides/madara/factmanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/factmanga/src/FactManga.kt b/multisrc/overrides/madara/factmanga/src/FactManga.kt new file mode 100644 index 0000000000..af542a68ec --- /dev/null +++ b/multisrc/overrides/madara/factmanga/src/FactManga.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.factmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class FactManga : Madara("FactManga", "https://factmanga.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/falconmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/falconmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..95bdbee52d Binary files /dev/null and b/multisrc/overrides/madara/falconmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/falconmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/falconmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..fcbc380af4 Binary files /dev/null and b/multisrc/overrides/madara/falconmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/falconmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/falconmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..4dd12c2f57 Binary files /dev/null and b/multisrc/overrides/madara/falconmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/falconmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/falconmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..46d634b909 Binary files /dev/null and b/multisrc/overrides/madara/falconmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/falconmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/falconmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..004a8231c1 Binary files /dev/null and b/multisrc/overrides/madara/falconmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/falconmanga/res/web_hi_res_512.png b/multisrc/overrides/madara/falconmanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..ac86b8566b Binary files /dev/null and b/multisrc/overrides/madara/falconmanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/falconmanga/src/FalconManga.kt b/multisrc/overrides/madara/falconmanga/src/FalconManga.kt new file mode 100644 index 0000000000..8f2069dad7 --- /dev/null +++ b/multisrc/overrides/madara/falconmanga/src/FalconManga.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.extension.ar.falconmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class FalconManga : Madara( + "فالكون مانجا", + "https://falconmanga.com", + "ar", + dateFormat = SimpleDateFormat("dd MMMM، yyyy", Locale("ar")), +) { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/overrides/madara/fdmscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/fdmscan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index f788a6f877..0000000000 Binary files a/multisrc/overrides/madara/fdmscan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/fdmscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/fdmscan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 914b442118..0000000000 Binary files a/multisrc/overrides/madara/fdmscan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/fdmscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/fdmscan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1ac00a17b3..0000000000 Binary files a/multisrc/overrides/madara/fdmscan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/fdmscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/fdmscan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 1dffccae51..0000000000 Binary files a/multisrc/overrides/madara/fdmscan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/fdmscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/fdmscan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index ea6ef4e5b0..0000000000 Binary files a/multisrc/overrides/madara/fdmscan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/fdmscan/res/web_hi_res_512.png b/multisrc/overrides/madara/fdmscan/res/web_hi_res_512.png deleted file mode 100644 index f9a41eca02..0000000000 Binary files a/multisrc/overrides/madara/fdmscan/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/fdmscan/src/FDMScan.kt b/multisrc/overrides/madara/fdmscan/src/FDMScan.kt deleted file mode 100644 index b689a8490e..0000000000 --- a/multisrc/overrides/madara/fdmscan/src/FDMScan.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.fdmscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class FDMScan : Madara( - "FDM Scan", - "https://fdmscan.com", - "pt-BR", - SimpleDateFormat("MMMM dd, yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/firstkissmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissdashmanga/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/firstkissmanga/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/madara/firstkissdashmanga/res/mipmap-hdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/firstkissmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissdashmanga/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/firstkissmanga/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/madara/firstkissdashmanga/res/mipmap-mdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/firstkissmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissdashmanga/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/firstkissmanga/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/madara/firstkissdashmanga/res/mipmap-xhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/firstkissmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissdashmanga/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/firstkissmanga/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/madara/firstkissdashmanga/res/mipmap-xxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/firstkissmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissdashmanga/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/firstkissmanga/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/madara/firstkissdashmanga/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/firstkissmanga/res/web_hi_res_512.png b/multisrc/overrides/madara/firstkissdashmanga/res/web_hi_res_512.png similarity index 100% rename from multisrc/overrides/madara/firstkissmanga/res/web_hi_res_512.png rename to multisrc/overrides/madara/firstkissdashmanga/res/web_hi_res_512.png diff --git a/multisrc/overrides/madara/firstkissdashmanga/src/FirstKissDashManga.kt b/multisrc/overrides/madara/firstkissdashmanga/src/FirstKissDashManga.kt new file mode 100644 index 0000000000..6438932b53 --- /dev/null +++ b/multisrc/overrides/madara/firstkissdashmanga/src/FirstKissDashManga.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.firstkissdashmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class FirstKissDashManga : Madara("1st Kiss-Manga (unoriginal)", "https://1stkiss-manga.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/firstkissmanga/src/FirstKissManga.kt b/multisrc/overrides/madara/firstkissmanga/src/FirstKissManga.kt deleted file mode 100644 index c8c6887ff0..0000000000 --- a/multisrc/overrides/madara/firstkissmanga/src/FirstKissManga.kt +++ /dev/null @@ -1,18 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.firstkissmanga - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.Headers -import java.util.concurrent.TimeUnit - -class FirstKissManga : Madara( - "1st Kiss", - "https://1stkissmanga.me", - "en", -) { - override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl) - - override val client = network.cloudflareClient.newBuilder() - .rateLimit(1, 3, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/firstkissmangablog/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d8bbacd490 Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangablog/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ec35ead1d0 Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..bd959ac04d Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..58989ca02b Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3ae51ec0a2 Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangablog/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangablog/res/web_hi_res_512.png b/multisrc/overrides/madara/firstkissmangablog/res/web_hi_res_512.png new file mode 100644 index 0000000000..a624bd17dd Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangablog/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/firstkissmangablog/src/FirstKissMangaBlog.kt b/multisrc/overrides/madara/firstkissmangablog/src/FirstKissMangaBlog.kt new file mode 100644 index 0000000000..2ec33b61a9 --- /dev/null +++ b/multisrc/overrides/madara/firstkissmangablog/src/FirstKissMangaBlog.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.firstkissmangablog + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class FirstKissMangaBlog : Madara("1stKissManga.blog", "https://1stkissmanga.blog", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/firstkissmangaclub/src/FirstKissMangaClub.kt b/multisrc/overrides/madara/firstkissmangaclub/src/FirstKissMangaClub.kt index 71b2e1916d..1f9e3d3b8f 100644 --- a/multisrc/overrides/madara/firstkissmangaclub/src/FirstKissMangaClub.kt +++ b/multisrc/overrides/madara/firstkissmangaclub/src/FirstKissMangaClub.kt @@ -10,7 +10,7 @@ class FirstKissMangaClub : Madara( "en", ) { - override val client = network.cloudflareClient.newBuilder() + override val client = super.client.newBuilder() .rateLimit(1, 3, TimeUnit.SECONDS) .build() } diff --git a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangalove/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 53308340f2..0000000000 Binary files a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangalove/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 979753d9b0..0000000000 Binary files a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 7d83a1455c..0000000000 Binary files a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index f678027d4c..0000000000 Binary files a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index aaf95b4e1b..0000000000 Binary files a/multisrc/overrides/madara/firstkissmangalove/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmangalove/res/web_hi_res_512.png b/multisrc/overrides/madara/firstkissmangalove/res/web_hi_res_512.png deleted file mode 100644 index c9559e92fb..0000000000 Binary files a/multisrc/overrides/madara/firstkissmangalove/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmangalove/src/FirstKissMangaLove.kt b/multisrc/overrides/madara/firstkissmangalove/src/FirstKissMangaLove.kt deleted file mode 100644 index 3e4ce15889..0000000000 --- a/multisrc/overrides/madara/firstkissmangalove/src/FirstKissMangaLove.kt +++ /dev/null @@ -1,16 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.firstkissmangalove - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import java.util.concurrent.TimeUnit - -class FirstKissMangaLove : Madara( - "1st Kiss Manga.love", - "https://1stkissmanga.love", - "en", -) { - - override val client = network.cloudflareClient.newBuilder() - .rateLimit(1, 3, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/firstkissmangatv/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..36d12e82fe Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangatv/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..bd9d5104ae Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..006852662b Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..519f14eeb7 Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..709f8a0844 Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangatv/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstkissmangatv/res/web_hi_res_512.png b/multisrc/overrides/madara/firstkissmangatv/res/web_hi_res_512.png new file mode 100644 index 0000000000..2f86f7755e Binary files /dev/null and b/multisrc/overrides/madara/firstkissmangatv/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/firstkissmangatv/src/FirstKissMangaTv.kt b/multisrc/overrides/madara/firstkissmangatv/src/FirstKissMangaTv.kt new file mode 100644 index 0000000000..794df0d49a --- /dev/null +++ b/multisrc/overrides/madara/firstkissmangatv/src/FirstKissMangaTv.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.firstkissmangatv + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class FirstKissMangaTv : Madara("1stKissManga.tv", "https://1stkissmanga.tv", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmanhua/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index d3f01d2b14..0000000000 Binary files a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmanhua/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 854a17abc9..0000000000 Binary files a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index d0279e2dd5..0000000000 Binary files a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d421b8e18f..0000000000 Binary files a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index a6df0ba573..0000000000 Binary files a/multisrc/overrides/madara/firstkissmanhua/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmanhua/res/web_hi_res_512.png b/multisrc/overrides/madara/firstkissmanhua/res/web_hi_res_512.png deleted file mode 100644 index c48ce78955..0000000000 Binary files a/multisrc/overrides/madara/firstkissmanhua/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/firstkissmanhua/src/FirstKissManhua.kt b/multisrc/overrides/madara/firstkissmanhua/src/FirstKissManhua.kt deleted file mode 100644 index 9afdeb94f6..0000000000 --- a/multisrc/overrides/madara/firstkissmanhua/src/FirstKissManhua.kt +++ /dev/null @@ -1,24 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.firstkissmanhua - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.Page -import okhttp3.Request -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class FirstKissManhua : Madara( - "1st Kiss Manhua", - "https://1stkissmanhua.com", - "en", - SimpleDateFormat("d MMM yyyy", Locale.US), -) { - - override val client = network.cloudflareClient.newBuilder() - .rateLimit(1, 3, TimeUnit.SECONDS) - .build() - - override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headersBuilder().add("Referer", "https://1stkissmanga.com").build()) -} diff --git a/multisrc/overrides/madara/firstmanhwa/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/firstmanhwa/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..4c01a60c66 Binary files /dev/null and b/multisrc/overrides/madara/firstmanhwa/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstmanhwa/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/firstmanhwa/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..153aa31447 Binary files /dev/null and b/multisrc/overrides/madara/firstmanhwa/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstmanhwa/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/firstmanhwa/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..6e2a6c4afa Binary files /dev/null and b/multisrc/overrides/madara/firstmanhwa/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstmanhwa/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstmanhwa/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2767aaf02d Binary files /dev/null and b/multisrc/overrides/madara/firstmanhwa/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstmanhwa/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/firstmanhwa/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..e666bd3c6d Binary files /dev/null and b/multisrc/overrides/madara/firstmanhwa/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/firstmanhwa/res/web_hi_res_512.png b/multisrc/overrides/madara/firstmanhwa/res/web_hi_res_512.png new file mode 100644 index 0000000000..0d45512480 Binary files /dev/null and b/multisrc/overrides/madara/firstmanhwa/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/firstmanhwa/src/FirstManhwa.kt b/multisrc/overrides/madara/firstmanhwa/src/FirstManhwa.kt new file mode 100644 index 0000000000..d7569cbd09 --- /dev/null +++ b/multisrc/overrides/madara/firstmanhwa/src/FirstManhwa.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.en.firstmanhwa + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class FirstManhwa : Madara("1st Manhwa", "https://1stmanhwa.com", "en") { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/fizmanga/src/FizManga.kt b/multisrc/overrides/madara/fizmanga/src/FizManga.kt index 91dd0cb750..b057a54a9b 100644 --- a/multisrc/overrides/madara/fizmanga/src/FizManga.kt +++ b/multisrc/overrides/madara/fizmanga/src/FizManga.kt @@ -1,9 +1,5 @@ package eu.kanade.tachiyomi.extension.en.fizmanga import eu.kanade.tachiyomi.multisrc.madara.Madara -import okhttp3.Headers -class FizManga : Madara("Fiz Manga", "https://fizmanga.com", "en") { - override fun headersBuilder(): Headers.Builder = super.headersBuilder() - .add("Referer", baseUrl) -} +class FizManga : Madara("Fiz Manga", "https://fizmanga.com", "en") diff --git a/multisrc/overrides/madara/fleurblanche/src/FleurBlanche.kt b/multisrc/overrides/madara/fleurblanche/src/FleurBlanche.kt index 5f3635289a..336d6c8a5d 100644 --- a/multisrc/overrides/madara/fleurblanche/src/FleurBlanche.kt +++ b/multisrc/overrides/madara/fleurblanche/src/FleurBlanche.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.pt.fleurblanche import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.Headers import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Response @@ -25,8 +24,6 @@ class FleurBlanche : Madara( override val useNewChapterEndpoint = true - override fun headersBuilder(): Headers.Builder = Headers.Builder() - private fun authWarningIntercept(chain: Interceptor.Chain): Response { val response = chain.proceed(chain.request()) diff --git a/multisrc/overrides/madara/frdashscan/src/FRScan.kt b/multisrc/overrides/madara/frdashscan/src/FRScan.kt index eb270757ea..99837e8a82 100644 --- a/multisrc/overrides/madara/frdashscan/src/FRScan.kt +++ b/multisrc/overrides/madara/frdashscan/src/FRScan.kt @@ -4,4 +4,4 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import java.text.SimpleDateFormat import java.util.Locale -class FRScan : Madara("FR-Scan", "https://fr-scan.com", "fr", dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.FRANCE)) +class FRScan : Madara("FR-Scan", "https://fr-scan.cc", "fr", dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.FRANCE)) diff --git a/multisrc/overrides/madara/freemanhwa/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/freemanhwa/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d61275c78b Binary files /dev/null and b/multisrc/overrides/madara/freemanhwa/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/freemanhwa/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/freemanhwa/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..e10f48a1cc Binary files /dev/null and b/multisrc/overrides/madara/freemanhwa/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/freemanhwa/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/freemanhwa/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..74537cee6b Binary files /dev/null and b/multisrc/overrides/madara/freemanhwa/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/freemanhwa/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/freemanhwa/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ae7b8396dc Binary files /dev/null and b/multisrc/overrides/madara/freemanhwa/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/freemanhwa/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/freemanhwa/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0a31331676 Binary files /dev/null and b/multisrc/overrides/madara/freemanhwa/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/freemanhwa/res/web_hi_res_512.png b/multisrc/overrides/madara/freemanhwa/res/web_hi_res_512.png new file mode 100644 index 0000000000..15f3102c8a Binary files /dev/null and b/multisrc/overrides/madara/freemanhwa/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/freemanhwa/src/FreeManhwa.kt b/multisrc/overrides/madara/freemanhwa/src/FreeManhwa.kt new file mode 100644 index 0000000000..3b86977e47 --- /dev/null +++ b/multisrc/overrides/madara/freemanhwa/src/FreeManhwa.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.freemanhwa + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class FreeManhwa : Madara("Free Manhwa", "https://manhwas.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/furioscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/furioscans/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 6e7fcaa133..0000000000 Binary files a/multisrc/overrides/madara/furioscans/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/furioscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/furioscans/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 53e69abafd..0000000000 Binary files a/multisrc/overrides/madara/furioscans/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/furioscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/furioscans/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 4ce9567f76..0000000000 Binary files a/multisrc/overrides/madara/furioscans/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/furioscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/furioscans/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 6c5d05fd29..0000000000 Binary files a/multisrc/overrides/madara/furioscans/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/furioscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/furioscans/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index f1bc366cfc..0000000000 Binary files a/multisrc/overrides/madara/furioscans/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/furioscans/res/web_hi_res_512.png b/multisrc/overrides/madara/furioscans/res/web_hi_res_512.png deleted file mode 100644 index deaad95709..0000000000 Binary files a/multisrc/overrides/madara/furioscans/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/furioscans/src/FurioScans.kt b/multisrc/overrides/madara/furioscans/src/FurioScans.kt deleted file mode 100644 index 2436dc4de6..0000000000 --- a/multisrc/overrides/madara/furioscans/src/FurioScans.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.furioscans - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class FurioScans : Madara( - "Furio Scans", - "https://furioscans.com", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/ghostscan/src/GhostScan.kt b/multisrc/overrides/madara/ghostscan/src/GhostScan.kt new file mode 100644 index 0000000000..577fac1275 --- /dev/null +++ b/multisrc/overrides/madara/ghostscan/src/GhostScan.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.extension.pt.ghostscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.OkHttpClient +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class GhostScan : Madara( + "Ghost Scan", + "https://ghostscan.com.br", + "pt-BR", + SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), +) { + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() + + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/girlslovemanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/girlslovemanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..221c1073b6 Binary files /dev/null and b/multisrc/overrides/madara/girlslovemanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/girlslovemanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/girlslovemanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ed34c81628 Binary files /dev/null and b/multisrc/overrides/madara/girlslovemanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/girlslovemanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/girlslovemanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..5e5bc117fc Binary files /dev/null and b/multisrc/overrides/madara/girlslovemanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/girlslovemanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/girlslovemanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..94cc3d70b4 Binary files /dev/null and b/multisrc/overrides/madara/girlslovemanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/girlslovemanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/girlslovemanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..5417070717 Binary files /dev/null and b/multisrc/overrides/madara/girlslovemanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/girlslovemanga/res/web_hi_res_512.png b/multisrc/overrides/madara/girlslovemanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..6f26e4d462 Binary files /dev/null and b/multisrc/overrides/madara/girlslovemanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/girlslovemanga/src/GirlsLoveManga.kt b/multisrc/overrides/madara/girlslovemanga/src/GirlsLoveManga.kt new file mode 100644 index 0000000000..76ce55f5f5 --- /dev/null +++ b/multisrc/overrides/madara/girlslovemanga/src/GirlsLoveManga.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.girlslovemanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class GirlsLoveManga : Madara("Girls Love Manga!", "https://glmanga.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/goodgirlsscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..9411becc21 Binary files /dev/null and b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/goodgirlsscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..16f7e03e4d Binary files /dev/null and b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b62027ea0c Binary files /dev/null and b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6ebc4a6842 Binary files /dev/null and b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1de17cb30b Binary files /dev/null and b/multisrc/overrides/madara/goodgirlsscan/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/goodgirlsscan/res/web_hi_res_512.png b/multisrc/overrides/madara/goodgirlsscan/res/web_hi_res_512.png new file mode 100644 index 0000000000..f5ea283b49 Binary files /dev/null and b/multisrc/overrides/madara/goodgirlsscan/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/goodgirlsscan/src/GoodGirlsScan.kt b/multisrc/overrides/madara/goodgirlsscan/src/GoodGirlsScan.kt new file mode 100644 index 0000000000..0ec7c82bf4 --- /dev/null +++ b/multisrc/overrides/madara/goodgirlsscan/src/GoodGirlsScan.kt @@ -0,0 +1,89 @@ +package eu.kanade.tachiyomi.extension.en.goodgirlsscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.FormBody +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import org.jsoup.nodes.Element + +class GoodGirlsScan : Madara("Good Girls Scan", "https://goodgirls.moe", "en") { + + override val fetchGenres = false + override fun popularMangaNextPageSelector() = "body:not(:has(.no-posts))" + override fun searchMangaSelector() = "article.wp-manga" + override fun searchMangaNextPageSelector() = "div.paginator .nav-next" + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + override val mangaDetailsSelectorDescription = "div.summary-specialfields" + override fun chapterListSelector() = "li.wp-manga-chapter:not(.vip-permission)" + + private fun madaraLoadMoreRequest(page: Int, metaKey: String): Request { + val formBody = FormBody.Builder().apply { + add("action", "madara_load_more") + add("page", page.toString()) + add("template", "madara-core/content/content-archive") + add("vars[paged]", "1") + add("vars[orderby]", "meta_value_num") + add("vars[template]", "archive") + add("vars[sidebar]", "right") + add("vars[post_type]", "wp-manga") + add("vars[post_status]", "publish") + add("vars[meta_key]", metaKey) + add("vars[meta_query][0][paged]", "1") + add("vars[meta_query][0][orderby]", "meta_value_num") + add("vars[meta_query][0][template]", "archive") + add("vars[meta_query][0][sidebar]", "right") + add("vars[meta_query][0][post_type]", "wp-manga") + add("vars[meta_query][0][post_status]", "publish") + add("vars[meta_query][0][meta_key]", metaKey) + add("vars[meta_query][relation]", "AND") + add("vars[manga_archives_item_layout]", "default") + }.build() + + val xhrHeaders = headersBuilder() + .add("Content-Length", formBody.contentLength().toString()) + .add("Content-Type", formBody.contentType().toString()) + .add("X-Requested-With", "XMLHttpRequest") + .build() + + return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, formBody) + } + + override fun popularMangaRequest(page: Int): Request { + return madaraLoadMoreRequest(page - 1, "_wp_manga_views") + } + + override fun latestUpdatesRequest(page: Int): Request { + return madaraLoadMoreRequest(page - 1, "_latest_update") + } + + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } + + // heavily modified madara theme, throws 5xx errors on any search filter + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = "$baseUrl/${searchPage(page)}".toHttpUrl().newBuilder().apply { + addQueryParameter("s", query.trim()) + }.build() + + return GET(url, headers) + } + + override fun getFilterList() = FilterList() + + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + element.select(".entry-title a").let { + setUrlWithoutDomain(it.attr("href")) + title = it.text() + } + thumbnail_url = element.selectFirst(".post-thumbnail img")?.let(::imageFromElement) + } +} diff --git a/multisrc/overrides/madara/grabberzone/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/grabberzone/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..38d18ddbfe Binary files /dev/null and b/multisrc/overrides/madara/grabberzone/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/grabberzone/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/grabberzone/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..f957266b14 Binary files /dev/null and b/multisrc/overrides/madara/grabberzone/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/grabberzone/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/grabberzone/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..7cebc98427 Binary files /dev/null and b/multisrc/overrides/madara/grabberzone/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/grabberzone/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/grabberzone/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..dede1ebffa Binary files /dev/null and b/multisrc/overrides/madara/grabberzone/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/grabberzone/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/grabberzone/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9e48c5591f Binary files /dev/null and b/multisrc/overrides/madara/grabberzone/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/grabberzone/res/web_hi_res_512.png b/multisrc/overrides/madara/grabberzone/res/web_hi_res_512.png new file mode 100644 index 0000000000..f831ed6857 Binary files /dev/null and b/multisrc/overrides/madara/grabberzone/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/grabberzone/src/GrabberZone.kt b/multisrc/overrides/madara/grabberzone/src/GrabberZone.kt new file mode 100644 index 0000000000..98a3b3502e --- /dev/null +++ b/multisrc/overrides/madara/grabberzone/src/GrabberZone.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.extension.all.grabberzone + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SChapter +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +class GrabberZone : Madara( + "Grabber Zone", + "https://grabber.zone", + "all", + SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH), +) { + override val mangaSubString = "comics" + + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } + + override fun chapterFromElement(element: Element): SChapter { + return super.chapterFromElement(element).apply { + name = element.selectFirst("a + a")!!.text() + } + } +} diff --git a/multisrc/overrides/madara/harimanga/src/Harimanga.kt b/multisrc/overrides/madara/harimanga/src/Harimanga.kt index 9af4f9751f..6fbeffa786 100644 --- a/multisrc/overrides/madara/harimanga/src/Harimanga.kt +++ b/multisrc/overrides/madara/harimanga/src/Harimanga.kt @@ -1,7 +1,5 @@ package eu.kanade.tachiyomi.extension.en.harimanga import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale -class Harimanga : Madara("Harimanga", "https://harimanga.com", "en", SimpleDateFormat("MM/dd/yyyy", Locale.US)) +class Harimanga : Madara("Harimanga", "https://harimanga.com", "en") diff --git a/multisrc/overrides/madara/helascan/src/HelaScan.kt b/multisrc/overrides/madara/helascan/src/HelaScan.kt deleted file mode 100644 index f6914e2792..0000000000 --- a/multisrc/overrides/madara/helascan/src/HelaScan.kt +++ /dev/null @@ -1,24 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.helascan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class HelaScan : Madara( - "Hela Scan", - "https://helascan.com", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true - - override val mangaDetailsSelectorTag = "" -} diff --git a/multisrc/overrides/madara/hentai3z/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/hentai3z/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..7825c08b7e Binary files /dev/null and b/multisrc/overrides/madara/hentai3z/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai3z/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/hentai3z/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ddcbe1ecca Binary files /dev/null and b/multisrc/overrides/madara/hentai3z/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai3z/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/hentai3z/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2635b6e493 Binary files /dev/null and b/multisrc/overrides/madara/hentai3z/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai3z/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/hentai3z/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3edefcaf43 Binary files /dev/null and b/multisrc/overrides/madara/hentai3z/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai3z/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/hentai3z/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cf5633a64d Binary files /dev/null and b/multisrc/overrides/madara/hentai3z/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai3z/res/web_hi_res_512.png b/multisrc/overrides/madara/hentai3z/res/web_hi_res_512.png new file mode 100644 index 0000000000..a6f227e83a Binary files /dev/null and b/multisrc/overrides/madara/hentai3z/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/hentai3z/src/Hentai3z.kt b/multisrc/overrides/madara/hentai3z/src/Hentai3z.kt new file mode 100644 index 0000000000..4563a8c3d4 --- /dev/null +++ b/multisrc/overrides/madara/hentai3z/src/Hentai3z.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.hentai3z + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Hentai3z : Madara("Hentai3z", "https://hentai3z.xyz", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/hentai4free/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/hentai4free/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..9cd3ea9b58 Binary files /dev/null and b/multisrc/overrides/madara/hentai4free/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai4free/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/hentai4free/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..f6fc19d46f Binary files /dev/null and b/multisrc/overrides/madara/hentai4free/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai4free/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/hentai4free/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..7c10fc9c03 Binary files /dev/null and b/multisrc/overrides/madara/hentai4free/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai4free/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/hentai4free/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6f398266dc Binary files /dev/null and b/multisrc/overrides/madara/hentai4free/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai4free/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/hentai4free/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..13856ef345 Binary files /dev/null and b/multisrc/overrides/madara/hentai4free/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentai4free/res/web_hi_res_512.png b/multisrc/overrides/madara/hentai4free/res/web_hi_res_512.png new file mode 100644 index 0000000000..13412e22b2 Binary files /dev/null and b/multisrc/overrides/madara/hentai4free/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/hentai4free/src/Hentai4Free.kt b/multisrc/overrides/madara/hentai4free/src/Hentai4Free.kt new file mode 100644 index 0000000000..d8b2e7729e --- /dev/null +++ b/multisrc/overrides/madara/hentai4free/src/Hentai4Free.kt @@ -0,0 +1,50 @@ +package eu.kanade.tachiyomi.extension.en.hentai4free + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.FilterList +import okhttp3.Request + +class Hentai4Free : Madara("Hentai4Free", "https://hentai4free.net", "en") { + override val useNewChapterEndpoint = true + override val mangaSubString = "hentai" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" + + override fun popularMangaSelector() = searchMangaSelector() + + override fun popularMangaRequest(page: Int): Request = + searchMangaRequest( + page, + "", + FilterList( + listOf( + OrderByFilter( + "", + listOf( + Pair("", ""), + Pair("", "views"), + ), + 1, + ), + ), + ), + ) + + override fun latestUpdatesRequest(page: Int): Request = + searchMangaRequest( + page, + "", + FilterList( + listOf( + OrderByFilter( + "", + listOf( + Pair("", ""), + Pair("", "latest"), + ), + 1, + ), + ), + ), + ) +} diff --git a/multisrc/overrides/madara/hentaicube/src/HentaiCB.kt b/multisrc/overrides/madara/hentaicube/src/HentaiCB.kt index eb1d34a6b1..9ae77feb79 100644 --- a/multisrc/overrides/madara/hentaicube/src/HentaiCB.kt +++ b/multisrc/overrides/madara/hentaicube/src/HentaiCB.kt @@ -6,7 +6,7 @@ import org.jsoup.nodes.Document import java.text.SimpleDateFormat import java.util.Locale -class HentaiCB : Madara("Hentai CB", "https://cubeteam.xyz", "vi", SimpleDateFormat("dd/MM/yyyy", Locale("vi"))) { +class HentaiCB : Madara("Hentai CB", "https://hentaicube.net", "vi", SimpleDateFormat("dd/MM/yyyy", Locale("vi"))) { override val id: Long = 823638192569572166 override fun pageListParse(document: Document): List { return super.pageListParse(document).distinctBy { it.imageUrl } diff --git a/multisrc/overrides/madara/hentaiteca/src/HentaiTeca.kt b/multisrc/overrides/madara/hentaiteca/src/HentaiTeca.kt index 5689d18f50..f63de0c24f 100644 --- a/multisrc/overrides/madara/hentaiteca/src/HentaiTeca.kt +++ b/multisrc/overrides/madara/hentaiteca/src/HentaiTeca.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.pt.hentaiteca import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.Headers import okhttp3.OkHttpClient import java.text.SimpleDateFormat import java.util.Locale @@ -18,7 +17,4 @@ class HentaiTeca : Madara( override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 2, TimeUnit.SECONDS) .build() - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("Referer", "$baseUrl/") } diff --git a/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..f8eb032487 Binary files /dev/null and b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..619332776a Binary files /dev/null and b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..0816dc0114 Binary files /dev/null and b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..fa25607252 Binary files /dev/null and b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0986c1539d Binary files /dev/null and b/multisrc/overrides/madara/hentaixdickgirl/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/hentaixdickgirl/res/web_hi_res_512.png b/multisrc/overrides/madara/hentaixdickgirl/res/web_hi_res_512.png new file mode 100644 index 0000000000..5205361787 Binary files /dev/null and b/multisrc/overrides/madara/hentaixdickgirl/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/hentaixdickgirl/src/HentaiXDickgirl.kt b/multisrc/overrides/madara/hentaixdickgirl/src/HentaiXDickgirl.kt new file mode 100644 index 0000000000..1e6704a684 --- /dev/null +++ b/multisrc/overrides/madara/hentaixdickgirl/src/HentaiXDickgirl.kt @@ -0,0 +1,23 @@ +package eu.kanade.tachiyomi.extension.en.hentaixdickgirl + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import org.jsoup.nodes.Document + +class HentaiXDickgirl : Madara("HentaiXDickgirl", "https://hentaixdickgirl.com", "en") { + + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } + + override fun mangaDetailsParse(document: Document): SManga { + return super.mangaDetailsParse(document).apply { + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + } + } +} diff --git a/multisrc/overrides/madara/hentaizone/src/HentaiZone.kt b/multisrc/overrides/madara/hentaizone/src/HentaiZone.kt new file mode 100644 index 0000000000..bc4d2b4a37 --- /dev/null +++ b/multisrc/overrides/madara/hentaizone/src/HentaiZone.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.fr.hentaizone + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class HentaiZone : Madara("HentaiZone", "https://hentaizone.xyz", "fr", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.FRENCH)) { + override val mangaSubString = "tous-les-mangas" +} diff --git a/multisrc/overrides/madara/hiperdex/src/Hiperdex.kt b/multisrc/overrides/madara/hiperdex/src/Hiperdex.kt index 9f7b72d805..7f01997905 100644 --- a/multisrc/overrides/madara/hiperdex/src/Hiperdex.kt +++ b/multisrc/overrides/madara/hiperdex/src/Hiperdex.kt @@ -9,10 +9,10 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -class Hiperdex : Madara("Hiperdex", "https://1sthiperdex.com", "en") { +class Hiperdex : Madara("Hiperdex", "https://hiperdex.com", "en") { override val useNewChapterEndpoint: Boolean = true - private val defaultBaseUrl = "https://1sthiperdex.com" + private val defaultBaseUrl = "https://hiperdex.com" override val baseUrl by lazy { getPrefBaseUrl() } diff --git a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 10ce53b207..0000000000 Binary files a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index dbf838111a..0000000000 Binary files a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index c80807b318..0000000000 Binary files a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d3ede62067..0000000000 Binary files a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 56cd6d5a46..0000000000 Binary files a/multisrc/overrides/madara/ichirinnohanayuri/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ichirinnohanayuri/res/web_hi_res_512.png b/multisrc/overrides/madara/ichirinnohanayuri/res/web_hi_res_512.png deleted file mode 100644 index 066c5dfa4a..0000000000 Binary files a/multisrc/overrides/madara/ichirinnohanayuri/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/ichirinnohanayuri/src/IchirinNoHanaYuri.kt b/multisrc/overrides/madara/ichirinnohanayuri/src/IchirinNoHanaYuri.kt deleted file mode 100644 index 9c5987f09b..0000000000 --- a/multisrc/overrides/madara/ichirinnohanayuri/src/IchirinNoHanaYuri.kt +++ /dev/null @@ -1,39 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.ichirinnohanayuri - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.Headers -import okhttp3.OkHttpClient -import java.io.IOException -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class IchirinNoHanaYuri : Madara( - "Ichirin No Hana Yuri", - "https://ichirinnohanayuriscan.com", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .addInterceptor { chain -> - val response = chain.proceed(chain.request()) - - if (response.code == 403) { - response.close() - throw IOException(BLOCKING_MESSAGE) - } - - response - } - .build() - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - - companion object { - private const val BLOCKING_MESSAGE = "O site está bloqueando o Tachiyomi. " + - "Migre para outra fonte caso o problema persistir." - } -} diff --git a/multisrc/overrides/madara/isekaiscaneu/src/IsekaiScanTo.kt b/multisrc/overrides/madara/isekaiscaneu/src/IsekaiScanTo.kt index 7cd4ebadf3..45797cb35e 100644 --- a/multisrc/overrides/madara/isekaiscaneu/src/IsekaiScanTo.kt +++ b/multisrc/overrides/madara/isekaiscaneu/src/IsekaiScanTo.kt @@ -4,6 +4,8 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import java.text.SimpleDateFormat import java.util.Locale -class IsekaiScanTo : Madara("IsekaiScan.to (unoriginal)", "https://isekaiscan.to", "en", SimpleDateFormat("MM/dd/yyyy", Locale.US)) { +class IsekaiScanTo : Madara("IsekaiScan.to (unoriginal)", "https://m.isekaiscan.to", "en", SimpleDateFormat("MM/dd/yyyy", Locale.US)) { override val id = 8608305834807261892L; // from former IsekaiScan.eu source + + override val mangaSubString = "mangax" } diff --git a/multisrc/overrides/madara/isekaiscantop/src/IsekaiScanTop.kt b/multisrc/overrides/madara/isekaiscantop/src/IsekaiScanTop.kt index e29c02c568..54614826c4 100644 --- a/multisrc/overrides/madara/isekaiscantop/src/IsekaiScanTop.kt +++ b/multisrc/overrides/madara/isekaiscantop/src/IsekaiScanTop.kt @@ -41,7 +41,6 @@ class IsekaiScanTop : Madara( if (chapterElements.isEmpty() && !chaptersWrapper.isNullOrEmpty()) { val mangaId = chaptersWrapper.attr("data-id") val xhrHeaders = headersBuilder() - .add("Referer", "$baseUrl/") .add("X-Requested-With", "XMLHttpRequest") .build() val xhrRequest = GET("$baseUrl/ajax-list-chapter?mangaID=$mangaId", xhrHeaders) diff --git a/multisrc/overrides/madara/jimanga/src/Jimanga.kt b/multisrc/overrides/madara/jimanga/src/Jimanga.kt new file mode 100644 index 0000000000..df1e41e133 --- /dev/null +++ b/multisrc/overrides/madara/jimanga/src/Jimanga.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.jimanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Jimanga : Madara("Jimanga", "https://jimanga.com", "en") { + override val useNewChapterEndpoint = false + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/kalangoscan/src/KalangoScan.kt b/multisrc/overrides/madara/kalangoscan/src/KalangoScan.kt deleted file mode 100644 index e5f19ebeec..0000000000 --- a/multisrc/overrides/madara/kalangoscan/src/KalangoScan.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.kalangoscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class KalangoScan : Madara( - "Kalango Scan", - "https://kalangoscan.online", - "pt-BR", - SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/kataitake/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/kataitake/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..8a7c31d5e0 Binary files /dev/null and b/multisrc/overrides/madara/kataitake/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kataitake/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/kataitake/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..0c1bc2802f Binary files /dev/null and b/multisrc/overrides/madara/kataitake/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kataitake/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/kataitake/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..1d29bf452c Binary files /dev/null and b/multisrc/overrides/madara/kataitake/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kataitake/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/kataitake/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..670bf77366 Binary files /dev/null and b/multisrc/overrides/madara/kataitake/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kataitake/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/kataitake/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cef93935d8 Binary files /dev/null and b/multisrc/overrides/madara/kataitake/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kataitake/res/web_hi_res_512.png b/multisrc/overrides/madara/kataitake/res/web_hi_res_512.png new file mode 100644 index 0000000000..6b3e83781a Binary files /dev/null and b/multisrc/overrides/madara/kataitake/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/kataitake/src/Kataitake.kt b/multisrc/overrides/madara/kataitake/src/Kataitake.kt new file mode 100644 index 0000000000..99080f11bd --- /dev/null +++ b/multisrc/overrides/madara/kataitake/src/Kataitake.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.fr.kataitake + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class Kataitake : Madara("Kataitake", "https://www.kataitake.fr", "fr", dateFormat = SimpleDateFormat("dd/mm/yyyy", Locale.FRANCE)) { + override val altName: String = "Noms alternatifs :" +} diff --git a/multisrc/overrides/madara/kingsmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/kingsmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..e5de1eaf75 Binary files /dev/null and b/multisrc/overrides/madara/kingsmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kingsmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/kingsmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..70ccfe481e Binary files /dev/null and b/multisrc/overrides/madara/kingsmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kingsmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/kingsmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..cdb900aa7d Binary files /dev/null and b/multisrc/overrides/madara/kingsmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kingsmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/kingsmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3f9485894b Binary files /dev/null and b/multisrc/overrides/madara/kingsmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kingsmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/kingsmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ad0c337369 Binary files /dev/null and b/multisrc/overrides/madara/kingsmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/kingsmanga/res/web_hi_res_512.png b/multisrc/overrides/madara/kingsmanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..c15978aa10 Binary files /dev/null and b/multisrc/overrides/madara/kingsmanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/kingsmanga/src/KingsManga.kt b/multisrc/overrides/madara/kingsmanga/src/KingsManga.kt new file mode 100644 index 0000000000..adf99355df --- /dev/null +++ b/multisrc/overrides/madara/kingsmanga/src/KingsManga.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.th.kingsmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class KingsManga : Madara( + "Kings-Manga", + "https://www.kings-manga.co", + "th", + dateFormat = SimpleDateFormat("d MMMM yyyy", Locale("th")), +) { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/koinoboriscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/koinoboriscan/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..4e63d01ef8 Binary files /dev/null and b/multisrc/overrides/madara/koinoboriscan/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/koinoboriscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/koinoboriscan/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..cb3bf91585 Binary files /dev/null and b/multisrc/overrides/madara/koinoboriscan/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/koinoboriscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/koinoboriscan/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..67ee088e6a Binary files /dev/null and b/multisrc/overrides/madara/koinoboriscan/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/koinoboriscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/koinoboriscan/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1e363666d4 Binary files /dev/null and b/multisrc/overrides/madara/koinoboriscan/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/koinoboriscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/koinoboriscan/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cc3885bc78 Binary files /dev/null and b/multisrc/overrides/madara/koinoboriscan/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/koinoboriscan/res/web_hi_res_512.png b/multisrc/overrides/madara/koinoboriscan/res/web_hi_res_512.png new file mode 100644 index 0000000000..54ec5ad667 Binary files /dev/null and b/multisrc/overrides/madara/koinoboriscan/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/koinoboriscan/src/KoinoboriScan.kt b/multisrc/overrides/madara/koinoboriscan/src/KoinoboriScan.kt new file mode 100644 index 0000000000..332da54fea --- /dev/null +++ b/multisrc/overrides/madara/koinoboriscan/src/KoinoboriScan.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.es.koinoboriscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import java.text.SimpleDateFormat +import java.util.Locale + +class KoinoboriScan : Madara( + "Koinobori Scan", + "https://koinoboriscan.com", + "es", + SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) { + override val client = super.client.newBuilder() + .rateLimit(2, 1) + .build() +} diff --git a/multisrc/overrides/madara/komikgue/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/komikgue/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..6aa99fd502 Binary files /dev/null and b/multisrc/overrides/madara/komikgue/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/komikgue/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/komikgue/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..006ad96cc1 Binary files /dev/null and b/multisrc/overrides/madara/komikgue/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/komikgue/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/komikgue/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..6b2257f69c Binary files /dev/null and b/multisrc/overrides/madara/komikgue/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/komikgue/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/komikgue/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..c021046ced Binary files /dev/null and b/multisrc/overrides/madara/komikgue/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/komikgue/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/komikgue/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1822e6a36a Binary files /dev/null and b/multisrc/overrides/madara/komikgue/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/komikgue/res/web_hi_res_512.png b/multisrc/overrides/madara/komikgue/res/web_hi_res_512.png new file mode 100644 index 0000000000..2a283540a1 Binary files /dev/null and b/multisrc/overrides/madara/komikgue/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/komikgue/src/KomikGue.kt b/multisrc/overrides/madara/komikgue/src/KomikGue.kt new file mode 100644 index 0000000000..d067b98c4b --- /dev/null +++ b/multisrc/overrides/madara/komikgue/src/KomikGue.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.id.komikgue + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class KomikGue : Madara( + "Komik Gue", + "https://komikgue.pro", + "id", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id")), +) { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/legionscan/src/LegionScan.kt b/multisrc/overrides/madara/legionscan/src/LegionScan.kt deleted file mode 100644 index 7ed52c5497..0000000000 --- a/multisrc/overrides/madara/legionscan/src/LegionScan.kt +++ /dev/null @@ -1,7 +0,0 @@ -package eu.kanade.tachiyomi.extension.es.legionscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class LegionScan : Madara("Legion Scan", "https://legionscans.com", "es", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es"))) diff --git a/multisrc/overrides/madara/leviatanscans/src/LeviatanScans.kt b/multisrc/overrides/madara/leviatanscans/src/LeviatanScans.kt index 247bd399c0..611cb5e874 100644 --- a/multisrc/overrides/madara/leviatanscans/src/LeviatanScans.kt +++ b/multisrc/overrides/madara/leviatanscans/src/LeviatanScans.kt @@ -1,21 +1,24 @@ -package eu.kanade.tachiyomi.extension.all.leviatanscans +package eu.kanade.tachiyomi.extension.en.leviatanscans import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import org.jsoup.nodes.Element import java.text.SimpleDateFormat +import java.util.Locale -abstract class LeviatanScans( - baseUrl: String, - lang: String, - dateFormat: SimpleDateFormat, -) : Madara( +class LeviatanScans : Madara( "Leviatan Scans", - baseUrl, - lang, - dateFormat, + "https://lscomic.com", + "en", + dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.US), ) { + + override val id = 4055499394183150749 + + override val mangaDetailsSelectorDescription = "div.manga-summary" + override val mangaDetailsSelectorAuthor = "div.manga-authors" + override val useNewChapterEndpoint: Boolean = true override fun chapterListSelector() = "li.wp-manga-chapter:not(.premium-block)" diff --git a/multisrc/overrides/madara/leviatanscans/src/LeviatanScansFactory.kt b/multisrc/overrides/madara/leviatanscans/src/LeviatanScansFactory.kt deleted file mode 100644 index d110edad1a..0000000000 --- a/multisrc/overrides/madara/leviatanscans/src/LeviatanScansFactory.kt +++ /dev/null @@ -1,36 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.leviatanscans - -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.source.SourceFactory -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale - -class LeviatanScansFactory : SourceFactory { - override fun createSources(): List = listOf( - LeviatanScansEN(), - LeviatanScansES(), - ) -} - -class LeviatanScansEN : LeviatanScans( - "https://en.leviatanscans.com", - "en", - SimpleDateFormat("MMM dd, yyyy", Locale.US), -) { - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2) - .build() - - override val mangaDetailsSelectorDescription = "div.manga-summary" - override val mangaDetailsSelectorAuthor = "div.manga-authors" -} - -class LeviatanScansES : LeviatanScans( - "https://es.leviatanscans.com", - "es", - SimpleDateFormat("MMM dd, yy", Locale("es")), -) { - override val mangaDetailsSelectorStatus = ".post-content_item:contains(Status) .summary-content" -} diff --git a/multisrc/overrides/madara/limascans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/limascans/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 9689b437a8..0000000000 Binary files a/multisrc/overrides/madara/limascans/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/limascans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/limascans/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index c386746eeb..0000000000 Binary files a/multisrc/overrides/madara/limascans/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/limascans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/limascans/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index d7038fb484..0000000000 Binary files a/multisrc/overrides/madara/limascans/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/limascans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/limascans/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 0feba0bae0..0000000000 Binary files a/multisrc/overrides/madara/limascans/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/limascans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/limascans/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 912927f9c9..0000000000 Binary files a/multisrc/overrides/madara/limascans/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/limascans/res/web_hi_res_512.png b/multisrc/overrides/madara/limascans/res/web_hi_res_512.png deleted file mode 100644 index ced8ba5dea..0000000000 Binary files a/multisrc/overrides/madara/limascans/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/limascans/src/LimaScans.kt b/multisrc/overrides/madara/limascans/src/LimaScans.kt deleted file mode 100644 index a811c9afac..0000000000 --- a/multisrc/overrides/madara/limascans/src/LimaScans.kt +++ /dev/null @@ -1,31 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.limascans - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.SManga -import okhttp3.OkHttpClient -import okhttp3.Request -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class LimaScans : Madara( - "Lima Scans", - "http://limascans.xyz/v2", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override fun mangaDetailsRequest(manga: SManga): Request { - return GET(baseUrl + manga.url.removePrefix("/v2"), headers) - } - - override fun chapterListRequest(manga: SManga): Request { - return GET(baseUrl + manga.url.removePrefix("/v2"), headers) - } -} diff --git a/multisrc/overrides/madara/lkscanlation/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/lkscanlation/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..4084c218be Binary files /dev/null and b/multisrc/overrides/madara/lkscanlation/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/lkscanlation/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/lkscanlation/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..5897927bf1 Binary files /dev/null and b/multisrc/overrides/madara/lkscanlation/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/lkscanlation/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/lkscanlation/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..a34adfd897 Binary files /dev/null and b/multisrc/overrides/madara/lkscanlation/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/lkscanlation/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/lkscanlation/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..799a13cbc2 Binary files /dev/null and b/multisrc/overrides/madara/lkscanlation/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/lkscanlation/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/lkscanlation/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..f2809cecdc Binary files /dev/null and b/multisrc/overrides/madara/lkscanlation/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/lkscanlation/res/web_hi_res_512.png b/multisrc/overrides/madara/lkscanlation/res/web_hi_res_512.png new file mode 100644 index 0000000000..6883f143b0 Binary files /dev/null and b/multisrc/overrides/madara/lkscanlation/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/lkscanlation/src/LKScanlation.kt b/multisrc/overrides/madara/lkscanlation/src/LKScanlation.kt new file mode 100644 index 0000000000..ff70698ddb --- /dev/null +++ b/multisrc/overrides/madara/lkscanlation/src/LKScanlation.kt @@ -0,0 +1,26 @@ +package eu.kanade.tachiyomi.extension.es.lkscanlation + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import java.text.SimpleDateFormat +import java.util.Locale + +class LKScanlation : Madara( + "Last Knight Translation", + "https://lkscanlation.com", + "es", + SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) { + override val client = super.client.newBuilder() + .rateLimit(2, 1) + .build() + + override val useNewChapterEndpoint = true + + override val mangaSubString = "manhwa" + + override val popularMangaUrlSelector = "div.post-title a:not([target='_self'])" + + override val mangaDetailsSelectorAuthor = "div.manga-authors > a" + override val mangaDetailsSelectorDescription = "div.manga-summary" +} diff --git a/multisrc/overrides/madara/luffymanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/luffymanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..fd2b83cad9 Binary files /dev/null and b/multisrc/overrides/madara/luffymanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/luffymanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/luffymanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..b70207b1d1 Binary files /dev/null and b/multisrc/overrides/madara/luffymanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/luffymanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/luffymanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..dfd26d5488 Binary files /dev/null and b/multisrc/overrides/madara/luffymanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/luffymanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/luffymanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4cb00558fb Binary files /dev/null and b/multisrc/overrides/madara/luffymanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/luffymanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/luffymanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..fe2edcf4fe Binary files /dev/null and b/multisrc/overrides/madara/luffymanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/luffymanga/res/web_hi_res_512.png b/multisrc/overrides/madara/luffymanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..61ff34eee9 Binary files /dev/null and b/multisrc/overrides/madara/luffymanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/luffymanga/src/LuffyManga.kt b/multisrc/overrides/madara/luffymanga/src/LuffyManga.kt new file mode 100644 index 0000000000..36a37b3afa --- /dev/null +++ b/multisrc/overrides/madara/luffymanga/src/LuffyManga.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.luffymanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class LuffyManga : Madara("Luffy Manga", "https://luffymanga.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/maidscan/src/MaidScan.kt b/multisrc/overrides/madara/maidscan/src/MaidScan.kt new file mode 100644 index 0000000000..2ea8073d09 --- /dev/null +++ b/multisrc/overrides/madara/maidscan/src/MaidScan.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.extension.pt.maidscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.OkHttpClient +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class MaidScan : Madara( + "Maid Scan", + "https://maidscan.com.br", + "pt-BR", + SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), +) { + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() + + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/manga18fx/src/Manga18fx.kt b/multisrc/overrides/madara/manga18fx/src/Manga18fx.kt index 43592535da..87c54b9ea2 100644 --- a/multisrc/overrides/madara/manga18fx/src/Manga18fx.kt +++ b/multisrc/overrides/madara/manga18fx/src/Manga18fx.kt @@ -26,8 +26,6 @@ class Manga18fx : Madara( ) { override val id = 3157287889751723714 - override val client = network.client - override val fetchGenres = false override val sendViewCount = false diff --git a/multisrc/overrides/madara/manga18h/src/Manga18h.kt b/multisrc/overrides/madara/manga18h/src/Manga18h.kt new file mode 100644 index 0000000000..ef951efe49 --- /dev/null +++ b/multisrc/overrides/madara/manga18h/src/Manga18h.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manga18h + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Manga18h : Madara("Manga 18h", "https://manga18h.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manga18x/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manga18x/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..bb728ca0f0 Binary files /dev/null and b/multisrc/overrides/madara/manga18x/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manga18x/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manga18x/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..2e44b77c85 Binary files /dev/null and b/multisrc/overrides/madara/manga18x/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manga18x/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manga18x/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..4ed5e495ce Binary files /dev/null and b/multisrc/overrides/madara/manga18x/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manga18x/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manga18x/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..96a508391f Binary files /dev/null and b/multisrc/overrides/madara/manga18x/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manga18x/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manga18x/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..49621c939d Binary files /dev/null and b/multisrc/overrides/madara/manga18x/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manga18x/res/web_hi_res_512.png b/multisrc/overrides/madara/manga18x/res/web_hi_res_512.png new file mode 100644 index 0000000000..99c220a077 Binary files /dev/null and b/multisrc/overrides/madara/manga18x/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manga18x/src/Manga18x.kt b/multisrc/overrides/madara/manga18x/src/Manga18x.kt new file mode 100644 index 0000000000..509fc67251 --- /dev/null +++ b/multisrc/overrides/madara/manga18x/src/Manga18x.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manga18x + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Manga18x : Madara("Manga 18x", "https://manga18x.net", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangabee/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangabee/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..93c827bc0f Binary files /dev/null and b/multisrc/overrides/madara/mangabee/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangabee/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangabee/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..a14794ca78 Binary files /dev/null and b/multisrc/overrides/madara/mangabee/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangabee/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangabee/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..31e8ceb2fa Binary files /dev/null and b/multisrc/overrides/madara/mangabee/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangabee/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangabee/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3ad483f5ba Binary files /dev/null and b/multisrc/overrides/madara/mangabee/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangabee/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangabee/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..775c3c6b6a Binary files /dev/null and b/multisrc/overrides/madara/mangabee/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangabee/res/web_hi_res_512.png b/multisrc/overrides/madara/mangabee/res/web_hi_res_512.png new file mode 100644 index 0000000000..cc60e72e2d Binary files /dev/null and b/multisrc/overrides/madara/mangabee/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangabee/src/MangaBee.kt b/multisrc/overrides/madara/mangabee/src/MangaBee.kt new file mode 100644 index 0000000000..9a80c001c6 --- /dev/null +++ b/multisrc/overrides/madara/mangabee/src/MangaBee.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.en.mangabee + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaBee : Madara( + "Manga Bee", + "https://mangabee.net", + "en", + dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.ROOT), +) { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangabilgini/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangabilgini/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index f14515d90b..0000000000 Binary files a/multisrc/overrides/madara/mangabilgini/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangabilgini/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangabilgini/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 266b5527f6..0000000000 Binary files a/multisrc/overrides/madara/mangabilgini/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangabilgini/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangabilgini/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index aab5aecb1b..0000000000 Binary files a/multisrc/overrides/madara/mangabilgini/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangabilgini/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangabilgini/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 56cac9f444..0000000000 Binary files a/multisrc/overrides/madara/mangabilgini/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangabilgini/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangabilgini/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 0efbad0854..0000000000 Binary files a/multisrc/overrides/madara/mangabilgini/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangabilgini/res/web_hi_res_512.png b/multisrc/overrides/madara/mangabilgini/res/web_hi_res_512.png deleted file mode 100644 index 1e901e7cec..0000000000 Binary files a/multisrc/overrides/madara/mangabilgini/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangabilgini/src/MangaBilgini.kt b/multisrc/overrides/madara/mangabilgini/src/MangaBilgini.kt deleted file mode 100644 index 47c51c4192..0000000000 --- a/multisrc/overrides/madara/mangabilgini/src/MangaBilgini.kt +++ /dev/null @@ -1,14 +0,0 @@ -package eu.kanade.tachiyomi.extension.tr.mangabilgini - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class MangaBilgini : Madara( - "Manga Bilgini", - "https://mangabilgini.com", - "tr", - dateFormat = SimpleDateFormat("MMM d, yyy", Locale("tr")), -) { - override val useNewChapterEndpoint = false -} diff --git a/multisrc/overrides/madara/mangaclashtv/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaclashtv/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..9c6e5e1808 Binary files /dev/null and b/multisrc/overrides/madara/mangaclashtv/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaclashtv/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaclashtv/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..125060730d Binary files /dev/null and b/multisrc/overrides/madara/mangaclashtv/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaclashtv/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaclashtv/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..1cab39ea25 Binary files /dev/null and b/multisrc/overrides/madara/mangaclashtv/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaclashtv/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaclashtv/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..eec512f713 Binary files /dev/null and b/multisrc/overrides/madara/mangaclashtv/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaclashtv/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaclashtv/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..30ba26fdfe Binary files /dev/null and b/multisrc/overrides/madara/mangaclashtv/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaclashtv/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaclashtv/res/web_hi_res_512.png new file mode 100644 index 0000000000..b64f6b9565 Binary files /dev/null and b/multisrc/overrides/madara/mangaclashtv/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaclashtv/src/MangaClashTv.kt b/multisrc/overrides/madara/mangaclashtv/src/MangaClashTv.kt new file mode 100644 index 0000000000..9f85cc2176 --- /dev/null +++ b/multisrc/overrides/madara/mangaclashtv/src/MangaClashTv.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaclashtv + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaClashTv : Madara("MangaClash.tv (unoriginal)", "https://mangaclash.tv", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangacrab/src/MangaCrab.kt b/multisrc/overrides/madara/mangacrab/src/MangaCrab.kt index d5333863c9..459d507a93 100644 --- a/multisrc/overrides/madara/mangacrab/src/MangaCrab.kt +++ b/multisrc/overrides/madara/mangacrab/src/MangaCrab.kt @@ -7,7 +7,7 @@ import java.util.Locale class MangaCrab : Madara( "Manga Crab", - "https://mangacrab.com", + "https://manga-crab.com", "es", SimpleDateFormat("dd/MM/yyyy", Locale("es")), ) { @@ -15,6 +15,8 @@ class MangaCrab : Madara( .rateLimit(1, 2) .build() + override val mangaSubString = "series" + override fun chapterListSelector() = "div.listing-chapters_wrap > ul > li" - override val mangaDetailsSelectorDescription = "div.c-page__content div.contenedor" + override val mangaDetailsSelectorDescription = "div.c-page__content div.modal-contenido" } diff --git a/multisrc/overrides/madara/mangacrazy/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangacrazy/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..b14dad52b4 Binary files /dev/null and b/multisrc/overrides/madara/mangacrazy/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangacrazy/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangacrazy/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..4905f2c0b6 Binary files /dev/null and b/multisrc/overrides/madara/mangacrazy/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangacrazy/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangacrazy/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..3f3b88fe22 Binary files /dev/null and b/multisrc/overrides/madara/mangacrazy/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangacrazy/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangacrazy/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1f6922f2e1 Binary files /dev/null and b/multisrc/overrides/madara/mangacrazy/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangacrazy/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangacrazy/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..975a5ff3e1 Binary files /dev/null and b/multisrc/overrides/madara/mangacrazy/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangacrazy/res/web_hi_res_512.png b/multisrc/overrides/madara/mangacrazy/res/web_hi_res_512.png new file mode 100644 index 0000000000..2880c3b52b Binary files /dev/null and b/multisrc/overrides/madara/mangacrazy/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangacrazy/src/MangaCrazy.kt b/multisrc/overrides/madara/mangacrazy/src/MangaCrazy.kt new file mode 100644 index 0000000000..c9fbab93f8 --- /dev/null +++ b/multisrc/overrides/madara/mangacrazy/src/MangaCrazy.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.all.mangacrazy + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaCrazy : Madara("MangaCrazy", "https://mangacrazy.net", "all") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangadash1001com/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangadash1001com/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..33f91b06d4 Binary files /dev/null and b/multisrc/overrides/madara/mangadash1001com/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadash1001com/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangadash1001com/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..8a97e25b44 Binary files /dev/null and b/multisrc/overrides/madara/mangadash1001com/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadash1001com/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangadash1001com/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..65907a275b Binary files /dev/null and b/multisrc/overrides/madara/mangadash1001com/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadash1001com/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangadash1001com/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..426772bfac Binary files /dev/null and b/multisrc/overrides/madara/mangadash1001com/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadash1001com/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangadash1001com/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..109ecc4dd3 Binary files /dev/null and b/multisrc/overrides/madara/mangadash1001com/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadash1001com/res/web_hi_res_512.png b/multisrc/overrides/madara/mangadash1001com/res/web_hi_res_512.png new file mode 100644 index 0000000000..a2cb2b9862 Binary files /dev/null and b/multisrc/overrides/madara/mangadash1001com/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangadash1001com/src/MangaDash1001Com.kt b/multisrc/overrides/madara/mangadash1001com/src/MangaDash1001Com.kt new file mode 100644 index 0000000000..88b3cd0ead --- /dev/null +++ b/multisrc/overrides/madara/mangadash1001com/src/MangaDash1001Com.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangadash1001com + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaDash1001Com : Madara("Manga-1001.com", "https://manga-1001.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangadinotop/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangadinotop/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d8bbacd490 Binary files /dev/null and b/multisrc/overrides/madara/mangadinotop/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadinotop/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangadinotop/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ec35ead1d0 Binary files /dev/null and b/multisrc/overrides/madara/mangadinotop/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadinotop/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangadinotop/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..bd959ac04d Binary files /dev/null and b/multisrc/overrides/madara/mangadinotop/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadinotop/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangadinotop/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..58989ca02b Binary files /dev/null and b/multisrc/overrides/madara/mangadinotop/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadinotop/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangadinotop/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3ae51ec0a2 Binary files /dev/null and b/multisrc/overrides/madara/mangadinotop/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangadinotop/res/web_hi_res_512.png b/multisrc/overrides/madara/mangadinotop/res/web_hi_res_512.png new file mode 100644 index 0000000000..a624bd17dd Binary files /dev/null and b/multisrc/overrides/madara/mangadinotop/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangadinotop/src/MangaDinoTop.kt b/multisrc/overrides/madara/mangadinotop/src/MangaDinoTop.kt new file mode 100644 index 0000000000..ef4d3cce17 --- /dev/null +++ b/multisrc/overrides/madara/mangadinotop/src/MangaDinoTop.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangadinotop + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaDinoTop : Madara("MangaDino.top (unoriginal)", "https://mangadino.top", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangadistrict/src/MangaDistrict.kt b/multisrc/overrides/madara/mangadistrict/src/MangaDistrict.kt index 5f10b78e60..515e522955 100644 --- a/multisrc/overrides/madara/mangadistrict/src/MangaDistrict.kt +++ b/multisrc/overrides/madara/mangadistrict/src/MangaDistrict.kt @@ -1,12 +1,11 @@ package eu.kanade.tachiyomi.extension.en.mangadistrict import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale class MangaDistrict : Madara( "Manga District", "https://mangadistrict.com", "en", - dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US), -) +) { + override fun searchMangaNextPageSelector() = "div[role=navigation] a.last" +} diff --git a/multisrc/overrides/madara/mangadods/src/MangaDods.kt b/multisrc/overrides/madara/mangadods/src/MangaDods.kt index 1ae4bcf2c9..777e228ad1 100644 --- a/multisrc/overrides/madara/mangadods/src/MangaDods.kt +++ b/multisrc/overrides/madara/mangadods/src/MangaDods.kt @@ -4,4 +4,12 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import java.text.SimpleDateFormat import java.util.Locale -class MangaDods : Madara("MangaDods", "https://www.mangadods.com", "en", SimpleDateFormat("yyyy-MM-dd", Locale.US)) +class MangaDods : Madara("MangaDods", "https://mangadods.com", "en", SimpleDateFormat("dd-MMM", Locale.US)) { + override val useNewChapterEndpoint = true + + override val chapterUrlSelector = "a:not([style])" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" + + override fun chapterDateSelector() = "span i" +} diff --git a/multisrc/overrides/madara/mangagoyaoi/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..09983f687e Binary files /dev/null and b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangagoyaoi/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..77e4aa64fd Binary files /dev/null and b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..d95dc46ab9 Binary files /dev/null and b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ec53998fe8 Binary files /dev/null and b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..29d1ba100c Binary files /dev/null and b/multisrc/overrides/madara/mangagoyaoi/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangagoyaoi/res/web_hi_res_512.png b/multisrc/overrides/madara/mangagoyaoi/res/web_hi_res_512.png new file mode 100644 index 0000000000..a9978cae15 Binary files /dev/null and b/multisrc/overrides/madara/mangagoyaoi/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangagoyaoi/src/MangaGoYaoi.kt b/multisrc/overrides/madara/mangagoyaoi/src/MangaGoYaoi.kt new file mode 100644 index 0000000000..ec62c7bc45 --- /dev/null +++ b/multisrc/overrides/madara/mangagoyaoi/src/MangaGoYaoi.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangagoyaoi + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaGoYaoi : Madara("MangaGo Yaoi", "https://mangagoyaoi.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangagreat/src/MangaGreat.kt b/multisrc/overrides/madara/mangagreat/src/MangaGreat.kt index 6fddf8cf06..d74b2937af 100644 --- a/multisrc/overrides/madara/mangagreat/src/MangaGreat.kt +++ b/multisrc/overrides/madara/mangagreat/src/MangaGreat.kt @@ -1,9 +1,126 @@ package eu.kanade.tachiyomi.extension.en.mangagreat +import android.util.Base64 +import app.cash.quickjs.QuickJs import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Cookie +import okhttp3.Interceptor +import okhttp3.OkHttpClient +import okhttp3.Response +import java.io.IOException +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec class MangaGreat : Madara("MangaGreat", "https://mangagreat.com", "en") { + override val client: OkHttpClient = super.client.newBuilder() + .addInterceptor(::JSChallengeInterceptor) + .build() // The website does not flag the content. override val filterNonMangaItems = false + + // /manga/page/1/ redirects to /manga/ + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" + + // + // JS Challenge logic start + // + @Suppress("FunctionName") + private fun JSChallengeInterceptor(chain: Interceptor.Chain): Response { + val request = chain.request() + val response = chain.proceed(request) + + if (response.code != 202) return response + val url = request.url + + Cookie.parse(url, getJSChallengeCookie(response)) + ?.let { client.cookieJar.saveFromResponse(url, listOf(it)) } + ?: throw IOException("Failed JavaScript challenge. Check WebView.") + + return client.newCall(request).execute() + } + + private fun getJSChallengeCookie(response: Response): String { + val document = response.asJsoup() + + val jsPatch = ";function atob(a){return base64.atob(a)};document.cookie" + val jsPayload = document.select("script") + .joinToString("\n") { it.data() } + .trimIndent() + jsPatch + + val fauxBase64 = FauxBase64() + val fauxLocation = FauxLocation() + val fauxDocument = FauxDocument() + val slowAES = FauxSlowAES() + + QuickJs.create().use { context -> + context.set("base64", FauxBase64Interface::class.java, fauxBase64) + context.set("location", FauxLocationInterface::class.java, fauxLocation) + context.set("document", FauxDocumentInterface::class.java, fauxDocument) + context.set("slowAES", FauxSlowAESInterface::class.java, slowAES) + + return context.evaluate(jsPayload) as String + } + } + + class FauxBase64 : FauxBase64Interface { + override fun atob(base64: String): String { + return String(Base64.decode(base64, Base64.DEFAULT)) + } + } + + class FauxLocation : FauxLocationInterface { + override var href: String = "" + } + + class FauxDocument : FauxDocumentInterface { + override var cookie: String = "" + } + + class FauxSlowAES : FauxSlowAESInterface { + private fun Array.toByteArray(): ByteArray { + return map { it.toByte() }.toByteArray() + } + + private fun ByteArray.toTypedArray(): Array { + return map { it.toInt() and 0xFF }.toTypedArray() + } + + override fun decrypt(cipherIn: Array, mode: Int, key: Array, iv: Array): Array { + val modeStr = when (mode) { + 0 -> "OFB" + 1 -> "CFB" + else -> "CBC" // 2 = CBC, 3+ = unknown + } + + val cipher = Cipher.getInstance("AES/$modeStr/NoPadding") + val keyS = SecretKeySpec(key.toByteArray(), "AES") + cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(iv.toByteArray())) + + return cipher.doFinal(cipherIn.toByteArray()).toTypedArray() + } + } + + interface FauxBase64Interface { + fun atob(base64: String): String + } + + interface FauxLocationInterface { + val href: String + } + + private interface FauxDocumentInterface { + val cookie: String + } + + @Suppress("unused") + private interface FauxSlowAESInterface { + fun decrypt(cipherIn: Array, mode: Int, key: Array, iv: Array): Array + } + + // + // JS Challenge logic end + // } diff --git a/multisrc/overrides/madara/mangahentai/src/MangaHentai.kt b/multisrc/overrides/madara/mangahentai/src/MangaHentai.kt index 808ed82985..3d5de98bd5 100644 --- a/multisrc/overrides/madara/mangahentai/src/MangaHentai.kt +++ b/multisrc/overrides/madara/mangahentai/src/MangaHentai.kt @@ -3,5 +3,9 @@ package eu.kanade.tachiyomi.extension.en.mangahentai import eu.kanade.tachiyomi.multisrc.madara.Madara class MangaHentai : Madara("Manga Hentai", "https://mangahentai.me", "en") { + override val mangaSubString = "manga-hentai" + override val useNewChapterEndpoint: Boolean = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" } diff --git a/multisrc/overrides/madara/mangahubfr/src/MangaHubFr.kt b/multisrc/overrides/madara/mangahubfr/src/MangaHubFr.kt index 4cb3a70404..0c0d34632b 100644 --- a/multisrc/overrides/madara/mangahubfr/src/MangaHubFr.kt +++ b/multisrc/overrides/madara/mangahubfr/src/MangaHubFr.kt @@ -4,6 +4,4 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import java.text.SimpleDateFormat import java.util.Locale -class MangaHubFr : Madara("MangaHub.fr", "https://mangahub.fr", "fr", dateFormat = SimpleDateFormat("d MMMM yyyy", Locale.FRENCH)) { - override fun chapterListSelector() = "li.wp-mangas-chapters" -} +class MangaHubFr : Madara("MangaHub.fr", "https://mangahub.fr", "fr", dateFormat = SimpleDateFormat("d MMMM yyyy", Locale.FRENCH)) diff --git a/multisrc/overrides/madara/mangakakalotio/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangakakalotio/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..397ccc6d19 Binary files /dev/null and b/multisrc/overrides/madara/mangakakalotio/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangakakalotio/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangakakalotio/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..67bbbe769a Binary files /dev/null and b/multisrc/overrides/madara/mangakakalotio/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangakakalotio/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangakakalotio/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b51b2f7b04 Binary files /dev/null and b/multisrc/overrides/madara/mangakakalotio/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangakakalotio/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangakakalotio/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4ae5101959 Binary files /dev/null and b/multisrc/overrides/madara/mangakakalotio/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangakakalotio/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangakakalotio/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4449730233 Binary files /dev/null and b/multisrc/overrides/madara/mangakakalotio/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangakakalotio/res/web_hi_res_512.png b/multisrc/overrides/madara/mangakakalotio/res/web_hi_res_512.png new file mode 100644 index 0000000000..202ad7c00b Binary files /dev/null and b/multisrc/overrides/madara/mangakakalotio/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangakakalotio/src/MangakakalotIo.kt b/multisrc/overrides/madara/mangakakalotio/src/MangakakalotIo.kt new file mode 100644 index 0000000000..be17512b48 --- /dev/null +++ b/multisrc/overrides/madara/mangakakalotio/src/MangakakalotIo.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangakakalotio + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangakakalotIo : Madara("Mangakakalot.io (unoriginal)", "https://mangakakalot.io", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangakakalotone/src/MangakakalotOne.kt b/multisrc/overrides/madara/mangakakalotone/src/MangakakalotOne.kt new file mode 100644 index 0000000000..b82935347e --- /dev/null +++ b/multisrc/overrides/madara/mangakakalotone/src/MangakakalotOne.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangakakalotone + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangakakalotOne : Madara("Mangakakalot.one (unoriginal)", "https://mangakakalot.one", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangakio/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangakio/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index bc75d71472..0000000000 Binary files a/multisrc/overrides/madara/mangakio/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangakio/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangakio/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index ba32c2e054..0000000000 Binary files a/multisrc/overrides/madara/mangakio/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangakio/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangakio/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 418f7e546d..0000000000 Binary files a/multisrc/overrides/madara/mangakio/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangakio/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangakio/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 87eebefaa7..0000000000 Binary files a/multisrc/overrides/madara/mangakio/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangakio/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangakio/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 2eecfc87d6..0000000000 Binary files a/multisrc/overrides/madara/mangakio/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangakio/res/web_hi_res_512.png b/multisrc/overrides/madara/mangakio/res/web_hi_res_512.png deleted file mode 100644 index d7be8a5dcd..0000000000 Binary files a/multisrc/overrides/madara/mangakio/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangakio/src/MangaKio.kt b/multisrc/overrides/madara/mangakio/src/MangaKio.kt deleted file mode 100644 index 967e096554..0000000000 --- a/multisrc/overrides/madara/mangakio/src/MangaKio.kt +++ /dev/null @@ -1,14 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.mangakio - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class MangaKio : Madara( - "Manga Kio", - "https://mangakio.com", - "en", - dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US), -) { - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/mangakitsu/src/MangaKitsu.kt b/multisrc/overrides/madara/mangakitsu/src/MangaKitsu.kt new file mode 100644 index 0000000000..db609628e7 --- /dev/null +++ b/multisrc/overrides/madara/mangakitsu/src/MangaKitsu.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.mangakitsu + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaKitsu : Madara("Manga Kitsu", "https://mangakitsu.com", "en") { + override val useNewChapterEndpoint = false + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaleks/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaleks/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..348915430d Binary files /dev/null and b/multisrc/overrides/madara/mangaleks/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaleks/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaleks/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..345012bc3d Binary files /dev/null and b/multisrc/overrides/madara/mangaleks/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaleks/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaleks/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2e7af18d5f Binary files /dev/null and b/multisrc/overrides/madara/mangaleks/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaleks/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaleks/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7ae8ef32a7 Binary files /dev/null and b/multisrc/overrides/madara/mangaleks/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaleks/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaleks/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8843070919 Binary files /dev/null and b/multisrc/overrides/madara/mangaleks/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaleks/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaleks/res/web_hi_res_512.png new file mode 100644 index 0000000000..242a833dac Binary files /dev/null and b/multisrc/overrides/madara/mangaleks/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaleks/src/MangaLeks.kt b/multisrc/overrides/madara/mangaleks/src/MangaLeks.kt new file mode 100644 index 0000000000..c17a92efb1 --- /dev/null +++ b/multisrc/overrides/madara/mangaleks/src/MangaLeks.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.ar.mangaleks + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaLeks : Madara( + "مانجا ليكس", + "https://mangaleks.com", + "ar", + SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH), +) { + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/overrides/madara/mangamammy/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangamammy/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..652f6cc073 Binary files /dev/null and b/multisrc/overrides/madara/mangamammy/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangamammy/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangamammy/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..f810ec5566 Binary files /dev/null and b/multisrc/overrides/madara/mangamammy/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangamammy/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangamammy/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..c074271fb0 Binary files /dev/null and b/multisrc/overrides/madara/mangamammy/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangamammy/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangamammy/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d5d3a60485 Binary files /dev/null and b/multisrc/overrides/madara/mangamammy/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangamammy/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangamammy/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..219a3c8960 Binary files /dev/null and b/multisrc/overrides/madara/mangamammy/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangamammy/res/web_hi_res_512.png b/multisrc/overrides/madara/mangamammy/res/web_hi_res_512.png new file mode 100644 index 0000000000..afa9890917 Binary files /dev/null and b/multisrc/overrides/madara/mangamammy/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangamammy/src/MangaMammy.kt b/multisrc/overrides/madara/mangamammy/src/MangaMammy.kt new file mode 100644 index 0000000000..a477f0846b --- /dev/null +++ b/multisrc/overrides/madara/mangamammy/src/MangaMammy.kt @@ -0,0 +1,56 @@ +package eu.kanade.tachiyomi.extension.ru.mangamammy + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.FilterList +import okhttp3.Request +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaMammy : Madara( + "Manga Mammy", + "https://mangamammy.ru", + "ru", + dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.ROOT), +) { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" + + override fun popularMangaSelector() = searchMangaSelector() + + override fun popularMangaRequest(page: Int): Request = + searchMangaRequest( + page, + "", + FilterList( + listOf( + OrderByFilter( + "", + listOf( + Pair("", ""), + Pair("", "views"), + ), + 1, + ), + ), + ), + ) + + override fun latestUpdatesRequest(page: Int): Request = + searchMangaRequest( + page, + "", + FilterList( + listOf( + OrderByFilter( + "", + listOf( + Pair("", ""), + Pair("", "latest"), + ), + 1, + ), + ), + ), + ) +} diff --git a/multisrc/overrides/madara/mangame/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangame/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 6b9ced9363..0000000000 Binary files a/multisrc/overrides/madara/mangame/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangame/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangame/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 45e9b09c60..0000000000 Binary files a/multisrc/overrides/madara/mangame/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangame/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangame/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1e2c0bb4ae..0000000000 Binary files a/multisrc/overrides/madara/mangame/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangame/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangame/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 05deb3fe4d..0000000000 Binary files a/multisrc/overrides/madara/mangame/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangame/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangame/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index c0c9c5b7db..0000000000 Binary files a/multisrc/overrides/madara/mangame/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangame/res/web_hi_res_512.png b/multisrc/overrides/madara/mangame/res/web_hi_res_512.png deleted file mode 100644 index ce80be44e4..0000000000 Binary files a/multisrc/overrides/madara/mangame/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangame/src/MangaMe.kt b/multisrc/overrides/madara/mangame/src/MangaMe.kt deleted file mode 100644 index 12eae13578..0000000000 --- a/multisrc/overrides/madara/mangame/src/MangaMe.kt +++ /dev/null @@ -1,12 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.mangame - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class MangaMe : Madara( - "MangaMe", - "https://mangame.org", - "en", - dateFormat = SimpleDateFormat("dd.MM.yyyy", Locale.US), -) diff --git a/multisrc/overrides/madara/manganelobiz/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manganelobiz/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d8bbacd490 Binary files /dev/null and b/multisrc/overrides/madara/manganelobiz/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelobiz/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manganelobiz/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ec35ead1d0 Binary files /dev/null and b/multisrc/overrides/madara/manganelobiz/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelobiz/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manganelobiz/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..bd959ac04d Binary files /dev/null and b/multisrc/overrides/madara/manganelobiz/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelobiz/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manganelobiz/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..58989ca02b Binary files /dev/null and b/multisrc/overrides/madara/manganelobiz/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelobiz/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manganelobiz/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3ae51ec0a2 Binary files /dev/null and b/multisrc/overrides/madara/manganelobiz/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelobiz/res/web_hi_res_512.png b/multisrc/overrides/madara/manganelobiz/res/web_hi_res_512.png new file mode 100644 index 0000000000..a624bd17dd Binary files /dev/null and b/multisrc/overrides/madara/manganelobiz/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manganelobiz/src/ManganeloBiz.kt b/multisrc/overrides/madara/manganelobiz/src/ManganeloBiz.kt new file mode 100644 index 0000000000..81ea5245f6 --- /dev/null +++ b/multisrc/overrides/madara/manganelobiz/src/ManganeloBiz.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manganelobiz + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManganeloBiz : Madara("Manganelo.biz", "https://manganelo.biz", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manganelowebsite/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manganelowebsite/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..03b4a84c38 Binary files /dev/null and b/multisrc/overrides/madara/manganelowebsite/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelowebsite/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manganelowebsite/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..2706869bb9 Binary files /dev/null and b/multisrc/overrides/madara/manganelowebsite/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelowebsite/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manganelowebsite/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..982cec3a3d Binary files /dev/null and b/multisrc/overrides/madara/manganelowebsite/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelowebsite/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manganelowebsite/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a20e946d24 Binary files /dev/null and b/multisrc/overrides/madara/manganelowebsite/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelowebsite/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manganelowebsite/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..839c009e9c Binary files /dev/null and b/multisrc/overrides/madara/manganelowebsite/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganelowebsite/res/web_hi_res_512.png b/multisrc/overrides/madara/manganelowebsite/res/web_hi_res_512.png new file mode 100644 index 0000000000..c6bad55edb Binary files /dev/null and b/multisrc/overrides/madara/manganelowebsite/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manganelowebsite/src/ManganeloWebsite.kt b/multisrc/overrides/madara/manganelowebsite/src/ManganeloWebsite.kt new file mode 100644 index 0000000000..b4c39d9103 --- /dev/null +++ b/multisrc/overrides/madara/manganelowebsite/src/ManganeloWebsite.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manganelowebsite + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManganeloWebsite : Madara("Manganelo.website (unoriginal)", "https://manganelo.website", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manganerds/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manganerds/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..fb9f9b87b1 Binary files /dev/null and b/multisrc/overrides/madara/manganerds/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganerds/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manganerds/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..8b4ab3601d Binary files /dev/null and b/multisrc/overrides/madara/manganerds/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganerds/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manganerds/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..8a5ae6a74a Binary files /dev/null and b/multisrc/overrides/madara/manganerds/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganerds/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manganerds/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..c98a79948d Binary files /dev/null and b/multisrc/overrides/madara/manganerds/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganerds/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manganerds/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0214a98821 Binary files /dev/null and b/multisrc/overrides/madara/manganerds/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manganerds/res/web_hi_res_512.png b/multisrc/overrides/madara/manganerds/res/web_hi_res_512.png new file mode 100644 index 0000000000..723ae884e0 Binary files /dev/null and b/multisrc/overrides/madara/manganerds/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manganerds/src/MangaNerds.kt b/multisrc/overrides/madara/manganerds/src/MangaNerds.kt new file mode 100644 index 0000000000..eb162e60bb --- /dev/null +++ b/multisrc/overrides/madara/manganerds/src/MangaNerds.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manganerds + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaNerds : Madara("Manga Nerds", "https://manganerds.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..e07fbaf126 Binary files /dev/null and b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..6a0426ef1a Binary files /dev/null and b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..922aef74f2 Binary files /dev/null and b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..5782860ca6 Binary files /dev/null and b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1fab747f49 Binary files /dev/null and b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/web_hi_res_512.png new file mode 100644 index 0000000000..24c0b68c3b Binary files /dev/null and b/multisrc/overrides/madara/mangaonlineteamunoriginal/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaonlineteamunoriginal/src/MangaOnlineTeamUnoriginal.kt b/multisrc/overrides/madara/mangaonlineteamunoriginal/src/MangaOnlineTeamUnoriginal.kt new file mode 100644 index 0000000000..44491dd7bf --- /dev/null +++ b/multisrc/overrides/madara/mangaonlineteamunoriginal/src/MangaOnlineTeamUnoriginal.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaonlineteamunoriginal + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaOnlineTeamUnoriginal : Madara("MangaOnline.team (unoriginal)", "https://mangaonline.team", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaowlblog/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlblog/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..4862cd83ed Binary files /dev/null and b/multisrc/overrides/madara/mangaowlblog/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlblog/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlblog/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..a8635c99d0 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlblog/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlblog/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlblog/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..f74bb6b32a Binary files /dev/null and b/multisrc/overrides/madara/mangaowlblog/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlblog/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlblog/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4f46369e89 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlblog/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlblog/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlblog/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..598971deef Binary files /dev/null and b/multisrc/overrides/madara/mangaowlblog/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlblog/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaowlblog/res/web_hi_res_512.png new file mode 100644 index 0000000000..29e87c79b7 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlblog/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaowlblog/src/MangaOwlBlog.kt b/multisrc/overrides/madara/mangaowlblog/src/MangaOwlBlog.kt new file mode 100644 index 0000000000..e51f921831 --- /dev/null +++ b/multisrc/overrides/madara/mangaowlblog/src/MangaOwlBlog.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaowlblog + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaOwlBlog : Madara("MangaOwl.blog (unoriginal)", "https://mangaowl.blog", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaowlio/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlio/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..49bf4a917f Binary files /dev/null and b/multisrc/overrides/madara/mangaowlio/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlio/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlio/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..7951283145 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlio/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlio/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlio/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..4f51b0e2bf Binary files /dev/null and b/multisrc/overrides/madara/mangaowlio/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlio/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlio/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..dcf9aa94fc Binary files /dev/null and b/multisrc/overrides/madara/mangaowlio/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlio/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlio/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..25b5c27792 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlio/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlio/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaowlio/res/web_hi_res_512.png new file mode 100644 index 0000000000..1b7dc6b6b7 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlio/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaowlio/src/MangaOwlIo.kt b/multisrc/overrides/madara/mangaowlio/src/MangaOwlIo.kt new file mode 100644 index 0000000000..8f41cc9f7a --- /dev/null +++ b/multisrc/overrides/madara/mangaowlio/src/MangaOwlIo.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaowlio + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaOwlIo : Madara("MangaOwl.io (unoriginal)", "https://mangaowl.io", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaowlone/src/MangaOwlOne.kt b/multisrc/overrides/madara/mangaowlone/src/MangaOwlOne.kt new file mode 100644 index 0000000000..36d7ad2f64 --- /dev/null +++ b/multisrc/overrides/madara/mangaowlone/src/MangaOwlOne.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.mangaowlone + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaOwlOne : Madara("MangaOwl.one (unoriginal)", "https://mangaowl.one", "en") { + override val useNewChapterEndpoint = false + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaowlus/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlus/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..639597b4cb Binary files /dev/null and b/multisrc/overrides/madara/mangaowlus/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlus/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlus/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..894494f762 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlus/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlus/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlus/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..d61ff9e20a Binary files /dev/null and b/multisrc/overrides/madara/mangaowlus/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlus/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlus/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2f68aecf16 Binary files /dev/null and b/multisrc/overrides/madara/mangaowlus/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlus/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaowlus/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d5f1e7bf4b Binary files /dev/null and b/multisrc/overrides/madara/mangaowlus/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaowlus/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaowlus/res/web_hi_res_512.png new file mode 100644 index 0000000000..7f5c02843b Binary files /dev/null and b/multisrc/overrides/madara/mangaowlus/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaowlus/src/MangaOwlUs.kt b/multisrc/overrides/madara/mangaowlus/src/MangaOwlUs.kt new file mode 100644 index 0000000000..4b5dba41b5 --- /dev/null +++ b/multisrc/overrides/madara/mangaowlus/src/MangaOwlUs.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaowlus + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaOwlUs : Madara("MangaOwl.us (unoriginal)", "https://mangaowl.us", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangapure/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangapure/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..6bd16fed43 Binary files /dev/null and b/multisrc/overrides/madara/mangapure/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangapure/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangapure/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..d0c5bd9549 Binary files /dev/null and b/multisrc/overrides/madara/mangapure/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangapure/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangapure/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..c70b55da99 Binary files /dev/null and b/multisrc/overrides/madara/mangapure/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangapure/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangapure/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1c74979fb3 Binary files /dev/null and b/multisrc/overrides/madara/mangapure/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangapure/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangapure/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d475ff12e4 Binary files /dev/null and b/multisrc/overrides/madara/mangapure/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangapure/res/web_hi_res_512.png b/multisrc/overrides/madara/mangapure/res/web_hi_res_512.png new file mode 100644 index 0000000000..f8a8d7c118 Binary files /dev/null and b/multisrc/overrides/madara/mangapure/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangapure/src/MangaPure.kt b/multisrc/overrides/madara/mangapure/src/MangaPure.kt new file mode 100644 index 0000000000..a918c44ea3 --- /dev/null +++ b/multisrc/overrides/madara/mangapure/src/MangaPure.kt @@ -0,0 +1,82 @@ +package eu.kanade.tachiyomi.extension.en.mangapure + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaPure : Madara( + "MangaPure", + "https://mangapure.net", + "en", + dateFormat = SimpleDateFormat("MMM dd, HH:mm", Locale.ENGLISH), +) { + override val useNewChapterEndpoint = false + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + + override fun searchMangaNextPageSelector(): String? = ".pagination a[rel=next]" + + override fun searchPage(page: Int): String = "search?page=$page" + + override fun popularMangaRequest(page: Int): Request = + GET("$baseUrl/popular-manga?page=$page", headers) + + override fun latestUpdatesRequest(page: Int): Request = + GET("$baseUrl/latest-manga?page=$page", headers) + + // Copied from IsekaiScan.top (unoriginal) + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + val chaptersWrapper = document.select("div[id^=manga-chapters-holder]") + + var chapterElements = document.select(chapterListSelector()) + + if (chapterElements.isEmpty() && !chaptersWrapper.isNullOrEmpty()) { + val mangaId = chaptersWrapper.attr("data-id") + val xhrHeaders = headersBuilder() + .add("Referer", "$baseUrl/") + .add("X-Requested-With", "XMLHttpRequest") + .build() + val xhrRequest = GET("$baseUrl/ajax-list-chapter?mangaID=$mangaId", xhrHeaders) + val xhrResponse = client.newCall(xhrRequest).execute() + + chapterElements = xhrResponse.asJsoup().select(chapterListSelector()) + xhrResponse.close() + } + + countViews(document) + return chapterElements.map(::chapterFromElement) + } + + // Copied from IsekaiScan.top (unoriginal) + override fun pageListParse(document: Document): List { + val stringArray = document.select("p#arraydata").text().split(",").toTypedArray() + return stringArray.mapIndexed { index, url -> + Page( + index, + document.location(), + url, + ) + } + } + + // Some thumbnails expect harimanga.com, which has hotlink protection + override fun headersBuilder() = super.headersBuilder() + .removeAll("Referer") + + // OnGoing => Ongoing + override fun mangaDetailsParse(document: Document): SManga { + return super.mangaDetailsParse(document).apply { + document.select(mangaDetailsSelectorStatus).lastOrNull()?.text() + .takeIf { it == "Ongoing" } + ?.let { status = SManga.ONGOING } + } + } +} diff --git a/multisrc/overrides/madara/mangaqueencom/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueencom/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..1193cd3748 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueencom/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueencom/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueencom/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..1aa7601976 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueencom/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueencom/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueencom/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..416fb40ded Binary files /dev/null and b/multisrc/overrides/madara/mangaqueencom/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueencom/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueencom/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..10a517b348 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueencom/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueencom/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueencom/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..f35d211201 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueencom/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueencom/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaqueencom/res/web_hi_res_512.png new file mode 100644 index 0000000000..b888d7534c Binary files /dev/null and b/multisrc/overrides/madara/mangaqueencom/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaqueencom/src/MangaQueenCom.kt b/multisrc/overrides/madara/mangaqueencom/src/MangaQueenCom.kt new file mode 100644 index 0000000000..ac7398fb7a --- /dev/null +++ b/multisrc/overrides/madara/mangaqueencom/src/MangaQueenCom.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaqueencom + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaQueenCom : Madara("Manga Queen.com", "https://mangaqueen.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaqueenonline/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..25a5b1a38b Binary files /dev/null and b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueenonline/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..dbe85bc46f Binary files /dev/null and b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..164b647f92 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..c8530f2148 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..00c6e317e7 Binary files /dev/null and b/multisrc/overrides/madara/mangaqueenonline/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaqueenonline/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaqueenonline/res/web_hi_res_512.png new file mode 100644 index 0000000000..ee69bd153a Binary files /dev/null and b/multisrc/overrides/madara/mangaqueenonline/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaqueenonline/src/MangaQueenOnline.kt b/multisrc/overrides/madara/mangaqueenonline/src/MangaQueenOnline.kt new file mode 100644 index 0000000000..4539fcf35a --- /dev/null +++ b/multisrc/overrides/madara/mangaqueenonline/src/MangaQueenOnline.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaqueenonline + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaQueenOnline : Madara("Manga Queen.online (unoriginal)", "https://mangaqueen.online", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangarawinfo/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangarawinfo/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..0d137e4505 Binary files /dev/null and b/multisrc/overrides/madara/mangarawinfo/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarawinfo/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangarawinfo/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..8f581c07bb Binary files /dev/null and b/multisrc/overrides/madara/mangarawinfo/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarawinfo/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarawinfo/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b9850c6610 Binary files /dev/null and b/multisrc/overrides/madara/mangarawinfo/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarawinfo/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarawinfo/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0caeae1f5f Binary files /dev/null and b/multisrc/overrides/madara/mangarawinfo/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarawinfo/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarawinfo/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..351aeba936 Binary files /dev/null and b/multisrc/overrides/madara/mangarawinfo/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarawinfo/res/web_hi_res_512.png b/multisrc/overrides/madara/mangarawinfo/res/web_hi_res_512.png new file mode 100644 index 0000000000..a7b94c3771 Binary files /dev/null and b/multisrc/overrides/madara/mangarawinfo/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangarawinfo/src/MangaRawInfo.kt b/multisrc/overrides/madara/mangarawinfo/src/MangaRawInfo.kt new file mode 100644 index 0000000000..aadc1a1bef --- /dev/null +++ b/multisrc/overrides/madara/mangarawinfo/src/MangaRawInfo.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangarawinfo + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaRawInfo : Madara("Manga-Raw.info (unoriginal)", "https://manga-raw.info", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..c4f9323374 Binary files /dev/null and b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..9368d956eb Binary files /dev/null and b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..fc4dee1169 Binary files /dev/null and b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2ace1cc1bb Binary files /dev/null and b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4826ab2e2c Binary files /dev/null and b/multisrc/overrides/madara/mangarockteamunoriginal/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/res/web_hi_res_512.png b/multisrc/overrides/madara/mangarockteamunoriginal/res/web_hi_res_512.png new file mode 100644 index 0000000000..86f271f4da Binary files /dev/null and b/multisrc/overrides/madara/mangarockteamunoriginal/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangarockteamunoriginal/src/MangaRockTeamUnoriginal.kt b/multisrc/overrides/madara/mangarockteamunoriginal/src/MangaRockTeamUnoriginal.kt new file mode 100644 index 0000000000..fe7d0fb266 --- /dev/null +++ b/multisrc/overrides/madara/mangarockteamunoriginal/src/MangaRockTeamUnoriginal.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangarockteamunoriginal + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaRockTeamUnoriginal : Madara("Manga Rock.team (unoriginal)", "https://mangarock.team", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangarose/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangarose/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..445a0c9299 Binary files /dev/null and b/multisrc/overrides/madara/mangarose/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarose/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangarose/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..dae395afbf Binary files /dev/null and b/multisrc/overrides/madara/mangarose/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarose/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarose/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..75d7f70dc0 Binary files /dev/null and b/multisrc/overrides/madara/mangarose/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarose/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarose/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b81f439a7a Binary files /dev/null and b/multisrc/overrides/madara/mangarose/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarose/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarose/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4918ad3213 Binary files /dev/null and b/multisrc/overrides/madara/mangarose/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarose/res/web_hi_res_512.png b/multisrc/overrides/madara/mangarose/res/web_hi_res_512.png new file mode 100644 index 0000000000..4aba204501 Binary files /dev/null and b/multisrc/overrides/madara/mangarose/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangarose/src/MangaRose.kt b/multisrc/overrides/madara/mangarose/src/MangaRose.kt new file mode 100644 index 0000000000..f6a9884707 --- /dev/null +++ b/multisrc/overrides/madara/mangarose/src/MangaRose.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.ar.mangarose + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaRose : Madara( + "Manga Rose", + "https://mangarose.net", + "ar", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), +) { + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/overrides/madara/mangarosie/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangarosie/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..df35ab65d1 Binary files /dev/null and b/multisrc/overrides/madara/mangarosie/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarosie/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangarosie/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..14aa21896e Binary files /dev/null and b/multisrc/overrides/madara/mangarosie/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarosie/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarosie/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b343dd43dd Binary files /dev/null and b/multisrc/overrides/madara/mangarosie/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarosie/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarosie/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..bb0770db70 Binary files /dev/null and b/multisrc/overrides/madara/mangarosie/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarosie/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangarosie/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1703be00ea Binary files /dev/null and b/multisrc/overrides/madara/mangarosie/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangarosie/res/web_hi_res_512.png b/multisrc/overrides/madara/mangarosie/res/web_hi_res_512.png new file mode 100644 index 0000000000..bad73d3e3c Binary files /dev/null and b/multisrc/overrides/madara/mangarosie/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangarosie/src/MangaRosie.kt b/multisrc/overrides/madara/mangarosie/src/MangaRosie.kt new file mode 100644 index 0000000000..08b563be91 --- /dev/null +++ b/multisrc/overrides/madara/mangarosie/src/MangaRosie.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.mangarosie + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaRosie : Madara("MangaRosie", "https://mangarosie.in", "en") { + override val useNewChapterEndpoint = false + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangarubycom/src/MangaRubyCom.kt b/multisrc/overrides/madara/mangarubycom/src/MangaRubyCom.kt new file mode 100644 index 0000000000..c75ef93363 --- /dev/null +++ b/multisrc/overrides/madara/mangarubycom/src/MangaRubyCom.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.mangarubycom + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaRubyCom : Madara("MangaRuby.com", "https://mangaruby.com", "en") { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaryu/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaryu/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..66b742d5aa Binary files /dev/null and b/multisrc/overrides/madara/mangaryu/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaryu/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaryu/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..6c0b1d49cc Binary files /dev/null and b/multisrc/overrides/madara/mangaryu/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaryu/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaryu/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..57c8aa6a1a Binary files /dev/null and b/multisrc/overrides/madara/mangaryu/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaryu/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaryu/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..e84d14bea2 Binary files /dev/null and b/multisrc/overrides/madara/mangaryu/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaryu/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaryu/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b154a8a74b Binary files /dev/null and b/multisrc/overrides/madara/mangaryu/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaryu/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaryu/res/web_hi_res_512.png new file mode 100644 index 0000000000..552c952df3 Binary files /dev/null and b/multisrc/overrides/madara/mangaryu/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaryu/src/Mangaryu.kt b/multisrc/overrides/madara/mangaryu/src/Mangaryu.kt new file mode 100644 index 0000000000..616998644c --- /dev/null +++ b/multisrc/overrides/madara/mangaryu/src/Mangaryu.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaryu + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Mangaryu : Madara("Mangaryu", "https://mangaryu.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..19e312104e Binary files /dev/null and b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ab64511ba5 Binary files /dev/null and b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..701f0bf67c Binary files /dev/null and b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b0e8a2baf9 Binary files /dev/null and b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..03f4db8613 Binary files /dev/null and b/multisrc/overrides/madara/mangasoriginesfr/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangasoriginesfr/res/web_hi_res_512.png b/multisrc/overrides/madara/mangasoriginesfr/res/web_hi_res_512.png new file mode 100644 index 0000000000..f465790acb Binary files /dev/null and b/multisrc/overrides/madara/mangasoriginesfr/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangasoriginesfr/src/MangasOriginesFr.kt b/multisrc/overrides/madara/mangasoriginesfr/src/MangasOriginesFr.kt new file mode 100644 index 0000000000..2af4ddc5dd --- /dev/null +++ b/multisrc/overrides/madara/mangasoriginesfr/src/MangasOriginesFr.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.extension.fr.mangasoriginesfr + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class MangasOriginesFr : Madara("Mangas-Origines.fr", "https://mangas-origines.fr", "fr", SimpleDateFormat("dd/mm/yyyy", Locale("fr"))) { + override val mangaSubString = "catalogues" + + // Manga Details Selectors + override val mangaDetailsSelectorAuthor = "div.manga-authors > a" + override val mangaDetailsSelectorDescription = "div.summary__content > p" +} diff --git a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesx/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 081e9f6bf3..0000000000 Binary files a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesx/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 2cabf7f70d..0000000000 Binary files a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 58b14a5f53..0000000000 Binary files a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index a1e16eafae..0000000000 Binary files a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index ba31465175..0000000000 Binary files a/multisrc/overrides/madara/mangasoriginesx/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangasoriginesx/res/web_hi_res_512.png b/multisrc/overrides/madara/mangasoriginesx/res/web_hi_res_512.png deleted file mode 100644 index 87dbfb91ce..0000000000 Binary files a/multisrc/overrides/madara/mangasoriginesx/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangasoriginesx/src/MangasOriginesX.kt b/multisrc/overrides/madara/mangasoriginesx/src/MangasOriginesX.kt deleted file mode 100644 index 56e3bcce24..0000000000 --- a/multisrc/overrides/madara/mangasoriginesx/src/MangasOriginesX.kt +++ /dev/null @@ -1,7 +0,0 @@ -package eu.kanade.tachiyomi.extension.fr.mangasoriginesx - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class MangasOriginesX : Madara("Mangas Origines X", "https://x.mangas-origines.fr", "fr", SimpleDateFormat("dd/MM/yyyy", Locale("fr"))) diff --git a/multisrc/overrides/madara/mangaspark/src/MangaSpark.kt b/multisrc/overrides/madara/mangaspark/src/MangaSpark.kt index d82e09e9d8..74928ff325 100644 --- a/multisrc/overrides/madara/mangaspark/src/MangaSpark.kt +++ b/multisrc/overrides/madara/mangaspark/src/MangaSpark.kt @@ -1,26 +1,14 @@ package eu.kanade.tachiyomi.extension.ar.mangaspark import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.source.model.SManga -import org.jsoup.nodes.Element - -class MangaSpark : Madara("MangaSpark", "https://mangaspark.com", "ar") { - override fun popularMangaFromElement(element: Element): SManga { - val manga = SManga.create() - - with(element) { - select(popularMangaUrlSelector).first()?.let { - manga.setUrlWithoutDomain(it.attr("abs:href")) - manga.title = it.ownText() - } - - select("img").first()?.let { - manga.thumbnail_url = imageFromElement(it)?.replace("mangaspark", "mangalek") - } - } - - return manga - } - +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaSpark : Madara( + "MangaSpark", + "https://mangaspark.com", + "ar", + dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.ROOT), +) { override val chapterUrlSuffix = "" } diff --git a/multisrc/overrides/madara/mangastarz/src/MangaStarz.kt b/multisrc/overrides/madara/mangastarz/src/MangaStarz.kt index 330b4c595e..9e5a994df0 100644 --- a/multisrc/overrides/madara/mangastarz/src/MangaStarz.kt +++ b/multisrc/overrides/madara/mangastarz/src/MangaStarz.kt @@ -2,13 +2,9 @@ package eu.kanade.tachiyomi.extension.ar.mangastarz import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.source.model.SManga -import okhttp3.Headers import org.jsoup.nodes.Element -class MangaStarz : Madara("Manga Starz", "https://mangastarz.com", "ar") { - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("Referer", baseUrl) +class MangaStarz : Madara("Manga Starz", "https://mangalike.org", "ar") { override fun popularMangaFromElement(element: Element): SManga { val manga = SManga.create() diff --git a/multisrc/overrides/madara/mangastk18/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangastk18/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index af02e5668c..0000000000 Binary files a/multisrc/overrides/madara/mangastk18/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangastk18/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangastk18/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 3145bc9a93..0000000000 Binary files a/multisrc/overrides/madara/mangastk18/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangastk18/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangastk18/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 81f7927557..0000000000 Binary files a/multisrc/overrides/madara/mangastk18/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangastk18/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangastk18/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 7304c324d5..0000000000 Binary files a/multisrc/overrides/madara/mangastk18/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangastk18/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangastk18/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index e818270b6a..0000000000 Binary files a/multisrc/overrides/madara/mangastk18/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangastk18/res/web_hi_res_512.png b/multisrc/overrides/madara/mangastk18/res/web_hi_res_512.png deleted file mode 100644 index 2393ecee06..0000000000 Binary files a/multisrc/overrides/madara/mangastk18/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mangastk18/src/MangasTK18.kt b/multisrc/overrides/madara/mangastk18/src/MangasTK18.kt deleted file mode 100644 index 66f0f95437..0000000000 --- a/multisrc/overrides/madara/mangastk18/src/MangasTK18.kt +++ /dev/null @@ -1,54 +0,0 @@ -package eu.kanade.tachiyomi.extension.es.mangastk18 - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.Locale - -class MangasTK18 : Madara( - "MangasTK18", - "https://mangastk18.com", - "es", - SimpleDateFormat("dd.MM.yyyy", Locale("es")), -) { - override fun popularMangaSelector() = "div#series-card:has(a:not([href*='bilibilicomics.com']))" - override val popularMangaUrlSelector = "a.series-link" - - override val mangaDetailsSelectorTag = "div.tags-content a.notUsed" // Source use this for the scanlator - override val mangaDetailsSelectorStatus = "div.post-status div.summary-content" - - override fun popularMangaFromElement(element: Element): SManga { - val manga = SManga.create() - - with(element) { - select(popularMangaUrlSelector).first()?.let { - manga.setUrlWithoutDomain(it.attr("abs:href")) - manga.title = it.attr("title") - } - - select("img").first()?.let { - manga.thumbnail_url = imageFromElement(it) - } - } - - return manga - } - - override fun chapterFromElement(element: Element): SChapter { - val chapter = SChapter.create() - - with(element) { - select(chapterUrlSelector).first()?.let { urlElement -> - chapter.url = urlElement.attr("abs:href").let { - it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "" - } - chapter.name = urlElement.select("p.chapter-manhwa-title").text() - chapter.date_upload = parseChapterDate(select("span.chapter-release-date").text()) - } - } - - return chapter - } -} diff --git a/multisrc/overrides/madara/mangatopsite/src/MangaTopSite.kt b/multisrc/overrides/madara/mangatopsite/src/MangaTopSite.kt new file mode 100644 index 0000000000..5a82c94afd --- /dev/null +++ b/multisrc/overrides/madara/mangatopsite/src/MangaTopSite.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.all.mangatopsite + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaTopSite : Madara( + "MangaTop.site", + "https://mangatop.site", + "all", + dateFormat = SimpleDateFormat("d MMM yyyy", Locale.ENGLISH), +) { + override val useNewChapterEndpoint = false + override val chapterUrlSuffix = "" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangatxgg/src/MangaTxGg.kt b/multisrc/overrides/madara/mangatxgg/src/MangaTxGg.kt new file mode 100644 index 0000000000..2a3fc88267 --- /dev/null +++ b/multisrc/overrides/madara/mangatxgg/src/MangaTxGg.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangatxgg + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaTxGg : Madara("Manga Tx.gg (unoriginal)", "https://mangatx.gg", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangatyrant/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangatyrant/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..74743325ee Binary files /dev/null and b/multisrc/overrides/madara/mangatyrant/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangatyrant/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangatyrant/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..328d8b00a0 Binary files /dev/null and b/multisrc/overrides/madara/mangatyrant/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangatyrant/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangatyrant/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..a9f6a61a2c Binary files /dev/null and b/multisrc/overrides/madara/mangatyrant/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangatyrant/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangatyrant/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6f5b31b4e3 Binary files /dev/null and b/multisrc/overrides/madara/mangatyrant/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangatyrant/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangatyrant/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..af9506f012 Binary files /dev/null and b/multisrc/overrides/madara/mangatyrant/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangatyrant/res/web_hi_res_512.png b/multisrc/overrides/madara/mangatyrant/res/web_hi_res_512.png new file mode 100644 index 0000000000..be48a766b7 Binary files /dev/null and b/multisrc/overrides/madara/mangatyrant/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangatyrant/src/MangaTyrant.kt b/multisrc/overrides/madara/mangatyrant/src/MangaTyrant.kt new file mode 100644 index 0000000000..88557eba36 --- /dev/null +++ b/multisrc/overrides/madara/mangatyrant/src/MangaTyrant.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.mangatyrant + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaTyrant : Madara("MangaTyrant", "https://mangatyrant.com", "en") { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mangaupdatestop/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d8bbacd490 Binary files /dev/null and b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaupdatestop/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ec35ead1d0 Binary files /dev/null and b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..bd959ac04d Binary files /dev/null and b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..58989ca02b Binary files /dev/null and b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3ae51ec0a2 Binary files /dev/null and b/multisrc/overrides/madara/mangaupdatestop/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mangaupdatestop/res/web_hi_res_512.png b/multisrc/overrides/madara/mangaupdatestop/res/web_hi_res_512.png new file mode 100644 index 0000000000..a624bd17dd Binary files /dev/null and b/multisrc/overrides/madara/mangaupdatestop/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mangaupdatestop/src/MangaUpdatesTop.kt b/multisrc/overrides/madara/mangaupdatestop/src/MangaUpdatesTop.kt new file mode 100644 index 0000000000..e8319f8289 --- /dev/null +++ b/multisrc/overrides/madara/mangaupdatestop/src/MangaUpdatesTop.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.mangaupdatestop + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class MangaUpdatesTop : Madara("MangaUpdates.top (unoriginal)", "https://mangaupdates.top", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhuadex/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manhuadex/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..e7fae49725 Binary files /dev/null and b/multisrc/overrides/madara/manhuadex/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuadex/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manhuadex/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..fbf331407c Binary files /dev/null and b/multisrc/overrides/madara/manhuadex/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuadex/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuadex/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..98fdb1ddd7 Binary files /dev/null and b/multisrc/overrides/madara/manhuadex/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuadex/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuadex/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..21f4f29efd Binary files /dev/null and b/multisrc/overrides/madara/manhuadex/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuadex/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuadex/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..44116a142d Binary files /dev/null and b/multisrc/overrides/madara/manhuadex/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuadex/res/web_hi_res_512.png b/multisrc/overrides/madara/manhuadex/res/web_hi_res_512.png new file mode 100644 index 0000000000..2bcad01a44 Binary files /dev/null and b/multisrc/overrides/madara/manhuadex/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manhuadex/src/ManhuaDex.kt b/multisrc/overrides/madara/manhuadex/src/ManhuaDex.kt new file mode 100644 index 0000000000..38509251f6 --- /dev/null +++ b/multisrc/overrides/madara/manhuadex/src/ManhuaDex.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manhuadex + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManhuaDex : Madara("ManhuaDex", "https://manhuadex.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhuafastnet/src/ManhuaFastNet.kt b/multisrc/overrides/madara/manhuafastnet/src/ManhuaFastNet.kt new file mode 100644 index 0000000000..cca61bb0c4 --- /dev/null +++ b/multisrc/overrides/madara/manhuafastnet/src/ManhuaFastNet.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manhuafastnet + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManhuaFastNet : Madara("ManhuaFast.net (unoriginal)", "https://manhuafast.net", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhuaga/src/Manhuaga.kt b/multisrc/overrides/madara/manhuaga/src/Manhuaga.kt index f10b60c7eb..9e8ddf99db 100644 --- a/multisrc/overrides/madara/manhuaga/src/Manhuaga.kt +++ b/multisrc/overrides/madara/manhuaga/src/Manhuaga.kt @@ -2,12 +2,9 @@ package eu.kanade.tachiyomi.extension.en.manhuaga import eu.kanade.tachiyomi.multisrc.madara.Madara import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class Manhuaga : Madara("Manhuaga", "https://manhuaga.com", "en") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .addInterceptor { chain -> val originalRequest = chain.request() chain.proceed(originalRequest).let { response -> diff --git a/multisrc/overrides/madara/manhuamanhwa/src/ManhuaManhwa.kt b/multisrc/overrides/madara/manhuamanhwa/src/ManhuaManhwa.kt new file mode 100644 index 0000000000..ee089eb82e --- /dev/null +++ b/multisrc/overrides/madara/manhuamanhwa/src/ManhuaManhwa.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.en.manhuamanhwa + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ManhuaManhwa : Madara( + "ManhuaManhwa", + "https://manhuamanhwa.com", + "en", + dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ROOT), +) { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhuamanhwaonline/src/ManhuaManhwaOnline.kt b/multisrc/overrides/madara/manhuamanhwaonline/src/ManhuaManhwaOnline.kt new file mode 100644 index 0000000000..81aea3a59d --- /dev/null +++ b/multisrc/overrides/madara/manhuamanhwaonline/src/ManhuaManhwaOnline.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manhuamanhwaonline + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManhuaManhwaOnline : Madara("ManhuaManhwa.online", "https://manhuamanhwa.online", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhuascaninfo/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..efeb9ba0bb Binary files /dev/null and b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuascaninfo/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ceae6c6035 Binary files /dev/null and b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..ebbe319321 Binary files /dev/null and b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1d5c406d0e Binary files /dev/null and b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..77b6bef37f Binary files /dev/null and b/multisrc/overrides/madara/manhuascaninfo/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuascaninfo/res/web_hi_res_512.png b/multisrc/overrides/madara/manhuascaninfo/res/web_hi_res_512.png new file mode 100644 index 0000000000..79d6a8907d Binary files /dev/null and b/multisrc/overrides/madara/manhuascaninfo/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manhuascaninfo/src/ManhuaScanInfo.kt b/multisrc/overrides/madara/manhuascaninfo/src/ManhuaScanInfo.kt new file mode 100644 index 0000000000..7a88dd1db6 --- /dev/null +++ b/multisrc/overrides/madara/manhuascaninfo/src/ManhuaScanInfo.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manhuascaninfo + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManhuaScanInfo : Madara("ManhuaScan.info (unoriginal)", "https://manhuascan.info", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhuazonghe/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manhuazonghe/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..850514942e Binary files /dev/null and b/multisrc/overrides/madara/manhuazonghe/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuazonghe/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manhuazonghe/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..d9a552289e Binary files /dev/null and b/multisrc/overrides/madara/manhuazonghe/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuazonghe/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuazonghe/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..8d4402cd23 Binary files /dev/null and b/multisrc/overrides/madara/manhuazonghe/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuazonghe/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuazonghe/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9fa5ca3503 Binary files /dev/null and b/multisrc/overrides/madara/manhuazonghe/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuazonghe/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhuazonghe/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..94188edc3f Binary files /dev/null and b/multisrc/overrides/madara/manhuazonghe/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhuazonghe/res/web_hi_res_512.png b/multisrc/overrides/madara/manhuazonghe/res/web_hi_res_512.png new file mode 100644 index 0000000000..c01736a24a Binary files /dev/null and b/multisrc/overrides/madara/manhuazonghe/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manhuazonghe/src/ManhuaZonghe.kt b/multisrc/overrides/madara/manhuazonghe/src/ManhuaZonghe.kt new file mode 100644 index 0000000000..212ec0eb0e --- /dev/null +++ b/multisrc/overrides/madara/manhuazonghe/src/ManhuaZonghe.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.en.manhuazonghe + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManhuaZonghe : Madara("Manhua Zonghe", "https://manhuazonghe.com", "en") { + override val useNewChapterEndpoint = false + override val filterNonMangaItems = false + override val mangaSubString = "manhua" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhwa2read/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manhwa2read/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..98e209fcec Binary files /dev/null and b/multisrc/overrides/madara/manhwa2read/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwa2read/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manhwa2read/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..b99e0beb64 Binary files /dev/null and b/multisrc/overrides/madara/manhwa2read/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwa2read/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwa2read/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..afa675f202 Binary files /dev/null and b/multisrc/overrides/madara/manhwa2read/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwa2read/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwa2read/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..01ed05c3b3 Binary files /dev/null and b/multisrc/overrides/madara/manhwa2read/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwa2read/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwa2read/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..71ef3de1cc Binary files /dev/null and b/multisrc/overrides/madara/manhwa2read/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwa2read/res/web_hi_res_512.png b/multisrc/overrides/madara/manhwa2read/res/web_hi_res_512.png new file mode 100644 index 0000000000..cfc34d4db7 Binary files /dev/null and b/multisrc/overrides/madara/manhwa2read/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manhwa2read/src/Manhwa2Read.kt b/multisrc/overrides/madara/manhwa2read/src/Manhwa2Read.kt new file mode 100644 index 0000000000..fbd6a15717 --- /dev/null +++ b/multisrc/overrides/madara/manhwa2read/src/Manhwa2Read.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.manhwa2read + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class Manhwa2Read : Madara("Manhwa2Read", "https://manhwa2read.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhwalatino/src/ManhwaLatino.kt b/multisrc/overrides/madara/manhwalatino/src/ManhwaLatino.kt index 9222c3e481..509fba0db8 100644 --- a/multisrc/overrides/madara/manhwalatino/src/ManhwaLatino.kt +++ b/multisrc/overrides/madara/manhwalatino/src/ManhwaLatino.kt @@ -1,6 +1,8 @@ package eu.kanade.tachiyomi.extension.es.manhwalatino import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.source.model.SChapter +import org.jsoup.nodes.Element import java.text.SimpleDateFormat import java.util.Locale @@ -10,12 +12,29 @@ class ManhwaLatino : Madara( "es", SimpleDateFormat("dd/MM/yyyy", Locale("es")), ) { - - override val supportsLatest = false - override val useNewChapterEndpoint = true - override val chapterUrlSelector = "a:eq(1)" + override val chapterUrlSelector = "div.mini-letters > a" override val mangaDetailsSelectorStatus = "div.post-content_item:contains(Estado del comic) > div.summary-content" + override val mangaDetailsSelectorDescription = "div.post-content_item:contains(Resumen) > h3 > div.summary-container" + + override fun chapterFromElement(element: Element): SChapter { + val chapter = SChapter.create() + + with(element) { + select(chapterUrlSelector).first()?.let { urlElement -> + chapter.url = urlElement.attr("abs:href").let { + it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "" + } + chapter.name = urlElement.wholeText().substringAfter("\n") + } + + chapter.date_upload = select("img:not(.thumb)").firstOrNull()?.attr("alt")?.let { parseRelativeDate(it) } + ?: select("span a").firstOrNull()?.attr("title")?.let { parseRelativeDate(it) } + ?: parseChapterDate(select(chapterDateSelector()).firstOrNull()?.text()) + } + + return chapter + } } diff --git a/multisrc/overrides/madara/manhwamanhua/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manhwamanhua/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..b3f3b1a35c Binary files /dev/null and b/multisrc/overrides/madara/manhwamanhua/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwamanhua/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manhwamanhua/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ab7cb5721d Binary files /dev/null and b/multisrc/overrides/madara/manhwamanhua/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwamanhua/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwamanhua/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..d88ac01357 Binary files /dev/null and b/multisrc/overrides/madara/manhwamanhua/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwamanhua/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwamanhua/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..c37ace6409 Binary files /dev/null and b/multisrc/overrides/madara/manhwamanhua/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwamanhua/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwamanhua/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..dfef5100c7 Binary files /dev/null and b/multisrc/overrides/madara/manhwamanhua/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwamanhua/res/web_hi_res_512.png b/multisrc/overrides/madara/manhwamanhua/res/web_hi_res_512.png new file mode 100644 index 0000000000..0b35f11598 Binary files /dev/null and b/multisrc/overrides/madara/manhwamanhua/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manhwamanhua/src/ManhwaManhua.kt b/multisrc/overrides/madara/manhwamanhua/src/ManhwaManhua.kt new file mode 100644 index 0000000000..db15b5db2b --- /dev/null +++ b/multisrc/overrides/madara/manhwamanhua/src/ManhwaManhua.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.manhwamanhua + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ManhwaManhua : Madara("ManhwaManhua", "https://manhwamanhua.com", "en") { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/manhwanew/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/manhwanew/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..63c13ede7d Binary files /dev/null and b/multisrc/overrides/madara/manhwanew/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwanew/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/manhwanew/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..c75ee6c3ff Binary files /dev/null and b/multisrc/overrides/madara/manhwanew/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwanew/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwanew/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..eab92c6bd3 Binary files /dev/null and b/multisrc/overrides/madara/manhwanew/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwanew/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwanew/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8af570d5cb Binary files /dev/null and b/multisrc/overrides/madara/manhwanew/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwanew/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/manhwanew/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cc67d8eb86 Binary files /dev/null and b/multisrc/overrides/madara/manhwanew/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/manhwanew/res/web_hi_res_512.png b/multisrc/overrides/madara/manhwanew/res/web_hi_res_512.png new file mode 100644 index 0000000000..7cf4f91729 Binary files /dev/null and b/multisrc/overrides/madara/manhwanew/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/manhwanew/src/ManhwaNew.kt b/multisrc/overrides/madara/manhwanew/src/ManhwaNew.kt new file mode 100644 index 0000000000..17be4d42d6 --- /dev/null +++ b/multisrc/overrides/madara/manhwanew/src/ManhwaNew.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.en.manhwanew + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ManhwaNew : Madara( + "ManhwaNew", + "https://manhwanew.com", + "en", + dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ROOT), +) { + override val useNewChapterEndpoint = true + override val filterNonMangaItems = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/mantrazscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mantrazscan/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d2aa736cba Binary files /dev/null and b/multisrc/overrides/madara/mantrazscan/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mantrazscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mantrazscan/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..158cc31306 Binary files /dev/null and b/multisrc/overrides/madara/mantrazscan/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mantrazscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mantrazscan/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..67dc81edd5 Binary files /dev/null and b/multisrc/overrides/madara/mantrazscan/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mantrazscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mantrazscan/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0e76feae7c Binary files /dev/null and b/multisrc/overrides/madara/mantrazscan/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mantrazscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mantrazscan/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a91ea2d3a6 Binary files /dev/null and b/multisrc/overrides/madara/mantrazscan/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/mantrazscan/res/web_hi_res_512.png b/multisrc/overrides/madara/mantrazscan/res/web_hi_res_512.png new file mode 100644 index 0000000000..0c24dc41c5 Binary files /dev/null and b/multisrc/overrides/madara/mantrazscan/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/mantrazscan/src/MantrazScan.kt b/multisrc/overrides/madara/mantrazscan/src/MantrazScan.kt new file mode 100644 index 0000000000..6b1ed6b149 --- /dev/null +++ b/multisrc/overrides/madara/mantrazscan/src/MantrazScan.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.extension.es.mantrazscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class MantrazScan : Madara( + "Mantraz Scan", + "https://mantrazscan.com", + "es", + SimpleDateFormat("dd/MM/yyyy", Locale("es")), +) { + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/mgkomik/src/MGKomik.kt b/multisrc/overrides/madara/mgkomik/src/MGKomik.kt index d0d08cd82c..45cedac000 100644 --- a/multisrc/overrides/madara/mgkomik/src/MGKomik.kt +++ b/multisrc/overrides/madara/mgkomik/src/MGKomik.kt @@ -4,10 +4,12 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import java.text.SimpleDateFormat import java.util.Locale -class MGKomik : Madara("MG Komik", "https://mgkomik.com", "id", SimpleDateFormat("dd MMM yy", Locale.US)) { +class MGKomik : Madara("MG Komik", "https://mgkomik.id", "id", SimpleDateFormat("dd MMM yy", Locale.US)) { override val chapterUrlSuffix = "" override val mangaSubString = "komik" override fun searchMangaNextPageSelector() = "a.page.larger" + + override fun searchPage(page: Int): String = "halaman/$page/" } diff --git a/multisrc/overrides/madara/mhentais/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/mhentais/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index c0e871a856..0000000000 Binary files a/multisrc/overrides/madara/mhentais/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mhentais/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/mhentais/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 2ba963c405..0000000000 Binary files a/multisrc/overrides/madara/mhentais/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mhentais/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/mhentais/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index e703dac92c..0000000000 Binary files a/multisrc/overrides/madara/mhentais/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mhentais/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/mhentais/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index e444d3c6c9..0000000000 Binary files a/multisrc/overrides/madara/mhentais/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mhentais/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/mhentais/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index c5b58f2be4..0000000000 Binary files a/multisrc/overrides/madara/mhentais/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/mhentais/res/web_hi_res_512.png b/multisrc/overrides/madara/mhentais/res/web_hi_res_512.png deleted file mode 100644 index cbd7a4e2e8..0000000000 Binary files a/multisrc/overrides/madara/mhentais/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/mhentais/src/MHentais.kt b/multisrc/overrides/madara/mhentais/src/MHentais.kt deleted file mode 100644 index 11f59b8db2..0000000000 --- a/multisrc/overrides/madara/mhentais/src/MHentais.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.mhentais - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class MHentais : Madara( - "MHentais", - "https://mhentais.com", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/miradscanlator/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/miradscanlator/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index bd2eac9693..0000000000 Binary files a/multisrc/overrides/madara/miradscanlator/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/miradscanlator/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/miradscanlator/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index fba0c24d7a..0000000000 Binary files a/multisrc/overrides/madara/miradscanlator/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/miradscanlator/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/miradscanlator/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index e3566e414c..0000000000 Binary files a/multisrc/overrides/madara/miradscanlator/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/miradscanlator/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/miradscanlator/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 002d184571..0000000000 Binary files a/multisrc/overrides/madara/miradscanlator/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/miradscanlator/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/miradscanlator/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index add4646828..0000000000 Binary files a/multisrc/overrides/madara/miradscanlator/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/miradscanlator/res/web_hi_res_512.png b/multisrc/overrides/madara/miradscanlator/res/web_hi_res_512.png deleted file mode 100644 index d21da04a1e..0000000000 Binary files a/multisrc/overrides/madara/miradscanlator/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/miradscanlator/src/MiradScanlator.kt b/multisrc/overrides/madara/miradscanlator/src/MiradScanlator.kt deleted file mode 100644 index 747eb3d13a..0000000000 --- a/multisrc/overrides/madara/miradscanlator/src/MiradScanlator.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.miradscanlator - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class MiradScanlator : Madara( - "Mirad Scanlator", - "https://miradscanlator.site", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt-BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/mixedmanga/src/MixedManga.kt b/multisrc/overrides/madara/mixedmanga/src/MixedManga.kt index 13c9f89349..136b989afd 100644 --- a/multisrc/overrides/madara/mixedmanga/src/MixedManga.kt +++ b/multisrc/overrides/madara/mixedmanga/src/MixedManga.kt @@ -1,10 +1,7 @@ package eu.kanade.tachiyomi.extension.en.mixedmanga import eu.kanade.tachiyomi.multisrc.madara.Madara -import okhttp3.Headers import java.text.SimpleDateFormat import java.util.Locale -class MixedManga : Madara("Mixed Manga", "https://mixedmanga.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US)) { - override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl) -} +class MixedManga : Madara("Mixed Manga", "https://mixedmanga.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US)) diff --git a/multisrc/overrides/madara/mmscans/src/MMScans.kt b/multisrc/overrides/madara/mmscans/src/MMScans.kt index db859e4ba4..03252b7a70 100644 --- a/multisrc/overrides/madara/mmscans/src/MMScans.kt +++ b/multisrc/overrides/madara/mmscans/src/MMScans.kt @@ -1,8 +1,11 @@ package eu.kanade.tachiyomi.extension.en.mmscans import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.FormBody +import okhttp3.Request import org.jsoup.nodes.Element class MMScans : Madara("MMScans", "https://mm-scans.org", "en") { @@ -12,8 +15,47 @@ class MMScans : Madara("MMScans", "https://mm-scans.org", "en") { override val popularMangaUrlSelector = "div.item-summary a" override fun chapterListSelector() = "li.chapter-li" - override fun searchMangaSelector() = "a" + override fun searchMangaSelector() = ".search-wrap >.tab-content-wrap > a" + override fun searchMangaNextPageSelector(): String? = "body:not(:has(.no-posts))" + fun oldLoadMoreRequest(page: Int, metaKey: String): Request { + val form = FormBody.Builder() + .add("action", "madara_load_more") + .add("page", page.toString()) + .add("template", "madara-core/content/content-archive") + .add("vars[paged]", "1") + .add("vars[orderby]", "meta_value_num") + .add("vars[template]", "archive") + .add("vars[sidebar]", "right") + .add("vars[post_type]", "wp-manga") + .add("vars[post_status]", "publish") + .add("vars[meta_key]", metaKey) + .add("vars[meta_query][0][paged]", "1") + .add("vars[meta_query][0][orderby]", "meta_value_num") + .add("vars[meta_query][0][template]", "archive") + .add("vars[meta_query][0][sidebar]", "right") + .add("vars[meta_query][0][post_type]", "wp-manga") + .add("vars[meta_query][0][post_status]", "publish") + .add("vars[meta_query][0][meta_key]", metaKey) + .add("vars[meta_query][relation]", "AND") + .add("vars[manga_archives_item_layout]", "default") + .build() + + val xhrHeaders = headersBuilder() + .add("Content-Length", form.contentLength().toString()) + .add("Content-Type", form.contentType().toString()) + .add("X-Requested-With", "XMLHttpRequest") + .build() + + return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, form) + } + + override fun popularMangaRequest(page: Int): Request { + return oldLoadMoreRequest(page - 1, "_wp_manga_views") + } + override fun latestUpdatesRequest(page: Int): Request { + return oldLoadMoreRequest(page - 1, "_latest_update") + } override fun popularMangaFromElement(element: Element): SManga { val manga = SManga.create() diff --git a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/myuniversescanlator/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index dbc382eae8..0000000000 Binary files a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/myuniversescanlator/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index a3eade6e72..0000000000 Binary files a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index acd05f899a..0000000000 Binary files a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index f22255ba8b..0000000000 Binary files a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index b473a5828d..0000000000 Binary files a/multisrc/overrides/madara/myuniversescanlator/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/myuniversescanlator/res/web_hi_res_512.png b/multisrc/overrides/madara/myuniversescanlator/res/web_hi_res_512.png deleted file mode 100644 index f4199f3c76..0000000000 Binary files a/multisrc/overrides/madara/myuniversescanlator/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/myuniversescanlator/src/MyUniverseScanlator.kt b/multisrc/overrides/madara/myuniversescanlator/src/MyUniverseScanlator.kt deleted file mode 100644 index 3ddee38a81..0000000000 --- a/multisrc/overrides/madara/myuniversescanlator/src/MyUniverseScanlator.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.myuniversescanlator - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class MyUniverseScanlator : Madara( - "My Universe Scanlator", - "https://muscan.com.br", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/nekobreakerscan/src/NekoBreakerScan.kt b/multisrc/overrides/madara/nekobreakerscan/src/NekoBreakerScan.kt deleted file mode 100644 index 258cfc600a..0000000000 --- a/multisrc/overrides/madara/nekobreakerscan/src/NekoBreakerScan.kt +++ /dev/null @@ -1,17 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.nekobreakerscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale - -class NekoBreakerScan : Madara( - "NekoBreaker Scan", - "https://nekobreakerscan.com", - "pt-BR", - SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .build() -} diff --git a/multisrc/overrides/madara/nekopostco/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/nekopostco/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..9a3861d3e0 Binary files /dev/null and b/multisrc/overrides/madara/nekopostco/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/nekopostco/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/nekopostco/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..d6f802b833 Binary files /dev/null and b/multisrc/overrides/madara/nekopostco/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/nekopostco/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/nekopostco/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..4a678c8e43 Binary files /dev/null and b/multisrc/overrides/madara/nekopostco/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/nekopostco/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/nekopostco/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a2ef8a6450 Binary files /dev/null and b/multisrc/overrides/madara/nekopostco/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/nekopostco/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/nekopostco/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..90feb1ac29 Binary files /dev/null and b/multisrc/overrides/madara/nekopostco/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/nekopostco/res/web_hi_res_512.png b/multisrc/overrides/madara/nekopostco/res/web_hi_res_512.png new file mode 100644 index 0000000000..8f8ae9aa78 Binary files /dev/null and b/multisrc/overrides/madara/nekopostco/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/nekopostco/src/NekoPostCo.kt b/multisrc/overrides/madara/nekopostco/src/NekoPostCo.kt new file mode 100644 index 0000000000..e6f41d5541 --- /dev/null +++ b/multisrc/overrides/madara/nekopostco/src/NekoPostCo.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.th.nekopostco + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class NekoPostCo : Madara( + "NekoPost.co (unoriginal)", + "https://www.nekopost.co", + "th", + dateFormat = SimpleDateFormat("d MMMM yyyy", Locale("th")), +) { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/ninjascan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/ninjascan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index fe4fc4642f..0000000000 Binary files a/multisrc/overrides/madara/ninjascan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ninjascan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/ninjascan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 5ff9fabb2e..0000000000 Binary files a/multisrc/overrides/madara/ninjascan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ninjascan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/ninjascan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index d8840bfc65..0000000000 Binary files a/multisrc/overrides/madara/ninjascan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ninjascan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/ninjascan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 1a5cf14e47..0000000000 Binary files a/multisrc/overrides/madara/ninjascan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ninjascan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/ninjascan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 6dc40fdd12..0000000000 Binary files a/multisrc/overrides/madara/ninjascan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/ninjascan/res/web_hi_res_512.png b/multisrc/overrides/madara/ninjascan/res/web_hi_res_512.png deleted file mode 100644 index f7a2288a7c..0000000000 Binary files a/multisrc/overrides/madara/ninjascan/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/ninjascan/src/NinjaScan.kt b/multisrc/overrides/madara/ninjascan/src/NinjaScan.kt deleted file mode 100644 index a74850c7e3..0000000000 --- a/multisrc/overrides/madara/ninjascan/src/NinjaScan.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.ninjascan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class NinjaScan : Madara( - "Ninja Scan", - "https://ninjascan.xyz", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/onlymanhwa/src/OnlyManhwa.kt b/multisrc/overrides/madara/onlymanhwa/src/OnlyManhwa.kt new file mode 100644 index 0000000000..5013f8987f --- /dev/null +++ b/multisrc/overrides/madara/onlymanhwa/src/OnlyManhwa.kt @@ -0,0 +1,18 @@ +package eu.kanade.tachiyomi.extension.en.onlymanhwa + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class OnlyManhwa : Madara( + "OnlyManhwa", + "https://onlymanhwa.org", + "en", + dateFormat = SimpleDateFormat("d 'de' MMMM 'de' yyyy", Locale.ENGLISH), +) { + override val useNewChapterEndpoint = true + override val mangaSubString = "manhwa" + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/paragonscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/paragonscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..0cc5a3e4fb Binary files /dev/null and b/multisrc/overrides/madara/paragonscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/paragonscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/paragonscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..f5efd36d30 Binary files /dev/null and b/multisrc/overrides/madara/paragonscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/paragonscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/paragonscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2c91a0d84e Binary files /dev/null and b/multisrc/overrides/madara/paragonscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/paragonscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/paragonscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b0ae9e84be Binary files /dev/null and b/multisrc/overrides/madara/paragonscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/paragonscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/paragonscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..30241a3c72 Binary files /dev/null and b/multisrc/overrides/madara/paragonscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/paragonscans/res/web_hi_res_512.png b/multisrc/overrides/madara/paragonscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..916b36beee Binary files /dev/null and b/multisrc/overrides/madara/paragonscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/paragonscans/src/ParagonScans.kt b/multisrc/overrides/madara/paragonscans/src/ParagonScans.kt new file mode 100644 index 0000000000..ef4d7ba70d --- /dev/null +++ b/multisrc/overrides/madara/paragonscans/src/ParagonScans.kt @@ -0,0 +1,40 @@ +package eu.kanade.tachiyomi.extension.en.paragonscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale + +class ParagonScans : Madara( + "Paragon Scans", + "https://paragonscans.com", + "en", + dateFormat = SimpleDateFormat("MM/dd/yyyy", Locale.ROOT), +) { + override val useNewChapterEndpoint = true + override val mangaSubString = "mangax" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" + + override fun parseChapterDate(date: String?): Long { + date ?: return 0 + + val splitDate = date.split(' ') + if (splitDate.size < 2) { + return super.parseChapterDate(date) + } + + val (amountStr, unit) = splitDate + val amount = amountStr.toIntOrNull() + ?: return super.parseChapterDate(date) + + val cal = Calendar.getInstance() + return when (unit) { + "s" -> cal.apply { add(Calendar.SECOND, -amount) }.timeInMillis // not observed + "m" -> cal.apply { add(Calendar.MINUTE, -amount) }.timeInMillis // not observed + "h" -> cal.apply { add(Calendar.HOUR_OF_DAY, -amount) }.timeInMillis + "d" -> cal.apply { add(Calendar.DAY_OF_MONTH, -amount) }.timeInMillis + else -> super.parseChapterDate(date) + } + } +} diff --git a/multisrc/overrides/madara/pawmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/pawmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..0e5b55b8b7 Binary files /dev/null and b/multisrc/overrides/madara/pawmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/pawmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/pawmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..8d78e85b8a Binary files /dev/null and b/multisrc/overrides/madara/pawmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/pawmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/pawmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..8442eba822 Binary files /dev/null and b/multisrc/overrides/madara/pawmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/pawmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/pawmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..4b5b3cb52c Binary files /dev/null and b/multisrc/overrides/madara/pawmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/pawmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/pawmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..fa47aeac2a Binary files /dev/null and b/multisrc/overrides/madara/pawmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/pawmanga/res/web_hi_res_512.png b/multisrc/overrides/madara/pawmanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..72158ffb51 Binary files /dev/null and b/multisrc/overrides/madara/pawmanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/pawmanga/src/PawManga.kt b/multisrc/overrides/madara/pawmanga/src/PawManga.kt new file mode 100644 index 0000000000..f82a6f9714 --- /dev/null +++ b/multisrc/overrides/madara/pawmanga/src/PawManga.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.pawmanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class PawManga : Madara("Paw Manga", "https://pawmanga.com", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/peachscan/src/PeachScan.kt b/multisrc/overrides/madara/peachscan/src/PeachScan.kt deleted file mode 100644 index 85051217f8..0000000000 --- a/multisrc/overrides/madara/peachscan/src/PeachScan.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.peachscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class PeachScan : Madara( - "Peach Scan", - "https://www.peachscan.com", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/phoenixfansub/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/phoenixfansub/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index b6eec99d99..0000000000 Binary files a/multisrc/overrides/madara/phoenixfansub/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/phoenixfansub/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/phoenixfansub/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index d172832113..0000000000 Binary files a/multisrc/overrides/madara/phoenixfansub/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/phoenixfansub/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/phoenixfansub/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 2218cb154b..0000000000 Binary files a/multisrc/overrides/madara/phoenixfansub/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/phoenixfansub/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/phoenixfansub/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 769a3a6cf3..0000000000 Binary files a/multisrc/overrides/madara/phoenixfansub/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/phoenixfansub/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/phoenixfansub/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 1498827298..0000000000 Binary files a/multisrc/overrides/madara/phoenixfansub/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/phoenixfansub/res/web_hi_res_512.png b/multisrc/overrides/madara/phoenixfansub/res/web_hi_res_512.png deleted file mode 100644 index 5ed840fb00..0000000000 Binary files a/multisrc/overrides/madara/phoenixfansub/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/phoenixfansub/src/PhoenixFansub.kt b/multisrc/overrides/madara/phoenixfansub/src/PhoenixFansub.kt deleted file mode 100644 index 0437b0b69a..0000000000 --- a/multisrc/overrides/madara/phoenixfansub/src/PhoenixFansub.kt +++ /dev/null @@ -1,16 +0,0 @@ -package eu.kanade.tachiyomi.extension.es.phoenixfansub - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class PhoenixFansub : Madara( - "Phoenix Fansub", - "https://phoenixmangas.com", - "es", - dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), -) { - // Site moved from MangaThemesia to Madara - override val versionId = 2 - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/ponymanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/ponymanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..5e9a385346 Binary files /dev/null and b/multisrc/overrides/madara/ponymanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/ponymanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/ponymanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..f594216ced Binary files /dev/null and b/multisrc/overrides/madara/ponymanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/ponymanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/ponymanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..204a043d73 Binary files /dev/null and b/multisrc/overrides/madara/ponymanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/ponymanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/ponymanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9b4a5c7062 Binary files /dev/null and b/multisrc/overrides/madara/ponymanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/ponymanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/ponymanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8fa44a9ded Binary files /dev/null and b/multisrc/overrides/madara/ponymanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/ponymanga/res/web_hi_res_512.png b/multisrc/overrides/madara/ponymanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..b86a13bad9 Binary files /dev/null and b/multisrc/overrides/madara/ponymanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/ponymanga/src/PonyManga.kt b/multisrc/overrides/madara/ponymanga/src/PonyManga.kt new file mode 100644 index 0000000000..559b8df951 --- /dev/null +++ b/multisrc/overrides/madara/ponymanga/src/PonyManga.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.ponymanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class PonyManga : Madara("Pony Manga", "https://ponymanga.com", "en") { + override val useNewChapterEndpoint = false + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/randomscan/src/RandomScan.kt b/multisrc/overrides/madara/randomscan/src/RandomScan.kt index 6fd07aaea1..dd72ddad59 100644 --- a/multisrc/overrides/madara/randomscan/src/RandomScan.kt +++ b/multisrc/overrides/madara/randomscan/src/RandomScan.kt @@ -9,12 +9,14 @@ import java.util.concurrent.TimeUnit class RandomScan : Madara( "Random Scan", - "https://randomscans.com", + "https://randomscanlators.net", "pt-BR", - SimpleDateFormat("dd 'de' MMMM 'de' yyyy", Locale("pt", "BR")), + SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), ) { override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 2, TimeUnit.SECONDS) .build() + + override val useNewChapterEndpoint = true } diff --git a/multisrc/overrides/madara/readergen/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/readergen/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..46076aec52 Binary files /dev/null and b/multisrc/overrides/madara/readergen/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/readergen/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/readergen/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..bb77a4f8ca Binary files /dev/null and b/multisrc/overrides/madara/readergen/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/readergen/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/readergen/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..26f49e502e Binary files /dev/null and b/multisrc/overrides/madara/readergen/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/readergen/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/readergen/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..48a855d254 Binary files /dev/null and b/multisrc/overrides/madara/readergen/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/readergen/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/readergen/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ecaf51d162 Binary files /dev/null and b/multisrc/overrides/madara/readergen/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/readergen/res/web_hi_res_512.png b/multisrc/overrides/madara/readergen/res/web_hi_res_512.png new file mode 100644 index 0000000000..d79614b274 Binary files /dev/null and b/multisrc/overrides/madara/readergen/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/readergen/src/ReaderGen.kt b/multisrc/overrides/madara/readergen/src/ReaderGen.kt new file mode 100644 index 0000000000..4127e93bf9 --- /dev/null +++ b/multisrc/overrides/madara/readergen/src/ReaderGen.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.fr.readergen + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.GET +import okhttp3.Request + +class ReaderGen : Madara("ReaderGen", "https://fr.readergen.fr", "fr") { + override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/page/$page/?s&post_type=wp-manga&m_orderby=views", headers) + override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/page/$page/?s&post_type=wp-manga&m_orderby=latest", headers) + override fun popularMangaSelector() = searchMangaSelector() +} diff --git a/multisrc/overrides/madara/rh2plusmanga/src/Rh2PlusManga.kt b/multisrc/overrides/madara/rh2plusmanga/src/Rh2PlusManga.kt index 7970a9b5aa..baddef60e6 100644 --- a/multisrc/overrides/madara/rh2plusmanga/src/Rh2PlusManga.kt +++ b/multisrc/overrides/madara/rh2plusmanga/src/Rh2PlusManga.kt @@ -6,4 +6,6 @@ import java.util.Locale class Rh2PlusManga : Madara("Rh2PlusManga", "https://www.rh2plusmanga.com", "th", SimpleDateFormat("d MMMM yyyy", Locale("th"))) { override val filterNonMangaItems = false + + override val pageListParseSelector = ".reading-content img" } diff --git a/multisrc/overrides/madara/rightdarkscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/rightdarkscan/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..80bbe4c250 Binary files /dev/null and b/multisrc/overrides/madara/rightdarkscan/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/rightdarkscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/rightdarkscan/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ebdff01657 Binary files /dev/null and b/multisrc/overrides/madara/rightdarkscan/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/rightdarkscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/rightdarkscan/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..a5e115d915 Binary files /dev/null and b/multisrc/overrides/madara/rightdarkscan/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/rightdarkscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/rightdarkscan/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..733a0120e2 Binary files /dev/null and b/multisrc/overrides/madara/rightdarkscan/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/rightdarkscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/rightdarkscan/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a1784a3fd4 Binary files /dev/null and b/multisrc/overrides/madara/rightdarkscan/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/rightdarkscan/res/web_hi_res_512.png b/multisrc/overrides/madara/rightdarkscan/res/web_hi_res_512.png new file mode 100644 index 0000000000..4e58fd4736 Binary files /dev/null and b/multisrc/overrides/madara/rightdarkscan/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/rightdarkscan/src/RightdarkScan.kt b/multisrc/overrides/madara/rightdarkscan/src/RightdarkScan.kt new file mode 100644 index 0000000000..6a328f797f --- /dev/null +++ b/multisrc/overrides/madara/rightdarkscan/src/RightdarkScan.kt @@ -0,0 +1,19 @@ +package eu.kanade.tachiyomi.extension.es.rightdarkscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import java.text.SimpleDateFormat +import java.util.Locale + +class RightdarkScan : Madara( + "Rightdark Scan", + "https://rightdark-scan.com", + "es", + SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) { + override val client = super.client.newBuilder() + .rateLimit(2, 1) + .build() + + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/rwbyscan/src/RwbyScan.kt b/multisrc/overrides/madara/rwbyscan/src/RwbyScan.kt deleted file mode 100644 index c0b5fa91b0..0000000000 --- a/multisrc/overrides/madara/rwbyscan/src/RwbyScan.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.rwbyscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class RwbyScan : Madara( - "RWBY Scan", - "https://rwbyscan.site", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/sensainayuri/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/sensainayuri/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 68f6c16708..0000000000 Binary files a/multisrc/overrides/madara/sensainayuri/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sensainayuri/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/sensainayuri/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index fd9546f059..0000000000 Binary files a/multisrc/overrides/madara/sensainayuri/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sensainayuri/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/sensainayuri/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 65615a9d40..0000000000 Binary files a/multisrc/overrides/madara/sensainayuri/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sensainayuri/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/sensainayuri/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 5fb86b9584..0000000000 Binary files a/multisrc/overrides/madara/sensainayuri/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sensainayuri/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/sensainayuri/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 183d25fcd6..0000000000 Binary files a/multisrc/overrides/madara/sensainayuri/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sensainayuri/res/web_hi_res_512.png b/multisrc/overrides/madara/sensainayuri/res/web_hi_res_512.png deleted file mode 100644 index 630676d26d..0000000000 Binary files a/multisrc/overrides/madara/sensainayuri/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/sensainayuri/src/SensainaYuri.kt b/multisrc/overrides/madara/sensainayuri/src/SensainaYuri.kt deleted file mode 100644 index cc374ffe19..0000000000 --- a/multisrc/overrides/madara/sensainayuri/src/SensainaYuri.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.sensainayuri - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class SensainaYuri : Madara( - "Sensaina Yuri", - "https://sensainayuri.dropescan.com", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/shadowtrad/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/shadowtrad/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..c751ecee60 Binary files /dev/null and b/multisrc/overrides/madara/shadowtrad/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shadowtrad/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/shadowtrad/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..b87862687a Binary files /dev/null and b/multisrc/overrides/madara/shadowtrad/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shadowtrad/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/shadowtrad/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..55c42162ba Binary files /dev/null and b/multisrc/overrides/madara/shadowtrad/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shadowtrad/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/shadowtrad/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7ccc160b0f Binary files /dev/null and b/multisrc/overrides/madara/shadowtrad/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shadowtrad/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/shadowtrad/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..26ada4639c Binary files /dev/null and b/multisrc/overrides/madara/shadowtrad/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shadowtrad/res/web_hi_res_512.png b/multisrc/overrides/madara/shadowtrad/res/web_hi_res_512.png new file mode 100644 index 0000000000..a9e1b31ef5 Binary files /dev/null and b/multisrc/overrides/madara/shadowtrad/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/shadowtrad/src/Shadowtrad.kt b/multisrc/overrides/madara/shadowtrad/src/Shadowtrad.kt new file mode 100644 index 0000000000..8a3250f318 --- /dev/null +++ b/multisrc/overrides/madara/shadowtrad/src/Shadowtrad.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.fr.shadowtrad + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class Shadowtrad : Madara("Shadowtrad", "https://shadowtrad.net", "fr", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.FRANCE)) { + override val useNewChapterEndpoint = true + override val mangaDetailsSelectorAuthor = "div.manga-authors > a" + override val mangaDetailsSelectorDescription = "div.manga-summary > .description, div.manga-summary" + override val chapterUrlSuffix = "" +} diff --git a/multisrc/overrides/madara/shibamanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/shibamanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..ae815a305c Binary files /dev/null and b/multisrc/overrides/madara/shibamanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shibamanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/shibamanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..c98bb95076 Binary files /dev/null and b/multisrc/overrides/madara/shibamanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shibamanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/shibamanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..6d5ce5e477 Binary files /dev/null and b/multisrc/overrides/madara/shibamanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shibamanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/shibamanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cb79d239f6 Binary files /dev/null and b/multisrc/overrides/madara/shibamanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shibamanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/shibamanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0be1e33a89 Binary files /dev/null and b/multisrc/overrides/madara/shibamanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/shibamanga/res/web_hi_res_512.png b/multisrc/overrides/madara/shibamanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..ddb05c2892 Binary files /dev/null and b/multisrc/overrides/madara/shibamanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/shibamanga/src/ShibaManga.kt b/multisrc/overrides/madara/shibamanga/src/ShibaManga.kt new file mode 100644 index 0000000000..e37cf7b743 --- /dev/null +++ b/multisrc/overrides/madara/shibamanga/src/ShibaManga.kt @@ -0,0 +1,23 @@ +package eu.kanade.tachiyomi.extension.en.shibamanga + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class ShibaManga : Madara( + "Shiba Manga", + "https://shibamanga.com", + "en", + SimpleDateFormat("MM/dd/yyyy", Locale.US), +) { + override val filterNonMangaItems = false + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String { + return if (page > 1) { + "page/$page/" + } else { + "" + } + } +} diff --git a/multisrc/overrides/madara/shieldmanga/src/ShieldManga.kt b/multisrc/overrides/madara/shieldmanga/src/ShieldManga.kt index 6aa6a9ef9b..e33716ae35 100644 --- a/multisrc/overrides/madara/shieldmanga/src/ShieldManga.kt +++ b/multisrc/overrides/madara/shieldmanga/src/ShieldManga.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.en.shieldmanga import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class ShieldManga : Madara("Shield Manga", "https://shieldmanga.io", "en") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1) .build() diff --git a/multisrc/overrides/madara/shinigami/src/Shinigami.kt b/multisrc/overrides/madara/shinigami/src/Shinigami.kt index db53a74acb..4ddde9d083 100644 --- a/multisrc/overrides/madara/shinigami/src/Shinigami.kt +++ b/multisrc/overrides/madara/shinigami/src/Shinigami.kt @@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.source.model.SChapter import okhttp3.HttpUrl.Companion.toHttpUrl import org.jsoup.nodes.Element -class Shinigami : Madara("Shinigami", "https://shinigami.id", "id") { +class Shinigami : Madara("Shinigami", "https://shinigami.ae", "id") { // moved from Reaper Scans (id) to Shinigami (id) override val id = 3411809758861089969 diff --git a/multisrc/overrides/madara/shiraiscans/src/ShiraiScans.kt b/multisrc/overrides/madara/shiraiscans/src/ShiraiScans.kt deleted file mode 100644 index 6c03d38774..0000000000 --- a/multisrc/overrides/madara/shiraiscans/src/ShiraiScans.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.shiraiscans - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class ShiraiScans : Madara( - "Shirai Scans", - "https://shiraiscans.com.br", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/sodascan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/sodascan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index ac0ed378e5..0000000000 Binary files a/multisrc/overrides/madara/sodascan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sodascan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/sodascan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 572562aa8f..0000000000 Binary files a/multisrc/overrides/madara/sodascan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sodascan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/sodascan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 141ece0e28..0000000000 Binary files a/multisrc/overrides/madara/sodascan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sodascan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/sodascan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 1d55a099ea..0000000000 Binary files a/multisrc/overrides/madara/sodascan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sodascan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/sodascan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 5a72d8304c..0000000000 Binary files a/multisrc/overrides/madara/sodascan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/sodascan/res/web_hi_res_512.png b/multisrc/overrides/madara/sodascan/res/web_hi_res_512.png deleted file mode 100644 index 38c16efe91..0000000000 Binary files a/multisrc/overrides/madara/sodascan/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/sodascan/src/SodaScan.kt b/multisrc/overrides/madara/sodascan/src/SodaScan.kt deleted file mode 100644 index ceb6b370f0..0000000000 --- a/multisrc/overrides/madara/sodascan/src/SodaScan.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.sodascan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class SodaScan : Madara( - "SodaScan", - "https://sodascan.xyz", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/sweetdesirescan/src/SweetDesireScan.kt b/multisrc/overrides/madara/sweetdesirescan/src/SweetDesireScan.kt deleted file mode 100644 index d5bff18edb..0000000000 --- a/multisrc/overrides/madara/sweetdesirescan/src/SweetDesireScan.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.sweetdesirescan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class SweetDesireScan : Madara( - "Sweet Desire Scan", - "https://sweetdesire.com.br", - "pt-BR", - SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 3, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/teenmanhua/src/TeenManhua.kt b/multisrc/overrides/madara/teenmanhua/src/TeenManhua.kt index 49ee684078..b010c54ee4 100644 --- a/multisrc/overrides/madara/teenmanhua/src/TeenManhua.kt +++ b/multisrc/overrides/madara/teenmanhua/src/TeenManhua.kt @@ -9,4 +9,6 @@ class TeenManhua : Madara( "https://teenmanhua.com", "en", dateFormat = SimpleDateFormat("dd/MM/yy", Locale.US), -) +) { + override val filterNonMangaItems = false +} diff --git a/multisrc/overrides/madara/templescan/src/TempleScan.kt b/multisrc/overrides/madara/templescan/src/TempleScan.kt deleted file mode 100644 index a7537980af..0000000000 --- a/multisrc/overrides/madara/templescan/src/TempleScan.kt +++ /dev/null @@ -1,47 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.templescan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import okhttp3.OkHttpClient -import org.jsoup.nodes.Element -import java.text.SimpleDateFormat -import java.util.Locale - -class TempleScan : Madara( - "Temple Scan", - "https://templescan.net", - "en", - SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH), -) { - override val mangaSubString = "comic" - override val useNewChapterEndpoint = true - override fun popularMangaSelector() = "div.c-tabs-item > div > div" - override val popularMangaUrlSelector = "div.series-box a" - override val mangaDetailsSelectorStatus = ".post-content_item:contains(Status) .summary-content" - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1) - .build() - - override fun popularMangaFromElement(element: Element): SManga { - return super.popularMangaFromElement(element).apply { - title = element.select(popularMangaUrlSelector).text() - } - } - - override fun searchPage(page: Int): String { - return if (page > 1) { - "page/$page/" - } else { - "" - } - } - - override fun chapterFromElement(element: Element): SChapter { - return super.chapterFromElement(element).apply { - name = element.select(".chapter-manhwa-title").text() - } - } -} diff --git a/multisrc/overrides/madara/templescanesp/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/templescanesp/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..aa68ca46e0 Binary files /dev/null and b/multisrc/overrides/madara/templescanesp/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/templescanesp/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/templescanesp/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3be9c027ae Binary files /dev/null and b/multisrc/overrides/madara/templescanesp/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/templescanesp/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/templescanesp/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..8d84cda9fa Binary files /dev/null and b/multisrc/overrides/madara/templescanesp/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/templescanesp/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/templescanesp/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..c709dc5a35 Binary files /dev/null and b/multisrc/overrides/madara/templescanesp/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/templescanesp/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/templescanesp/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b093413dbd Binary files /dev/null and b/multisrc/overrides/madara/templescanesp/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/templescanesp/res/web_hi_res_512.png b/multisrc/overrides/madara/templescanesp/res/web_hi_res_512.png new file mode 100644 index 0000000000..b15248d654 Binary files /dev/null and b/multisrc/overrides/madara/templescanesp/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/templescanesp/src/TempleScanEsp.kt b/multisrc/overrides/madara/templescanesp/src/TempleScanEsp.kt new file mode 100644 index 0000000000..3c5318afa6 --- /dev/null +++ b/multisrc/overrides/madara/templescanesp/src/TempleScanEsp.kt @@ -0,0 +1,103 @@ +package eu.kanade.tachiyomi.extension.es.templescanesp + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.FormBody +import okhttp3.Request +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +class TempleScanEsp : Madara( + "TempleScan", + "https://templescanesp.com", + "es", + SimpleDateFormat("dd.MM.yyyy", Locale("es")), +) { + override val mangaSubString = "series" + + override fun popularMangaSelector() = "div:has(> div#series-card)" + override val popularMangaUrlSelector = "div#series-card a.series-link" + override fun popularMangaNextPageSelector() = "body:not(:has(.no-posts))" + + override val mangaDetailsSelectorAuthor = "div.post-content_item:contains(Autor) div.summary-content" + override val mangaDetailsSelectorArtist = "div.post-content_item:contains(Artista) div.summary-content" + override val mangaDetailsSelectorStatus = "div.post-content_item:contains(Estado) div.summary-content" + + private fun loadMoreRequest(page: Int, metaKey: String): Request { + val formBody = FormBody.Builder().apply { + add("action", "madara_load_more") + add("page", page.toString()) + add("template", "madara-core/content/content-archive") + add("vars[paged]", "1") + add("vars[orderby]", "meta_value_num") + add("vars[template]", "archive") + add("vars[sidebar]", "full") + add("vars[meta_query][0][0][key]", "_wp_manga_chapter_type") + add("vars[meta_query][0][0][value]", "manga") + add("vars[meta_query][0][relation]", "AND") + add("vars[meta_query][relation]", "AND") + add("vars[post_type]", "wp-manga") + add("vars[post_status]", "publish") + add("vars[meta_key]", metaKey) + add("vars[manga_archives_item_layout]", "big_thumbnail") + }.build() + + val xhrHeaders = headersBuilder() + .add("Content-Length", formBody.contentLength().toString()) + .add("Content-Type", formBody.contentType().toString()) + .add("X-Requested-With", "XMLHttpRequest") + .build() + + return POST("$baseUrl/wp-admin/admin-ajax.php", xhrHeaders, formBody) + } + + override fun popularMangaRequest(page: Int): Request { + return loadMoreRequest(page - 1, "_wp_manga_views") + } + + override fun latestUpdatesRequest(page: Int): Request { + return loadMoreRequest(page - 1, "_latest_update") + } + + override fun popularMangaFromElement(element: Element): SManga { + val manga = SManga.create() + + with(element) { + select(popularMangaUrlSelector).first()?.let { + manga.setUrlWithoutDomain(it.attr("abs:href")) + } + + select("div.series-box .series-title").first()?.let { + manga.title = it.text() + } + + select("img").first()?.let { + manga.thumbnail_url = imageFromElement(it) + } + } + + return manga + } + + override fun chapterFromElement(element: Element): SChapter { + val chapter = SChapter.create() + + with(element) { + select(chapterUrlSelector).first()?.let { urlElement -> + chapter.url = urlElement.attr("abs:href").let { + it.substringBefore("?style=paged") + if (!it.endsWith(chapterUrlSuffix)) chapterUrlSuffix else "" + } + chapter.name = urlElement.select("p").text() + } + + chapter.date_upload = select("img:not(.thumb)").firstOrNull()?.attr("alt")?.let { parseRelativeDate(it) } + ?: select("span a").firstOrNull()?.attr("title")?.let { parseRelativeDate(it) } + ?: parseChapterDate(select(chapterDateSelector()).firstOrNull()?.text()) + } + + return chapter + } +} diff --git a/multisrc/overrides/madara/thesugar/src/TheSugar.kt b/multisrc/overrides/madara/thesugar/src/TheSugar.kt deleted file mode 100644 index e233ab3672..0000000000 --- a/multisrc/overrides/madara/thesugar/src/TheSugar.kt +++ /dev/null @@ -1,22 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.thesugar - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class TheSugar : Madara( - "The Sugar", - "https://thesugarscan.com", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val useNewChapterEndpoint = true -} diff --git a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/threequeensscanlator/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 4970c44c4d..0000000000 Binary files a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/threequeensscanlator/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index be647813f4..0000000000 Binary files a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 3d07e88274..0000000000 Binary files a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 671de7d7a7..0000000000 Binary files a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 7448561607..0000000000 Binary files a/multisrc/overrides/madara/threequeensscanlator/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/threequeensscanlator/res/web_hi_res_512.png b/multisrc/overrides/madara/threequeensscanlator/res/web_hi_res_512.png deleted file mode 100644 index b0992b3cf2..0000000000 Binary files a/multisrc/overrides/madara/threequeensscanlator/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/threequeensscanlator/src/ThreeQueensScanlator.kt b/multisrc/overrides/madara/threequeensscanlator/src/ThreeQueensScanlator.kt deleted file mode 100644 index a211c93b15..0000000000 --- a/multisrc/overrides/madara/threequeensscanlator/src/ThreeQueensScanlator.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.threequeensscanlator - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class ThreeQueensScanlator : Madara( - "Three Queens Scanlator", - "https://tqscan.com.br", - "pt-BR", - SimpleDateFormat("dd 'de' MMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/toonizy/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/toonizy/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..85890f979a Binary files /dev/null and b/multisrc/overrides/madara/toonizy/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/toonizy/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/toonizy/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..c78e313f04 Binary files /dev/null and b/multisrc/overrides/madara/toonizy/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/toonizy/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/toonizy/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2d21e7e900 Binary files /dev/null and b/multisrc/overrides/madara/toonizy/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/toonizy/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/toonizy/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..edcac38ca9 Binary files /dev/null and b/multisrc/overrides/madara/toonizy/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/toonizy/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/toonizy/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9c722cb0ee Binary files /dev/null and b/multisrc/overrides/madara/toonizy/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/toonizy/res/web_hi_res_512.png b/multisrc/overrides/madara/toonizy/res/web_hi_res_512.png new file mode 100644 index 0000000000..c378308af6 Binary files /dev/null and b/multisrc/overrides/madara/toonizy/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/toonizy/src/Toonizy.kt b/multisrc/overrides/madara/toonizy/src/Toonizy.kt new file mode 100644 index 0000000000..fce888281e --- /dev/null +++ b/multisrc/overrides/madara/toonizy/src/Toonizy.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.en.toonizy + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class Toonizy : Madara( + "Toonizy", + "https://toonizy.com", + "en", + dateFormat = SimpleDateFormat("MMM d, yy", Locale.ENGLISH), +) { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/topmanhua/src/TopManhua.kt b/multisrc/overrides/madara/topmanhua/src/TopManhua.kt index c27d6e19c9..4a8b4a3663 100644 --- a/multisrc/overrides/madara/topmanhua/src/TopManhua.kt +++ b/multisrc/overrides/madara/topmanhua/src/TopManhua.kt @@ -1,13 +1,10 @@ package eu.kanade.tachiyomi.extension.en.topmanhua import eu.kanade.tachiyomi.multisrc.madara.Madara -import okhttp3.Headers import java.text.SimpleDateFormat import java.util.Locale class TopManhua : Madara("Top Manhua", "https://topmanhua.com", "en", SimpleDateFormat("MM/dd/yy", Locale.US)) { - override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl) - // The website does not flag the content. override val filterNonMangaItems = false } diff --git a/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..08728fdd4e Binary files /dev/null and b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..c14c3fc3ea Binary files /dev/null and b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..75c19dac29 Binary files /dev/null and b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ffd3932795 Binary files /dev/null and b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d661e5611f Binary files /dev/null and b/multisrc/overrides/madara/traduccionesmoonlight/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/traduccionesmoonlight/res/web_hi_res_512.png b/multisrc/overrides/madara/traduccionesmoonlight/res/web_hi_res_512.png new file mode 100644 index 0000000000..bdfc28d88a Binary files /dev/null and b/multisrc/overrides/madara/traduccionesmoonlight/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/traduccionesmoonlight/src/TraduccionesMoonlight.kt b/multisrc/overrides/madara/traduccionesmoonlight/src/TraduccionesMoonlight.kt new file mode 100644 index 0000000000..6d20405564 --- /dev/null +++ b/multisrc/overrides/madara/traduccionesmoonlight/src/TraduccionesMoonlight.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.extension.es.traduccionesmoonlight + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class TraduccionesMoonlight : Madara( + "Traducciones Moonlight", + "https://traduccionesmoonlight.com", + "es", + SimpleDateFormat("dd 'de' MMMM 'de' yyyy", Locale("es")), +) { + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 8e167e74b7..0000000000 Binary files a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index e1e9fd3479..0000000000 Binary files a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 75e15f740e..0000000000 Binary files a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 120c9864a6..0000000000 Binary files a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 42e557dfa9..0000000000 Binary files a/multisrc/overrides/madara/tudoquadrinhos/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/tudoquadrinhos/res/web_hi_res_512.png b/multisrc/overrides/madara/tudoquadrinhos/res/web_hi_res_512.png deleted file mode 100644 index 8318e2f8d3..0000000000 Binary files a/multisrc/overrides/madara/tudoquadrinhos/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/tudoquadrinhos/src/TudoQuadrinhos.kt b/multisrc/overrides/madara/tudoquadrinhos/src/TudoQuadrinhos.kt deleted file mode 100644 index 3d7541bda4..0000000000 --- a/multisrc/overrides/madara/tudoquadrinhos/src/TudoQuadrinhos.kt +++ /dev/null @@ -1,23 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.tudoquadrinhos - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class TudoQuadrinhos : Madara( - "Tudo Quadrinhos", - "https://tudoquadrinhos.com.br", - "pt-BR", - SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - // The tags are just SEO keywords. - override val mangaDetailsSelectorTag: String = "" -} diff --git a/multisrc/overrides/madara/unitoon/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/unitoon/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..289df82873 Binary files /dev/null and b/multisrc/overrides/madara/unitoon/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoon/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/unitoon/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3769279907 Binary files /dev/null and b/multisrc/overrides/madara/unitoon/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoon/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/unitoon/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..f63ace60c3 Binary files /dev/null and b/multisrc/overrides/madara/unitoon/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoon/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/unitoon/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7d1709c7fa Binary files /dev/null and b/multisrc/overrides/madara/unitoon/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoon/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/unitoon/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ce25768b1b Binary files /dev/null and b/multisrc/overrides/madara/unitoon/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoon/res/web_hi_res_512.png b/multisrc/overrides/madara/unitoon/res/web_hi_res_512.png new file mode 100644 index 0000000000..262c279157 Binary files /dev/null and b/multisrc/overrides/madara/unitoon/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/unitoon/src/Unitoon.kt b/multisrc/overrides/madara/unitoon/src/Unitoon.kt new file mode 100644 index 0000000000..4f6c3fb279 --- /dev/null +++ b/multisrc/overrides/madara/unitoon/src/Unitoon.kt @@ -0,0 +1,19 @@ +package eu.kanade.tachiyomi.extension.es.unitoon + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import java.text.SimpleDateFormat +import java.util.Locale + +class Unitoon : Madara( + "Unitoon", + "https://lectorunitoon.com", + "es", + SimpleDateFormat("dd/MM/yyyy", Locale("es")), +) { + override val client = super.client.newBuilder() + .rateLimit(2, 1) + .build() + + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/unitoonoficial/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/unitoonoficial/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..9197367c76 Binary files /dev/null and b/multisrc/overrides/madara/unitoonoficial/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoonoficial/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/unitoonoficial/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..6fb6d41030 Binary files /dev/null and b/multisrc/overrides/madara/unitoonoficial/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoonoficial/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/unitoonoficial/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..0d1d4ac36a Binary files /dev/null and b/multisrc/overrides/madara/unitoonoficial/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoonoficial/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/unitoonoficial/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..21a2f11b0a Binary files /dev/null and b/multisrc/overrides/madara/unitoonoficial/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoonoficial/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/unitoonoficial/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..35cd506f51 Binary files /dev/null and b/multisrc/overrides/madara/unitoonoficial/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/unitoonoficial/res/web_hi_res_512.png b/multisrc/overrides/madara/unitoonoficial/res/web_hi_res_512.png new file mode 100644 index 0000000000..7bde7ec0e0 Binary files /dev/null and b/multisrc/overrides/madara/unitoonoficial/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/unitoonoficial/src/UnitoonOficial.kt b/multisrc/overrides/madara/unitoonoficial/src/UnitoonOficial.kt new file mode 100644 index 0000000000..428f4f1c3c --- /dev/null +++ b/multisrc/overrides/madara/unitoonoficial/src/UnitoonOficial.kt @@ -0,0 +1,14 @@ +package eu.kanade.tachiyomi.extension.es.unitoonoficial + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import java.text.SimpleDateFormat +import java.util.Locale + +class UnitoonOficial : Madara( + "Unitoon Oficial", + "https://unitoonoficial.com", + "es", + SimpleDateFormat("dd/MM/yyyy", Locale("es")), +) { + override val useNewChapterEndpoint = true +} diff --git a/multisrc/overrides/madara/visbellum/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/visbellum/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index cd777dc5fa..0000000000 Binary files a/multisrc/overrides/madara/visbellum/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/visbellum/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/visbellum/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index a5874ec461..0000000000 Binary files a/multisrc/overrides/madara/visbellum/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/visbellum/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/visbellum/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 52f273a417..0000000000 Binary files a/multisrc/overrides/madara/visbellum/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/visbellum/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/visbellum/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 3ae6be76f5..0000000000 Binary files a/multisrc/overrides/madara/visbellum/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/visbellum/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/visbellum/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 56ebb3db57..0000000000 Binary files a/multisrc/overrides/madara/visbellum/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/visbellum/res/web_hi_res_512.png b/multisrc/overrides/madara/visbellum/res/web_hi_res_512.png deleted file mode 100644 index b98919c32b..0000000000 Binary files a/multisrc/overrides/madara/visbellum/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/visbellum/src/Visbellum.kt b/multisrc/overrides/madara/visbellum/src/Visbellum.kt deleted file mode 100644 index 1421998d23..0000000000 --- a/multisrc/overrides/madara/visbellum/src/Visbellum.kt +++ /dev/null @@ -1,20 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.visbellum - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class Visbellum : Madara( - "Visbellum", - "https://visbellum.com", - "pt-BR", - SimpleDateFormat("dd 'de' MMMMM 'de' yyyy", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() -} diff --git a/multisrc/overrides/madara/voircomic/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/voircomic/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 63777d07e9..0000000000 Binary files a/multisrc/overrides/madara/voircomic/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/voircomic/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/voircomic/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 393cf245d6..0000000000 Binary files a/multisrc/overrides/madara/voircomic/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/voircomic/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/voircomic/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1ed4ba3927..0000000000 Binary files a/multisrc/overrides/madara/voircomic/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/voircomic/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/voircomic/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 6142066c46..0000000000 Binary files a/multisrc/overrides/madara/voircomic/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/voircomic/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/voircomic/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 24b220cf46..0000000000 Binary files a/multisrc/overrides/madara/voircomic/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/voircomic/res/web_hi_res_512.png b/multisrc/overrides/madara/voircomic/res/web_hi_res_512.png deleted file mode 100644 index 4eb393d0b6..0000000000 Binary files a/multisrc/overrides/madara/voircomic/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/voircomic/src/VoirComic.kt b/multisrc/overrides/madara/voircomic/src/VoirComic.kt deleted file mode 100644 index a5782c0ccb..0000000000 --- a/multisrc/overrides/madara/voircomic/src/VoirComic.kt +++ /dev/null @@ -1,7 +0,0 @@ -package eu.kanade.tachiyomi.extension.fr.voircomic - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import java.text.SimpleDateFormat -import java.util.Locale - -class VoirComic : Madara("VoirComic", "https://voircomic.com", "fr", dateFormat = SimpleDateFormat("d MMMM yyyy", Locale.FRANCE)) diff --git a/multisrc/overrides/madara/warqueenscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/warqueenscan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 325d68a081..0000000000 Binary files a/multisrc/overrides/madara/warqueenscan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/warqueenscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/warqueenscan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 2d60fa9473..0000000000 Binary files a/multisrc/overrides/madara/warqueenscan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/warqueenscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/warqueenscan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 1ec905994b..0000000000 Binary files a/multisrc/overrides/madara/warqueenscan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/warqueenscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/warqueenscan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 047d04596c..0000000000 Binary files a/multisrc/overrides/madara/warqueenscan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/warqueenscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/warqueenscan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index ff8f468ada..0000000000 Binary files a/multisrc/overrides/madara/warqueenscan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/madara/warqueenscan/res/web_hi_res_512.png b/multisrc/overrides/madara/warqueenscan/res/web_hi_res_512.png deleted file mode 100644 index 90ec98cea8..0000000000 Binary files a/multisrc/overrides/madara/warqueenscan/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/madara/warqueenscan/src/WarQueenScan.kt b/multisrc/overrides/madara/warqueenscan/src/WarQueenScan.kt deleted file mode 100644 index 3c485e0d54..0000000000 --- a/multisrc/overrides/madara/warqueenscan/src/WarQueenScan.kt +++ /dev/null @@ -1,23 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.warqueenscan - -import eu.kanade.tachiyomi.multisrc.madara.Madara -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class WarQueenScan : Madara( - "War Queen Scan", - "https://wqscan.com", - "pt-BR", - SimpleDateFormat("yyyy-MM-dd", Locale("pt", "BR")), -) { - - override val client: OkHttpClient = super.client.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .writeTimeout(1, TimeUnit.MINUTES) - .build() -} diff --git a/multisrc/overrides/madara/webdexscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/webdexscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..2eeb1cb6da Binary files /dev/null and b/multisrc/overrides/madara/webdexscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/webdexscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/webdexscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..82d9ae249d Binary files /dev/null and b/multisrc/overrides/madara/webdexscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/webdexscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/webdexscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..d29474fad4 Binary files /dev/null and b/multisrc/overrides/madara/webdexscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/webdexscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/webdexscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..dc4cc9c2ff Binary files /dev/null and b/multisrc/overrides/madara/webdexscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/webdexscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/webdexscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b20b7dfc48 Binary files /dev/null and b/multisrc/overrides/madara/webdexscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/webdexscans/res/web_hi_res_512.png b/multisrc/overrides/madara/webdexscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..92757565e2 Binary files /dev/null and b/multisrc/overrides/madara/webdexscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/webdexscans/src/WebdexScans.kt b/multisrc/overrides/madara/webdexscans/src/WebdexScans.kt new file mode 100644 index 0000000000..76a5446485 --- /dev/null +++ b/multisrc/overrides/madara/webdexscans/src/WebdexScans.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.webdexscans + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class WebdexScans : Madara("Webdex Scans", "https://webdexscans.com", "en") { + override val useNewChapterEndpoint = true + override val mangaDetailsSelectorStatus = "div.summary-heading:contains(Status) + div.summary-content" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/webtooncity/src/WebtoonCity.kt b/multisrc/overrides/madara/webtooncity/src/WebtoonCity.kt new file mode 100644 index 0000000000..9418a5078b --- /dev/null +++ b/multisrc/overrides/madara/webtooncity/src/WebtoonCity.kt @@ -0,0 +1,10 @@ +package eu.kanade.tachiyomi.extension.en.webtooncity + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class WebtoonCity : Madara("Webtoon City", "https://webtooncity.com", "en") { + override val useNewChapterEndpoint = false + override val mangaSubString = "webtoon" + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/madara/wickedwitchscan/src/WickedWitchScan.kt b/multisrc/overrides/madara/wickedwitchscan/src/WickedWitchScan.kt new file mode 100644 index 0000000000..50e48664a1 --- /dev/null +++ b/multisrc/overrides/madara/wickedwitchscan/src/WickedWitchScan.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.pt.wickedwitchscan + +import eu.kanade.tachiyomi.multisrc.madara.Madara +import okhttp3.OkHttpClient +import java.text.SimpleDateFormat +import java.util.Locale + +class WickedWitchScan : Madara( + "Wicked Witch Scan", + "https://wickedwitchscan.com", + "pt-BR", + SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), +) { + + override val client: OkHttpClient = super.client.newBuilder() + .build() +} diff --git a/multisrc/overrides/madara/yanpfansub/src/YANPFansub.kt b/multisrc/overrides/madara/yanpfansub/src/YANPFansub.kt index 1963f693f3..6b468f1efd 100644 --- a/multisrc/overrides/madara/yanpfansub/src/YANPFansub.kt +++ b/multisrc/overrides/madara/yanpfansub/src/YANPFansub.kt @@ -20,7 +20,7 @@ class YANPFansub : Madara( // Scanlator changed the theme from WpMangaReader to Madara. override val versionId: Int = 2 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 2, TimeUnit.SECONDS) .addInterceptor(::checkPasswordProtectedIntercept) .build() diff --git a/multisrc/overrides/madara/yaoitoshokan/src/YaoiToshokan.kt b/multisrc/overrides/madara/yaoitoshokan/src/YaoiToshokan.kt index 5855a24f7a..cb76122dad 100644 --- a/multisrc/overrides/madara/yaoitoshokan/src/YaoiToshokan.kt +++ b/multisrc/overrides/madara/yaoitoshokan/src/YaoiToshokan.kt @@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.Page -import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request import org.jsoup.nodes.Document @@ -19,10 +18,7 @@ class YaoiToshokan : Madara( SimpleDateFormat("dd MMM yyyy", Locale("pt", "BR")), ) { - override fun headersBuilder(): Headers.Builder = super.headersBuilder() - .removeAll("User-Agent") - - override val client: OkHttpClient = network.client.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 2, TimeUnit.SECONDS) .build() diff --git a/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt b/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt index dc62d586a6..0163cc160e 100644 --- a/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt +++ b/multisrc/overrides/madara/yugenmangas/src/YugenMangas.kt @@ -1,9 +1,11 @@ package eu.kanade.tachiyomi.extension.pt.yugenmangas +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.lib.randomua.UserAgentType +import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.SChapter -import okhttp3.Headers import okhttp3.OkHttpClient import org.jsoup.nodes.Element import java.text.SimpleDateFormat @@ -17,14 +19,17 @@ class YugenMangas : Madara( SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), ) { - override val client: OkHttpClient = super.client.newBuilder() - .addInterceptor(uaIntercept) + override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .setRandomUserAgent( + UserAgentType.DESKTOP, + ) + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS) .build() - override fun headersBuilder(): Headers.Builder = Headers.Builder() + override fun headersBuilder() = super.headersBuilder() .add("Origin", baseUrl) - .add("Referer", "$baseUrl/") override val useNewChapterEndpoint: Boolean = true @@ -41,5 +46,5 @@ class YugenMangas : Madara( ) } - override val useRandomUserAgentByDefault: Boolean = true + override fun setupPreferenceScreen(screen: PreferenceScreen) { } } diff --git a/multisrc/overrides/madara/zinmangatop/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/madara/zinmangatop/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..97b1749cfe Binary files /dev/null and b/multisrc/overrides/madara/zinmangatop/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/zinmangatop/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/madara/zinmangatop/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..660e2fda74 Binary files /dev/null and b/multisrc/overrides/madara/zinmangatop/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/zinmangatop/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/madara/zinmangatop/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..12b451c468 Binary files /dev/null and b/multisrc/overrides/madara/zinmangatop/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/zinmangatop/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/madara/zinmangatop/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..677d119bce Binary files /dev/null and b/multisrc/overrides/madara/zinmangatop/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/zinmangatop/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/madara/zinmangatop/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ffc766fa0a Binary files /dev/null and b/multisrc/overrides/madara/zinmangatop/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/madara/zinmangatop/res/web_hi_res_512.png b/multisrc/overrides/madara/zinmangatop/res/web_hi_res_512.png new file mode 100644 index 0000000000..bdbde6818d Binary files /dev/null and b/multisrc/overrides/madara/zinmangatop/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/madara/zinmangatop/src/ZinMangaTop.kt b/multisrc/overrides/madara/zinmangatop/src/ZinMangaTop.kt new file mode 100644 index 0000000000..445a22ebe4 --- /dev/null +++ b/multisrc/overrides/madara/zinmangatop/src/ZinMangaTop.kt @@ -0,0 +1,9 @@ +package eu.kanade.tachiyomi.extension.en.zinmangatop + +import eu.kanade.tachiyomi.multisrc.madara.Madara + +class ZinMangaTop : Madara("ZinManga.top (unoriginal)", "https://zinmanga.top", "en") { + override val useNewChapterEndpoint = true + + override fun searchPage(page: Int): String = if (page == 1) "" else "page/$page/" +} diff --git a/multisrc/overrides/mangacatalog/readblackclovermangaonline/src/ReadBlackCloverMangaOnline.kt b/multisrc/overrides/mangacatalog/readblackclovermangaonline/src/ReadBlackCloverMangaOnline.kt new file mode 100644 index 0000000000..1efd430498 --- /dev/null +++ b/multisrc/overrides/mangacatalog/readblackclovermangaonline/src/ReadBlackCloverMangaOnline.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.en.readblackclovermangaonline + +import eu.kanade.tachiyomi.multisrc.mangacatalog.MangaCatalog + +class ReadBlackCloverMangaOnline : MangaCatalog("Read Black Clover Manga Online", "https://ww7.readblackclover.com", "en") { + override val sourceList = listOf( + Pair("Black Clover", "$baseUrl/manga/black-clover"), + Pair("Black Clover Gainden Quartet Knights", "$baseUrl/manga/black-clover-gaiden-quartet-knights"), + Pair("Fan Colored", "$baseUrl/manga/black-clover-colored"), + Pair("Hungry Joker", "$baseUrl/manga/hungry-joker"), + ) +} diff --git a/multisrc/overrides/mangadventure/assortedscans/AndroidManifest.xml b/multisrc/overrides/mangadventure/assortedscans/AndroidManifest.xml index 227102ee96..0a89d8d68b 100644 --- a/multisrc/overrides/mangadventure/assortedscans/AndroidManifest.xml +++ b/multisrc/overrides/mangadventure/assortedscans/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - + element.text() } - .toMutableSet() - - manga.genre = genres.toList().joinToString(", ") - - return manga - } - - override fun Document.getSanitizedDetails(): Element = this - override fun chapterListSelector() = ".chapter-l a" - override fun String.sanitizeChapter() = substringAfterLast(" – ").substringBeforeLast("漫画") - - override fun pageSelector(): Evaluator { - return Evaluator.Tag("img") - } - - override fun pageListParse(document: Document): List { - val position = 32 - val parser = ImageListParser(document.html(), position) - - return parser.getImageList().orEmpty().mapIndexed { i, imageUrl -> - Page(i, imageUrl = imageUrl) - } - } -} diff --git a/multisrc/overrides/mangasar/default/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangasar/default/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 5f3c83a4c9..0000000000 Binary files a/multisrc/overrides/mangasar/default/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangasar/default/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangasar/default/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index aa7edff855..0000000000 Binary files a/multisrc/overrides/mangasar/default/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangasar/default/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangasar/default/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 0dfe9e0759..0000000000 Binary files a/multisrc/overrides/mangasar/default/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangasar/default/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangasar/default/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 1690d709ea..0000000000 Binary files a/multisrc/overrides/mangasar/default/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangasar/default/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangasar/default/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 39f7dda6ac..0000000000 Binary files a/multisrc/overrides/mangasar/default/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangasar/default/res/web_hi_res_512.png b/multisrc/overrides/mangasar/default/res/web_hi_res_512.png deleted file mode 100644 index 5b26d20e55..0000000000 Binary files a/multisrc/overrides/mangasar/default/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mangasar/mangasup/src/MangasUp.kt b/multisrc/overrides/mangasar/mangasup/src/MangasUp.kt deleted file mode 100644 index ff4addb2db..0000000000 --- a/multisrc/overrides/mangasar/mangasup/src/MangasUp.kt +++ /dev/null @@ -1,32 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.mangasup - -import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Element - -class MangasUp : MangaSar( - "MangásUp", - "https://mangasup.net", - "pt-BR", -) { - - override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { - return GET(baseUrl + mangaUrl, headers) - } - - override fun chapterListParse(response: Response): List { - return response.asJsoup() - .select("ul.full-chapters-list > li > a") - .map(::chapterFromElement) - } - - private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - name = element.selectFirst("span.cap-text")!!.text() - date_upload = element.selectFirst("span.chapter-date")?.text()?.toDate() ?: 0L - setUrlWithoutDomain(element.attr("href")) - } -} diff --git a/multisrc/overrides/mangasar/mangazim/src/Mangazim.kt b/multisrc/overrides/mangasar/mangazim/src/Mangazim.kt deleted file mode 100644 index ca0299f24a..0000000000 --- a/multisrc/overrides/mangasar/mangazim/src/Mangazim.kt +++ /dev/null @@ -1,28 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.mangazim - -import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Element - -class Mangazim : MangaSar("Mangazim", "https://mangazim.com", "pt-BR") { - - override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { - return GET(baseUrl + mangaUrl, headers) - } - - override fun chapterListParse(response: Response): List { - return response.asJsoup() - .select("ul.full-chapters-list > li > a") - .map(::chapterFromElement) - } - - private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - name = element.selectFirst("span.cap-text")!!.text() - date_upload = element.selectFirst("span.chapter-date")?.text()?.toDate() ?: 0L - setUrlWithoutDomain(element.attr("href")) - } -} diff --git a/multisrc/overrides/mangasar/seemangas/src/Seemangas.kt b/multisrc/overrides/mangasar/seemangas/src/Seemangas.kt deleted file mode 100644 index 246e3820f7..0000000000 --- a/multisrc/overrides/mangasar/seemangas/src/Seemangas.kt +++ /dev/null @@ -1,136 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.seemangas - -import eu.kanade.tachiyomi.multisrc.mangasar.MangaSar -import eu.kanade.tachiyomi.multisrc.mangasar.MangaSarLatestDto -import eu.kanade.tachiyomi.multisrc.mangasar.MangaSarReaderDto -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import okhttp3.FormBody -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Element - -class Seemangas : MangaSar( - "Seemangas", - "https://seemangas.com", - "pt-BR", -) { - - override fun popularMangaSelector() = "ul.sidebar-popular li.popular-treending" - - override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.selectFirst("h4.title")!!.text() - thumbnail_url = element.selectFirst("div.tumbl img")!!.attr("data-lazy-src") - setUrlWithoutDomain(element.selectFirst("a")!!.attr("abs:href")) - } - - override fun latestUpdatesRequest(page: Int): Request { - val payload = FormBody.Builder() - .add("action", "get_lancamentos") - .add("pagina", page.toString()) - .build() - - val newHeaders = headersBuilder() - .add("Content-Length", payload.contentLength().toString()) - .add("Content-Type", payload.contentType().toString()) - .add("X-Requested-With", "XMLHttpRequest") - .build() - - return POST("$baseUrl/wp-admin/admin-ajax.php", newHeaders, payload) - } - - override fun latestUpdatesParse(response: Response): MangasPage { - val result = response.parseAs() - - val latestMangas = result.releases - .map(::latestUpdatesFromObject) - .distinctBy { it.url } - - return MangasPage(latestMangas, hasNextPage = result.releases.isNotEmpty()) - } - - override fun mangaDetailsParse(response: Response): SManga { - val document = response.asJsoup() - val infoElement = document.selectFirst("div.box-single:has(div.mangapage)")!! - - return SManga.create().apply { - title = infoElement.selectFirst("h1.kw-title")!!.text() - author = infoElement.selectFirst("div.mdq.author")!!.text().trim() - description = infoElement.selectFirst("div.sinopse-page")!!.text() - genre = infoElement.select("div.generos a.widget-btn")!!.joinToString { it.text() } - status = infoElement.selectFirst("span.mdq")!!.text().toStatus() - thumbnail_url = infoElement.selectFirst("div.thumb img")!!.attr("abs:data-lazy-src") - } - } - override fun chapterListPaginatedRequest(mangaUrl: String, page: Int): Request { - return GET(baseUrl + mangaUrl, headers) - } - - override fun chapterListParse(response: Response): List { - return response.asJsoup() - .select("ul.full-chapters-list > li > a") - .map(::chapterFromElement) - } - - private fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - name = element.selectFirst("span.cap-text")!!.text() - date_upload = element.selectFirst("span.chapter-date")?.text()?.toDate() ?: 0L - setUrlWithoutDomain(element.attr("href")) - } - - override fun pageListApiRequest(chapterUrl: String, serieId: String, token: String): Request { - val chapterId = CHAPTER_ID_REGEX.find(chapterUrl)!!.groupValues[1] - - val payload = FormBody.Builder() - .add("action", "get_image_list") - .add("id_serie", chapterId) - .add("secury", token) - .build() - - val newHeaders = apiHeadersBuilder() - .add("Content-Length", payload.contentLength().toString()) - .add("Content-Type", payload.contentType().toString()) - .set("Referer", chapterUrl) - .build() - - return POST("$baseUrl/wp-admin/admin-ajax.php", newHeaders, payload) - } - - override fun pageListParse(response: Response): List { - val document = response.asJsoup() - val apiParams = document.selectFirst("script:containsData(id_serie)")?.data() - ?: throw Exception(TOKEN_NOT_FOUND) - - val chapterUrl = response.request.url.toString() - val infoReader = apiParams - .substringAfter("{") - .substringBeforeLast("}") - val readerParams = json.parseToJsonElement("{$infoReader}").jsonObject - val serieId = readerParams["id_serie"]!!.jsonPrimitive.content - val token = readerParams["token"]!!.jsonPrimitive.content - - val apiRequest = pageListApiRequest(chapterUrl, serieId, token) - val apiResponse = client.newCall(apiRequest).execute().parseAs() - - return apiResponse.images - .filter { it.url.startsWith("http") } - .mapIndexed { i, page -> Page(i, chapterUrl, page.url) } - } - - private fun String.toStatus(): Int = when (this) { - "Em andamento" -> SManga.ONGOING - "Completo" -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - - companion object { - private val CHAPTER_ID_REGEX = "(\\d+)$".toRegex() - } -} diff --git a/multisrc/overrides/mangathemesia/arcanescan/src/Arcanescan.kt b/multisrc/overrides/mangathemesia/arcanescan/src/Arcanescan.kt deleted file mode 100644 index 835f254408..0000000000 --- a/multisrc/overrides/mangathemesia/arcanescan/src/Arcanescan.kt +++ /dev/null @@ -1,12 +0,0 @@ -package eu.kanade.tachiyomi.extension.fr.arcanescan - -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import java.text.SimpleDateFormat -import java.util.Locale - -class Arcanescan : MangaThemesia( - "Arcane scan", - "https://arcanescan.fr", - "fr", - dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale.FRANCE), -) diff --git a/multisrc/overrides/mangathemesia/areamanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..7df6a260f8 Binary files /dev/null and b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/areamanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..cfb55ab6ce Binary files /dev/null and b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..dac425a959 Binary files /dev/null and b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..c1aae1158e Binary files /dev/null and b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..378c39bdfa Binary files /dev/null and b/multisrc/overrides/mangathemesia/areamanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/areamanga/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/areamanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..83b42018cd Binary files /dev/null and b/multisrc/overrides/mangathemesia/areamanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/areamanga/src/AreaManga.kt b/multisrc/overrides/mangathemesia/areamanga/src/AreaManga.kt new file mode 100644 index 0000000000..ea18cb04a7 --- /dev/null +++ b/multisrc/overrides/mangathemesia/areamanga/src/AreaManga.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.extension.ar.areamanga + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.source.model.SManga +import java.text.SimpleDateFormat +import java.util.Locale + +class AreaManga : MangaThemesia( + "أريا مانجا", + "https://www.areascans.net", + "ar", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), +) { + override val seriesArtistSelector = + ".tsinfo .imptdt:contains(الرسام) i, ${super.seriesArtistSelector}" + override val seriesAuthorSelector = + ".tsinfo .imptdt:contains(المؤلف) i, ${super.seriesAuthorSelector}" + override val seriesStatusSelector = + ".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}" + override val seriesTypeSelector = + ".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}" + + override fun String?.parseStatus() = when { + this == null -> SManga.UNKNOWN + this.contains("مستمر", ignoreCase = true) -> SManga.ONGOING + this.contains("مكتمل", ignoreCase = true) -> SManga.COMPLETED + this.contains("متوقف", ignoreCase = true) -> SManga.ON_HIATUS + else -> SManga.UNKNOWN + } +} diff --git a/multisrc/overrides/mangathemesia/arkhamscan/src/ArkhamScan.kt b/multisrc/overrides/mangathemesia/arkhamscan/src/ArkhamScan.kt new file mode 100644 index 0000000000..3cb5038910 --- /dev/null +++ b/multisrc/overrides/mangathemesia/arkhamscan/src/ArkhamScan.kt @@ -0,0 +1,22 @@ +package eu.kanade.tachiyomi.extension.pt.arkhamscan + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.OkHttpClient +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class ArkhamScan : MangaThemesia( + "Arkham Scan", + "https://arkhamscan.com", + "pt-BR", + dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), +) { + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() + + override val altNamePrefix = "Nomes alternativos: " +} diff --git a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt index c9b473d19f..b3f775fa33 100644 --- a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt +++ b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansEn.kt @@ -28,18 +28,15 @@ import java.util.concurrent.TimeUnit class AsuraScansEn : MangaThemesia( "Asura Scans", - "https://www.asurascans.com", + "https://asuracomics.com", "en", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale.US), ) { private val preferences = Injekt.get().getSharedPreferences("source_$id", 0x0000) - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .addInterceptor(::urlChangeInterceptor) - .addInterceptor(uaIntercept) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS) .build() @@ -232,7 +229,7 @@ class AsuraScansEn : MangaThemesia( setDefaultValue(true) }.also(screen::addPreference) - addRandomAndCustomUserAgentPreferences(screen) + super.setupPreferenceScreen(screen) } private val SharedPreferences.permaUrlPref diff --git a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt index 433569bf33..6a0365439c 100644 --- a/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt +++ b/multisrc/overrides/mangathemesia/asurascans/src/AsuraScansTr.kt @@ -19,10 +19,7 @@ class AsuraScansTr : MangaThemesia( "tr", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")), ) { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(uaIntercept) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 3, TimeUnit.SECONDS) .build() diff --git a/multisrc/overrides/mangathemesia/beastscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..45a794664b Binary files /dev/null and b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/beastscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3fe369faa3 Binary files /dev/null and b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..0ff76471d7 Binary files /dev/null and b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..dbd6ebc111 Binary files /dev/null and b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..cd8f8e96a7 Binary files /dev/null and b/multisrc/overrides/mangathemesia/beastscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/beastscans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/beastscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..b90ad2e820 Binary files /dev/null and b/multisrc/overrides/mangathemesia/beastscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/beastscans/src/BeastScans.kt b/multisrc/overrides/mangathemesia/beastscans/src/BeastScans.kt new file mode 100644 index 0000000000..287b328350 --- /dev/null +++ b/multisrc/overrides/mangathemesia/beastscans/src/BeastScans.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.extension.ar.beastscans + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.source.model.SManga +import java.text.SimpleDateFormat +import java.util.Locale + +class BeastScans : MangaThemesia( + "Beast Scans", + "https://beast-scans.com", + "ar", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), +) { + override val seriesArtistSelector = + ".tsinfo .imptdt:contains(الرسام) i, ${super.seriesArtistSelector}" + override val seriesAuthorSelector = + ".tsinfo .imptdt:contains(المؤلف) i, ${super.seriesAuthorSelector}" + override val seriesStatusSelector = + ".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}" + override val seriesTypeSelector = + ".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}" + + override fun String?.parseStatus() = when { + this == null -> SManga.UNKNOWN + this.contains("مستمر", ignoreCase = true) -> SManga.ONGOING + this.contains("مكتمل", ignoreCase = true) -> SManga.COMPLETED + this.contains("متوقف", ignoreCase = true) -> SManga.ON_HIATUS + else -> SManga.UNKNOWN + } +} diff --git a/multisrc/overrides/mangathemesia/boosei/src/Boosei.kt b/multisrc/overrides/mangathemesia/boosei/src/Boosei.kt index 20a9a49895..33d0a61690 100644 --- a/multisrc/overrides/mangathemesia/boosei/src/Boosei.kt +++ b/multisrc/overrides/mangathemesia/boosei/src/Boosei.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.boosei import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class Boosei : MangaThemesia("Boosei", "https://boosei.net", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/clayrer/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index c1c4b906df..0000000000 Binary files a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/clayrer/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index de64ce5990..0000000000 Binary files a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 5d7ec338b3..0000000000 Binary files a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 33a3df730c..0000000000 Binary files a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 6be55bbd06..0000000000 Binary files a/multisrc/overrides/mangathemesia/clayrer/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/clayrer/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/clayrer/res/web_hi_res_512.png deleted file mode 100644 index 9ab90fa0d7..0000000000 Binary files a/multisrc/overrides/mangathemesia/clayrer/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/clayrer/src/Clayrer.kt b/multisrc/overrides/mangathemesia/clayrer/src/Clayrer.kt deleted file mode 100644 index 033cec99ec..0000000000 --- a/multisrc/overrides/mangathemesia/clayrer/src/Clayrer.kt +++ /dev/null @@ -1,7 +0,0 @@ -package eu.kanade.tachiyomi.extension.es.clayrer - -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import java.text.SimpleDateFormat -import java.util.Locale - -class Clayrer : MangaThemesia("Clayrer", "https://clayrer.net", "es", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("es"))) diff --git a/multisrc/overrides/mangathemesia/constellarscans/additional.gradle b/multisrc/overrides/mangathemesia/constellarscans/additional.gradle deleted file mode 100644 index 2505114b36..0000000000 --- a/multisrc/overrides/mangathemesia/constellarscans/additional.gradle +++ /dev/null @@ -1,3 +0,0 @@ -dependencies { - implementation(project(':lib-dataimage')) -} \ No newline at end of file diff --git a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 37a749336d..0000000000 Binary files a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 910e4e5359..0000000000 Binary files a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index a6e9204965..0000000000 Binary files a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 85c05d0670..0000000000 Binary files a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index d9eaf09202..0000000000 Binary files a/multisrc/overrides/mangathemesia/constellarscans/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/constellarscans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/constellarscans/res/web_hi_res_512.png deleted file mode 100644 index a8dd214cc9..0000000000 Binary files a/multisrc/overrides/mangathemesia/constellarscans/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/constellarscans/src/ConstellarScans.kt b/multisrc/overrides/mangathemesia/constellarscans/src/ConstellarScans.kt deleted file mode 100644 index 7e96a214da..0000000000 --- a/multisrc/overrides/mangathemesia/constellarscans/src/ConstellarScans.kt +++ /dev/null @@ -1,157 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.constellarscans - -import android.annotation.SuppressLint -import android.app.Application -import android.os.Handler -import android.os.Looper -import android.util.Log -import android.view.View -import android.webkit.ConsoleMessage -import android.webkit.JavascriptInterface -import android.webkit.WebChromeClient -import android.webkit.WebView -import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import okhttp3.CacheControl -import okhttp3.Headers -import okhttp3.Request -import org.jsoup.nodes.Document -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import java.util.concurrent.CountDownLatch - -class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarscans.com", "en") { - - override val client = super.client.newBuilder() - .addInterceptor(DataImageInterceptor()) - .rateLimit(1, 3) - .build() - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("Referer", "$baseUrl/") - .add("Accept-Language", "en-US,en;q=0.9") - .add("DNT", "1") - .add("User-Agent", mobileUserAgent) - .add("Upgrade-Insecure-Requests", "1") - - override val seriesStatusSelector = ".status" - - private val mobileUserAgent by lazy { - val req = GET(UA_DB_URL) - val data = client.newCall(req).execute().body.use { - json.parseToJsonElement(it.string()).jsonArray - }.mapNotNull { - it.jsonObject["user-agent"]?.jsonPrimitive?.content?.takeIf { ua -> - ua.startsWith("Mozilla/5.0") && - ( - ua.contains("iPhone") && - (ua.contains("FxiOS") || ua.contains("CriOS")) || - ua.contains("Android") && - (ua.contains("EdgA") || ua.contains("Chrome") || ua.contains("Firefox")) - ) - } - } - data.random() - } - - override fun pageListRequest(chapter: SChapter): Request = - super.pageListRequest(chapter).newBuilder() - .header( - "Accept", - "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", - ) - .header("Sec-Fetch-Site", "same-origin") - .header("Sec-Fetch-Mode", "navigate") - .header("Sec-Fetch-Dest", "document") - .header("Sec-Fetch-User", "?1") - .cacheControl(CacheControl.FORCE_NETWORK) - .build() - - internal class JsObject(val imageList: MutableList = mutableListOf()) { - @JavascriptInterface - fun passSingleImage(url: String) { - Log.d("constellarscans", "received image: $url") - imageList.add(url) - } - } - - private fun randomString(length: Int = 10): String { - val charPool = ('a'..'z') + ('A'..'Z') - return List(length) { charPool.random() }.joinToString("") - } - - private val funkyScript by lazy { - client.newCall(GET(FUNKY_SCRIPT_URL)).execute().body.string() - } - - @SuppressLint("SetJavaScriptEnabled") - override fun pageListParse(document: Document): List { - val interfaceName = randomString() - document.body().prepend("") - - val handler = Handler(Looper.getMainLooper()) - val latch = CountDownLatch(1) - val jsInterface = JsObject() - var webView: WebView? = null - handler.post { - val webview = WebView(Injekt.get()) - webView = webview - webview.settings.javaScriptEnabled = true - webview.settings.domStorageEnabled = true - webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null) - webview.settings.useWideViewPort = false - webview.settings.loadWithOverviewMode = false - webview.settings.userAgentString = mobileUserAgent - webview.addJavascriptInterface(jsInterface, interfaceName) - - webview.webChromeClient = object : WebChromeClient() { - override fun onProgressChanged(view: WebView?, newProgress: Int) { - if (newProgress == 100) { - latch.countDown() - } - } - - override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean { - if (consoleMessage == null) { return false } - val logContent = "wv: ${consoleMessage.message()} ${consoleMessage.sourceId()}, line ${consoleMessage.lineNumber()}" - when (consoleMessage.messageLevel()) { - ConsoleMessage.MessageLevel.DEBUG -> Log.d("constellarscans", logContent) - ConsoleMessage.MessageLevel.ERROR -> Log.e("constellarscans", logContent) - ConsoleMessage.MessageLevel.LOG -> Log.i("constellarscans", logContent) - ConsoleMessage.MessageLevel.TIP -> Log.i("constellarscans", logContent) - ConsoleMessage.MessageLevel.WARNING -> Log.w("constellarscans", logContent) - else -> Log.d("constellarscans", logContent) - } - - return true - } - } - Log.d("constellarscans", "starting webview shenanigans") - webview.loadDataWithBaseURL(baseUrl, document.toString(), "text/html", "UTF-8", null) - } - - latch.await() - handler.post { webView?.destroy() } - return jsInterface.imageList.mapIndexed { idx, it -> Page(idx, imageUrl = it) } - } - - override fun imageRequest(page: Page): Request = super.imageRequest(page).newBuilder() - .header("Accept", "image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8") - .header("Sec-Fetch-Dest", "image") - .header("Sec-Fetch-Mode", "no-cors") - .header("Sec-Fetch-Site", "same-origin") - .build() - - companion object { - const val UA_DB_URL = - "https://cdn.jsdelivr.net/gh/mimmi20/browscap-helper@30a83c095688f40b9eaca0165a479c661e5a7fbe/tests/0002999.json" - val FUNKY_SCRIPT_URL = "https://cdn.jsdelivr.net/npm/@beerpsi/funky-script@latest/constellar.js" - } -} diff --git a/multisrc/overrides/mangathemesia/default/AndroidManifest.xml b/multisrc/overrides/mangathemesia/default/AndroidManifest.xml index ded43d296e..3db1038222 100644 --- a/multisrc/overrides/mangathemesia/default/AndroidManifest.xml +++ b/multisrc/overrides/mangathemesia/default/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - \ No newline at end of file + diff --git a/multisrc/overrides/mangathemesia/default/additional.gradle b/multisrc/overrides/mangathemesia/default/additional.gradle new file mode 100644 index 0000000000..57807a7d07 --- /dev/null +++ b/multisrc/overrides/mangathemesia/default/additional.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation(project(":lib-randomua")) +} diff --git a/multisrc/overrides/mangathemesia/dojingnet/src/DojingNet.kt b/multisrc/overrides/mangathemesia/dojingnet/src/DojingNet.kt index bd326bbf52..60e0003152 100644 --- a/multisrc/overrides/mangathemesia/dojingnet/src/DojingNet.kt +++ b/multisrc/overrides/mangathemesia/dojingnet/src/DojingNet.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.dojingnet import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class DojingNet : MangaThemesia("Dojing.net", "https://dojing.net", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 144f3530d7..0000000000 Binary files a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index d30f74496d..0000000000 Binary files a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 7e25b185ab..0000000000 Binary files a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 70a89d4607..0000000000 Binary files a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index d4ec8796b1..0000000000 Binary files a/multisrc/overrides/mangathemesia/dragontranslation/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/dragontranslation/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/dragontranslation/res/web_hi_res_512.png deleted file mode 100644 index b39ae07d2e..0000000000 Binary files a/multisrc/overrides/mangathemesia/dragontranslation/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/dragontranslation/src/DragonTranslation.kt b/multisrc/overrides/mangathemesia/dragontranslation/src/DragonTranslation.kt deleted file mode 100644 index 1f80427371..0000000000 --- a/multisrc/overrides/mangathemesia/dragontranslation/src/DragonTranslation.kt +++ /dev/null @@ -1,13 +0,0 @@ -package eu.kanade.tachiyomi.extension.es.dragontranslation - -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import java.text.SimpleDateFormat -import java.util.Locale - -class DragonTranslation : MangaThemesia( - "DragonTranslation", - "https://dragontranslation.com", - "es", - mangaUrlDirectory = "/manga", - dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("en")), -) diff --git a/multisrc/overrides/mangathemesia/duniakomikid/src/DuniaKomikId.kt b/multisrc/overrides/mangathemesia/duniakomikid/src/DuniaKomikId.kt index 24352c5cf3..1c1bc531a5 100644 --- a/multisrc/overrides/mangathemesia/duniakomikid/src/DuniaKomikId.kt +++ b/multisrc/overrides/mangathemesia/duniakomikid/src/DuniaKomikId.kt @@ -1,8 +1,10 @@ package eu.kanade.tachiyomi.extension.id.duniakomikid import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale -class DuniaKomikId : MangaThemesia("DuniaKomik.id", "https://duniakomik.id", "id") { +class DuniaKomikId : MangaThemesia("DuniaKomik.id", "https://duniakomik.org", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id", "ID"))) { override val hasProjectPage = true } diff --git a/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..47370da2ff Binary files /dev/null and b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..e7091505f2 Binary files /dev/null and b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..ca9fb349ba Binary files /dev/null and b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..f1fc6417ea Binary files /dev/null and b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..e2ab65b8cb Binary files /dev/null and b/multisrc/overrides/mangathemesia/enryumanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/enryumanga/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/enryumanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..e966355906 Binary files /dev/null and b/multisrc/overrides/mangathemesia/enryumanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/enryumanga/src/EnryuManga.kt b/multisrc/overrides/mangathemesia/enryumanga/src/EnryuManga.kt new file mode 100644 index 0000000000..d43a904961 --- /dev/null +++ b/multisrc/overrides/mangathemesia/enryumanga/src/EnryuManga.kt @@ -0,0 +1,5 @@ +package eu.kanade.tachiyomi.extension.en.enryumanga + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia + +class EnryuManga : MangaThemesia("EnryuManga", "https://enryumanga.com", "en") diff --git a/multisrc/overrides/mangathemesia/flamescans/src/FlameScans.kt b/multisrc/overrides/mangathemesia/flamescans/src/FlameScans.kt index a36616de87..50e78b8aab 100644 --- a/multisrc/overrides/mangathemesia/flamescans/src/FlameScans.kt +++ b/multisrc/overrides/mangathemesia/flamescans/src/FlameScans.kt @@ -28,7 +28,6 @@ import uy.kohesive.injekt.api.get import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit open class FlameScans( override val baseUrl: String, @@ -48,9 +47,7 @@ open class FlameScans( Injekt.get().getSharedPreferences("source_$id", 0x0000) } - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .addInterceptor(::composedImageIntercept) .build() diff --git a/multisrc/overrides/mangathemesia/franxxmangas/src/FranxxMangas.kt b/multisrc/overrides/mangathemesia/franxxmangas/src/FranxxMangas.kt index 1c5733419b..1baec64a98 100644 --- a/multisrc/overrides/mangathemesia/franxxmangas/src/FranxxMangas.kt +++ b/multisrc/overrides/mangathemesia/franxxmangas/src/FranxxMangas.kt @@ -14,7 +14,7 @@ class FranxxMangas : MangaThemesia( dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), ) { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 2, TimeUnit.SECONDS) .build() diff --git a/multisrc/overrides/mangathemesia/kiryuu/src/Kiryuu.kt b/multisrc/overrides/mangathemesia/kiryuu/src/Kiryuu.kt index 74d2c613a4..1d5df8691d 100644 --- a/multisrc/overrides/mangathemesia/kiryuu/src/Kiryuu.kt +++ b/multisrc/overrides/mangathemesia/kiryuu/src/Kiryuu.kt @@ -6,15 +6,12 @@ import okhttp3.OkHttpClient import org.jsoup.nodes.Document import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit class Kiryuu : MangaThemesia("Kiryuu", "https://kiryuu.id", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id"))) { // Formerly "Kiryuu (WP Manga Stream)" override val id = 3639673976007021338 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/komikav/src/KomikAV.kt b/multisrc/overrides/mangathemesia/komikav/src/KomikAV.kt index c2c5770367..1802c33989 100644 --- a/multisrc/overrides/mangathemesia/komikav/src/KomikAV.kt +++ b/multisrc/overrides/mangathemesia/komikav/src/KomikAV.kt @@ -8,7 +8,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit class KomikAV : MangaThemesia( "Komik AV (WP Manga Stream)", @@ -19,9 +18,7 @@ class KomikAV : MangaThemesia( // Formerly "Komik AV (WP Manga Stream)" override val id = 7875815514004535629 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/komikcast/src/KomikCast.kt b/multisrc/overrides/mangathemesia/komikcast/src/KomikCast.kt index 7a0fbdd217..04d4b39099 100644 --- a/multisrc/overrides/mangathemesia/komikcast/src/KomikCast.kt +++ b/multisrc/overrides/mangathemesia/komikcast/src/KomikCast.kt @@ -17,7 +17,6 @@ import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element import java.util.Calendar -import java.util.concurrent.TimeUnit class KomikCast : MangaThemesia( "Komik Cast", @@ -28,13 +27,11 @@ class KomikCast : MangaThemesia( // Formerly "Komik Cast (WP Manga Stream)" override val id = 972717448578983812 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(3) .build() - override fun headersBuilder(): Headers.Builder = Headers.Builder() + override fun headersBuilder(): Headers.Builder = super.headersBuilder() .add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9") .add("Accept-language", "en-US,en;q=0.9,id;q=0.8") .add("Referer", baseUrl) @@ -76,7 +73,7 @@ class KomikCast : MangaThemesia( override fun chapterFromElement(element: Element) = SChapter.create().apply { val urlElements = element.select("a") setUrlWithoutDomain(urlElements.attr("href")) - name = element.select(".lch a, .chapternum")!!.text().ifBlank { urlElements.first()!!.text() } + name = element.select(".chapter-link-item").text() date_upload = parseChapterDate2(element.select(".chapter-link-time").text()) } diff --git a/multisrc/overrides/mangathemesia/komikdewasa/src/KomikDewasa.kt b/multisrc/overrides/mangathemesia/komikdewasa/src/KomikDewasa.kt index 98380340de..75b799fddb 100644 --- a/multisrc/overrides/mangathemesia/komikdewasa/src/KomikDewasa.kt +++ b/multisrc/overrides/mangathemesia/komikdewasa/src/KomikDewasa.kt @@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.komikdewasa import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class KomikDewasa : MangaThemesia("KomikDewasa", "https://komikdewasa.org", "id", "/komik") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/komikindoco/src/KomikindoCo.kt b/multisrc/overrides/mangathemesia/komikindoco/src/KomikindoCo.kt index 9a6c96240e..1744757e7e 100644 --- a/multisrc/overrides/mangathemesia/komikindoco/src/KomikindoCo.kt +++ b/multisrc/overrides/mangathemesia/komikindoco/src/KomikindoCo.kt @@ -5,15 +5,12 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit class KomikindoCo : MangaThemesia("KomikIndo.co", "https://komikindo.co", "id", dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale("id"))) { // Formerly "Komikindo.co" override val id = 734619124437406170 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/komikmanhwa/src/KomikManhwa.kt b/multisrc/overrides/mangathemesia/komikmanhwa/src/KomikManhwa.kt index 65105ec823..bfd0fc6e17 100644 --- a/multisrc/overrides/mangathemesia/komikmanhwa/src/KomikManhwa.kt +++ b/multisrc/overrides/mangathemesia/komikmanhwa/src/KomikManhwa.kt @@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.komikmanhwa import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class KomikManhwa : MangaThemesia("KomikManhwa", "https://komikmanhwa.me", "id", "/series") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/komiksan/src/Komiksan.kt b/multisrc/overrides/mangathemesia/komiksan/src/Komiksan.kt new file mode 100644 index 0000000000..af81466498 --- /dev/null +++ b/multisrc/overrides/mangathemesia/komiksan/src/Komiksan.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.id.komiksan + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.OkHttpClient +import org.jsoup.nodes.Document + +class Komiksan : MangaThemesia("Komiksan", "https://komiksan.link", "id", "/list") { + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(4) + .build() + + override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply { + title = document.selectFirst(seriesThumbnailSelector)!!.attr("alt") + } +} diff --git a/multisrc/overrides/mangathemesia/komikstation/src/KomikStation.kt b/multisrc/overrides/mangathemesia/komikstation/src/KomikStation.kt index 51437f642a..44c0c82441 100644 --- a/multisrc/overrides/mangathemesia/komikstation/src/KomikStation.kt +++ b/multisrc/overrides/mangathemesia/komikstation/src/KomikStation.kt @@ -3,15 +3,12 @@ package eu.kanade.tachiyomi.extension.id.komikstation import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class KomikStation : MangaThemesia("Komik Station", "https://komikstation.co", "id") { // Formerly "Komik Station (WP Manga Stream)" override val id = 6148605743576635261 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/kumapoi/src/KumaPoi.kt b/multisrc/overrides/mangathemesia/kumapoi/src/KumaPoi.kt index 316d091b76..3ddba7cc50 100644 --- a/multisrc/overrides/mangathemesia/kumapoi/src/KumaPoi.kt +++ b/multisrc/overrides/mangathemesia/kumapoi/src/KumaPoi.kt @@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.kumapoi import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class KumaPoi : MangaThemesia("KumaPoi", "https://kumapoi.club", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/kumascans/src/KumaScans.kt b/multisrc/overrides/mangathemesia/kumascans/src/KumaScans.kt index 4a0bde5802..2c2bdf41ce 100644 --- a/multisrc/overrides/mangathemesia/kumascans/src/KumaScans.kt +++ b/multisrc/overrides/mangathemesia/kumascans/src/KumaScans.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.en.kumascans import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class KumaScans : MangaThemesia("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/madara/legionscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/legionscan/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/legionscan/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/legionscan/res/mipmap-hdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/legionscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/legionscan/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/legionscan/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/legionscan/res/mipmap-mdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/legionscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/legionscan/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/legionscan/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/legionscan/res/mipmap-xhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/legionscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/legionscan/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/legionscan/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/legionscan/res/mipmap-xxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/legionscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/legionscan/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/legionscan/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/legionscan/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/legionscan/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/legionscan/res/web_hi_res_512.png similarity index 100% rename from multisrc/overrides/madara/legionscan/res/web_hi_res_512.png rename to multisrc/overrides/mangathemesia/legionscan/res/web_hi_res_512.png diff --git a/multisrc/overrides/mangathemesia/legionscan/src/LegionScan.kt b/multisrc/overrides/mangathemesia/legionscan/src/LegionScan.kt new file mode 100644 index 0000000000..9c3afd6849 --- /dev/null +++ b/multisrc/overrides/mangathemesia/legionscan/src/LegionScan.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.es.legionscan + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale + +class LegionScan : MangaThemesia( + "Legion Scan", + "https://legionscans.com", + "es", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) { + // Theme changed from Madara to MangaThemesia + override val versionId = 2 + override val seriesAltNameSelector = ".infotable tr:contains(alt) td:last-child" +} diff --git a/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..f6b98f6f3e Binary files /dev/null and b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..114e338101 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..9b2d22544e Binary files /dev/null and b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d0c92d4d9a Binary files /dev/null and b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ffe56d3656 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lelmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lelmanga/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/lelmanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..e5a6e78529 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lelmanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/lelmanga/src/Lelmanga.kt b/multisrc/overrides/mangathemesia/lelmanga/src/Lelmanga.kt new file mode 100644 index 0000000000..7e0ca7deef --- /dev/null +++ b/multisrc/overrides/mangathemesia/lelmanga/src/Lelmanga.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.fr.lelmanga + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale + +class Lelmanga : MangaThemesia("Lelmanga", "https://lelmanga.com", "fr", dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)) { + override val altNamePrefix = "Nom alternatif: " + override val seriesAuthorSelector = ".imptdt:contains(Auteur) i" + override val seriesArtistSelector = ".imptdt:contains(Artiste) i" +} diff --git a/multisrc/overrides/mangathemesia/lianscans/src/LianScans.kt b/multisrc/overrides/mangathemesia/lianscans/src/LianScans.kt index 3b6a2a1198..7bdb98e228 100644 --- a/multisrc/overrides/mangathemesia/lianscans/src/LianScans.kt +++ b/multisrc/overrides/mangathemesia/lianscans/src/LianScans.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.lianscans import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class LianScans : MangaThemesia("LianScans", "https://www.lianscans.my.id", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..51faf999b2 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..50a5fb1e24 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..6f0cf0b3c4 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ea55342e70 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..bab441b497 Binary files /dev/null and b/multisrc/overrides/mangathemesia/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/lynxscans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/lynxscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..3a35efa06c Binary files /dev/null and b/multisrc/overrides/mangathemesia/lynxscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/lynxscans/src/LynxScans.kt b/multisrc/overrides/mangathemesia/lynxscans/src/LynxScans.kt new file mode 100644 index 0000000000..9fb2156a66 --- /dev/null +++ b/multisrc/overrides/mangathemesia/lynxscans/src/LynxScans.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.extension.en.lynxscans + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.Interceptor +import okhttp3.Response + +class LynxScans : MangaThemesia("LynxScans", "https://lynxscans.com", "en", "/comics") { + override val versionId = 3 + override val hasProjectPage = true + + override val client = super.client.newBuilder() + .addNetworkInterceptor(::pageSpeedRedirectIntercept) + .rateLimit(2) + .build() + + private fun pageSpeedRedirectIntercept(chain: Interceptor.Chain): Response { + val request = chain.request() + + if (request.url.pathSegments.contains("wp-content") || request.method == "POST") { + return chain.proceed(request) + } + + val newUrl = request.url.newBuilder() + .setQueryParameter("PageSpeed", "noscript") + .build() + + val newRequest = request.newBuilder() + .url(newUrl) + .build() + + return chain.proceed(newRequest) + } +} diff --git a/multisrc/overrides/mangathemesia/mangakyo/src/Mangakyo.kt b/multisrc/overrides/mangathemesia/mangakyo/src/Mangakyo.kt index 1bf4ea3d8c..8304a18a53 100644 --- a/multisrc/overrides/mangathemesia/mangakyo/src/Mangakyo.kt +++ b/multisrc/overrides/mangathemesia/mangakyo/src/Mangakyo.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.mangakyo import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit -class Mangakyo : MangaThemesia("Mangakyo", "https://mangakyo.id", "id") { +class Mangakyo : MangaThemesia("Mangakyo", "https://mangakyo.org", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/mangaraworg/src/MangaRawOrg.kt b/multisrc/overrides/mangathemesia/mangaraworg/src/MangaRawOrg.kt index 224359a25b..4cfed65b60 100644 --- a/multisrc/overrides/mangathemesia/mangaraworg/src/MangaRawOrg.kt +++ b/multisrc/overrides/mangathemesia/mangaraworg/src/MangaRawOrg.kt @@ -15,14 +15,11 @@ import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable -import java.util.concurrent.TimeUnit class MangaRawOrg : MangaThemesia("Manga Raw.org", "https://mangaraw.org", "ja") { override val id = 6223520752496636410 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/mangaschan/src/MangasChan.kt b/multisrc/overrides/mangathemesia/mangaschan/src/MangasChan.kt index ebbeed5989..76620649fc 100644 --- a/multisrc/overrides/mangathemesia/mangaschan/src/MangasChan.kt +++ b/multisrc/overrides/mangathemesia/mangaschan/src/MangasChan.kt @@ -7,17 +7,17 @@ import java.util.Locale class MangasChan : MangaThemesia( "Mangás Chan", - "https://mangaschan.com", + "https://mangaschan.net", "pt-BR", dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), ) { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .build() override val altNamePrefix = "Nomes alternativos: " - override val seriesArtistSelector = ".infotable tr:contains(Artista) td:last-child" - override val seriesAuthorSelector = ".infotable tr:contains(Autor) td:last-child" - override val seriesTypeSelector = ".infotable tr:contains(Tipo) td:last-child" + override val seriesArtistSelector = ".tsinfo .imptdt:contains(Artista) > i" + override val seriesAuthorSelector = ".tsinfo .imptdt:contains(Autor) > i" + override val seriesTypeSelector = ".tsinfo .imptdt:contains(Tipo) > a" } diff --git a/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..0bde288891 Binary files /dev/null and b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ce8507766c Binary files /dev/null and b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..dd2d028201 Binary files /dev/null and b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b710dfa1fc Binary files /dev/null and b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..d00e6a4a84 Binary files /dev/null and b/multisrc/overrides/mangathemesia/mangashiina/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/mangashiina/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/mangashiina/res/web_hi_res_512.png new file mode 100644 index 0000000000..8411e149d8 Binary files /dev/null and b/multisrc/overrides/mangathemesia/mangashiina/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/mangashiina/src/MangaShiina.kt b/multisrc/overrides/mangathemesia/mangashiina/src/MangaShiina.kt new file mode 100644 index 0000000000..87d66ff089 --- /dev/null +++ b/multisrc/overrides/mangathemesia/mangashiina/src/MangaShiina.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.es.mangashiina + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaShiina : MangaThemesia( + "MangaShiina", + "https://mangashiina.com", + "es", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) diff --git a/multisrc/overrides/mangathemesia/mangaswat/src/MangaSwat.kt b/multisrc/overrides/mangathemesia/mangaswat/src/MangaSwat.kt index 220816e7e4..1d0c83bbf4 100644 --- a/multisrc/overrides/mangathemesia/mangaswat/src/MangaSwat.kt +++ b/multisrc/overrides/mangathemesia/mangaswat/src/MangaSwat.kt @@ -7,12 +7,16 @@ import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.extension.BuildConfig import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import okhttp3.Headers import okhttp3.OkHttpClient +import okhttp3.Request import org.jsoup.nodes.Document +import org.jsoup.nodes.Element import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat @@ -20,11 +24,11 @@ import java.util.Locale class MangaSwat : MangaThemesia( "MangaSwat", - "https://swatmanga.me", + "https://stmgs.com", "ar", - dateFormat = SimpleDateFormat("yyyy-MM-dd", Locale.US), + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), ) { - private val defaultBaseUrl = "https://swatmanga.me" + private val defaultBaseUrl = "https://swatmanga.co" override val baseUrl by lazy { getPrefBaseUrl() } @@ -39,6 +43,23 @@ class MangaSwat : MangaThemesia( override fun headersBuilder(): Headers.Builder = super.headersBuilder() .add("Referer", "$baseUrl/") + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val request = super.searchMangaRequest(page, query, filters) + if (query.isBlank()) return request + + val url = request.url.newBuilder() + .removePathSegment(0) + .removeAllQueryParameters("title") + .addQueryParameter("s", query) + .build() + + return request.newBuilder() + .url(url) + .build() + } + + override fun searchMangaNextPageSelector() = "a[rel=next]" + override val seriesArtistSelector = "span:contains(الناشر) i" override val seriesAuthorSelector = "span:contains(المؤلف) i" override val seriesGenreSelector = "span:contains(التصنيف) a, .mgen a" @@ -55,6 +76,13 @@ class MangaSwat : MangaThemesia( override fun chapterListSelector() = "div.bxcl li, ul div:has(span.lchx)" + override fun chapterFromElement(element: Element) = SChapter.create().apply { + val urlElements = element.select("a") + setUrlWithoutDomain(urlElements.attr("href")) + name = element.select(".lch a, .chapternum").text().ifBlank { urlElements.last()!!.text() } + date_upload = element.selectFirst(".chapter-date")?.text().parseChapterDate() + } + @Serializable data class TSReader( val sources: List, diff --git a/multisrc/overrides/mangathemesia/mangayaro/src/Mangayaro.kt b/multisrc/overrides/mangathemesia/mangayaro/src/Mangayaro.kt index 34fd4e1586..c0bcefb4a6 100644 --- a/multisrc/overrides/mangathemesia/mangayaro/src/Mangayaro.kt +++ b/multisrc/overrides/mangathemesia/mangayaro/src/Mangayaro.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.mangayaro import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class Mangayaro : MangaThemesia("Mangayaro", "https://mangayaro.net", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/mangceh/src/Mareceh.kt b/multisrc/overrides/mangathemesia/mangceh/src/Mareceh.kt index 891845d19d..8af8604347 100644 --- a/multisrc/overrides/mangathemesia/mangceh/src/Mareceh.kt +++ b/multisrc/overrides/mangathemesia/mangceh/src/Mareceh.kt @@ -3,15 +3,12 @@ package eu.kanade.tachiyomi.extension.id.mangceh import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class Mareceh : MangaThemesia("Mareceh", "https://mareceh.com", "id") { override val versionId = 2 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/mangkomik/src/MangKomik.kt b/multisrc/overrides/mangathemesia/mangkomik/src/MangKomik.kt index 29181369d2..4f067ffbe4 100644 --- a/multisrc/overrides/mangathemesia/mangkomik/src/MangKomik.kt +++ b/multisrc/overrides/mangathemesia/mangkomik/src/MangKomik.kt @@ -10,7 +10,7 @@ class MangKomik : MangaThemesia("MangKomik", "https://mangkomik.net", "id") { override fun pageListParse(document: Document): List { // Get external JS for image urls - val scriptEl = document.selectFirst("script[data-minify]")!! + val scriptEl = document.selectFirst("script[data-minify]") val scriptUrl = scriptEl?.attr("src") if (scriptUrl.isNullOrEmpty()) { return super.pageListParse(document) diff --git a/multisrc/overrides/mangathemesia/manhwadesu/src/ManhwaDesu.kt b/multisrc/overrides/mangathemesia/manhwadesu/src/ManhwaDesu.kt index 56c0de2750..b6669bb501 100644 --- a/multisrc/overrides/mangathemesia/manhwadesu/src/ManhwaDesu.kt +++ b/multisrc/overrides/mangathemesia/manhwadesu/src/ManhwaDesu.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.manhwadesu import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class ManhwaDesu : MangaThemesia("ManhwaDesu", "https://manhwadesu.org", "id", "/komik") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/manhwafreak/src/ManhwaFreak.kt b/multisrc/overrides/mangathemesia/manhwafreak/src/ManhwaFreak.kt index 0ad450efca..e01785e6b5 100644 --- a/multisrc/overrides/mangathemesia/manhwafreak/src/ManhwaFreak.kt +++ b/multisrc/overrides/mangathemesia/manhwafreak/src/ManhwaFreak.kt @@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.model.SManga import okhttp3.Request import org.jsoup.nodes.Element -class ManhwaFreak : MangaThemesia("Manhwa Freak", "https://manhwafreak.com", "en") { +class ManhwaFreak : MangaThemesia("Manhwa Freak", "https://manhwa-freak.com", "en") { // they called the theme "mangareaderfix" @@ -51,7 +51,7 @@ class ManhwaFreak : MangaThemesia("Manhwa Freak", "https://manhwafreak.com", "en override fun chapterFromElement(element: Element) = SChapter.create().apply { val urlElements = element.select("a") setUrlWithoutDomain(urlElements.attr("href")) - name = element.select(".chapter-info p:nth-child(1)")!!.text().ifBlank { urlElements.first()!!.text() } + name = element.select(".chapter-info p:nth-child(1)").text().ifBlank { urlElements.first()!!.text() } date_upload = element.selectFirst(".chapter-info p:nth-child(2)")?.text().parseChapterDate() } diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..7284178864 Binary files /dev/null and b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..cb1acc5361 Binary files /dev/null and b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..142f451b07 Binary files /dev/null and b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..032e7759b8 Binary files /dev/null and b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..676f90eb69 Binary files /dev/null and b/multisrc/overrides/mangathemesia/manhwafreakfr/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/manhwafreakfr/res/web_hi_res_512.png new file mode 100644 index 0000000000..4d0f594988 Binary files /dev/null and b/multisrc/overrides/mangathemesia/manhwafreakfr/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/manhwafreakfr/src/ManhwaFreakFR.kt b/multisrc/overrides/mangathemesia/manhwafreakfr/src/ManhwaFreakFR.kt new file mode 100644 index 0000000000..1662a06417 --- /dev/null +++ b/multisrc/overrides/mangathemesia/manhwafreakfr/src/ManhwaFreakFR.kt @@ -0,0 +1,32 @@ +package eu.kanade.tachiyomi.extension.fr.manhwafreakfr + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.SChapter +import okhttp3.Request +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +class ManhwaFreakFR : MangaThemesia("ManhwaFreak", "https://manhwafreak.fr", "fr", dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)) { + override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/manga/?type=comic", headers) + + override fun popularMangaRequest(page: Int) = GET("$baseUrl/manga/?order=views&type=comic", headers) + + override fun searchMangaSelector() = ".listupd .lastest-serie" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = + GET("$baseUrl/page/$page/?s=$query") + + override fun chapterListSelector() = ".chapter-li a:not(:has(svg))" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + val urlElements = element.select("a") + setUrlWithoutDomain(urlElements.attr("href")) + name = element.select(".chapter-info p:nth-child(1)").text().ifBlank { urlElements.first()!!.text() } + date_upload = element.selectFirst(".chapter-info p:nth-child(2)")?.text().parseChapterDate() + } + + override fun getFilterList() = FilterList() +} diff --git a/multisrc/overrides/mangathemesia/manhwaindo/src/ManhwaIndo.kt b/multisrc/overrides/mangathemesia/manhwaindo/src/ManhwaIndo.kt index 365a2d8b56..2a6266576d 100644 --- a/multisrc/overrides/mangathemesia/manhwaindo/src/ManhwaIndo.kt +++ b/multisrc/overrides/mangathemesia/manhwaindo/src/ManhwaIndo.kt @@ -14,7 +14,7 @@ class ManhwaIndo : MangaThemesia( SimpleDateFormat("MMMM dd, yyyy", Locale("id")), ) { - override fun headersBuilder(): Headers.Builder = Headers.Builder() + override fun headersBuilder(): Headers.Builder = super.headersBuilder() .add("Referer", baseUrl) override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply { diff --git a/multisrc/overrides/mangathemesia/manhwalandmom/src/ManhwaLandMom.kt b/multisrc/overrides/mangathemesia/manhwalandmom/src/ManhwaLandMom.kt index fcab463287..5ef7a524fa 100644 --- a/multisrc/overrides/mangathemesia/manhwalandmom/src/ManhwaLandMom.kt +++ b/multisrc/overrides/mangathemesia/manhwalandmom/src/ManhwaLandMom.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.manhwalandmom import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class ManhwaLandMom : MangaThemesia("ManhwaLand.mom", "https://manhwaland.us", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/manhwalist/src/ManhwaList.kt b/multisrc/overrides/mangathemesia/manhwalist/src/ManhwaList.kt index ba3a8b0888..67411c519e 100644 --- a/multisrc/overrides/mangathemesia/manhwalist/src/ManhwaList.kt +++ b/multisrc/overrides/mangathemesia/manhwalist/src/ManhwaList.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.manhwalist import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class ManhwaList : MangaThemesia("ManhwaList", "https://manhwalist.xyz", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-hdpi/ic_launcher.png index 2be925c7d0..27f52461b1 100644 Binary files a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-hdpi/ic_launcher.png and b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-mdpi/ic_launcher.png index 2da0321045..49e1b01151 100644 Binary files a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-mdpi/ic_launcher.png and b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xhdpi/ic_launcher.png index 8bc21baa3c..9f4e3ea230 100644 Binary files a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xhdpi/ic_launcher.png and b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxhdpi/ic_launcher.png index 9c02b6d375..730c728806 100644 Binary files a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxhdpi/ic_launcher.png and b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxxhdpi/ic_launcher.png index 42b6479b1a..033ab83a0d 100644 Binary files a/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxxhdpi/ic_launcher.png and b/multisrc/overrides/mangathemesia/masterkomik/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/masterkomik/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/masterkomik/res/web_hi_res_512.png index bb9dd6dca4..62b45e5330 100644 Binary files a/multisrc/overrides/mangathemesia/masterkomik/res/web_hi_res_512.png and b/multisrc/overrides/mangathemesia/masterkomik/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/masterkomik/src/MasterKomik.kt b/multisrc/overrides/mangathemesia/masterkomik/src/MasterKomik.kt deleted file mode 100644 index e9082ffb68..0000000000 --- a/multisrc/overrides/mangathemesia/masterkomik/src/MasterKomik.kt +++ /dev/null @@ -1,19 +0,0 @@ -package eu.kanade.tachiyomi.extension.id.masterkomik - -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit - -class MasterKomik : MangaThemesia("MasterKomik", "https://masterkomik.com", "id") { - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .rateLimit(4) - .build() - - override val projectPageString = "/project-list" - - override val hasProjectPage = true -} diff --git a/multisrc/overrides/mangathemesia/masterkomik/src/TenshiId.kt b/multisrc/overrides/mangathemesia/masterkomik/src/TenshiId.kt new file mode 100644 index 0000000000..2dcc003d83 --- /dev/null +++ b/multisrc/overrides/mangathemesia/masterkomik/src/TenshiId.kt @@ -0,0 +1,30 @@ +package eu.kanade.tachiyomi.extension.id.masterkomik + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.OkHttpClient +import java.text.SimpleDateFormat +import java.util.Locale + +class TenshiId : MangaThemesia( + "Tenshi.id", + "https://tenshi.id", + "id", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id", "ID")), + mangaUrlDirectory = "/komik", +) { + + // MasterKomik changed to Tenshi.id + override val id: Long = 3146720114171452298 + + override val seriesArtistSelector: String = ".imptdt-artist-sub-2 i .js-button-custom" + override val seriesAuthorSelector: String = ".imptdt-author-sub-2 i .js-button-custom" + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(4) + .build() + + override val projectPageString = "/project-list" + + override val hasProjectPage = true +} diff --git a/multisrc/overrides/mangathemesia/miauscan/src/MiauScanFactory.kt b/multisrc/overrides/mangathemesia/miauscan/src/MiauScanFactory.kt index 1e3d18ae1e..9c848c1a0b 100644 --- a/multisrc/overrides/mangathemesia/miauscan/src/MiauScanFactory.kt +++ b/multisrc/overrides/mangathemesia/miauscan/src/MiauScanFactory.kt @@ -13,18 +13,23 @@ import java.util.Locale class MiauScanFactory : SourceFactory { override fun createSources() = listOf( - MiauScan("es", Filter.TriState.STATE_EXCLUDE), - MiauScan("pt-BR", Filter.TriState.STATE_INCLUDE), + MiauScan("es"), + MiauScan("pt-BR"), ) } -open class MiauScan(lang: String, private val portugueseMode: Int) : MangaThemesia( +open class MiauScan(lang: String) : MangaThemesia( name = "Miau Scan", - baseUrl = "https://miauscan.com", + baseUrl = "https://miauscans.com", lang = lang, dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), ) { + private val portugueseMode = + if (lang == "pt-BR") Filter.TriState.STATE_INCLUDE else Filter.TriState.STATE_EXCLUDE + + override val seriesGenreSelector = ".mgen a:not(:contains(Português))" + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val genreFilterIndex = filters.indexOfFirst { it is GenreListFilter } val genreFilter = filters.getOrNull(genreFilterIndex) as? GenreListFilter @@ -32,7 +37,7 @@ open class MiauScan(lang: String, private val portugueseMode: Int) : MangaThemes val overloadedGenreFilter = GenreListFilter( genres = genreFilter.state + listOf( - Genre("", PORTUGUESE_GENRE, portugueseMode), + Genre("", PORTUGUESE_GENRE_ID, portugueseMode), ), ) @@ -60,11 +65,11 @@ open class MiauScan(lang: String, private val portugueseMode: Int) : MangaThemes } override fun getGenreList(): List { - return super.getGenreList().filter { it.value != PORTUGUESE_GENRE } + return super.getGenreList().filter { it.value != PORTUGUESE_GENRE_ID } } companion object { - const val PORTUGUESE_GENRE = "307" + const val PORTUGUESE_GENRE_ID = "307" val PORTUGUESE_SUFFIX = "^\\(\\s*Portugu[êe]s\\s*\\)\\s*".toRegex(RegexOption.IGNORE_CASE) } diff --git a/multisrc/overrides/mangathemesia/mirrordesu/src/MirrorDesu.kt b/multisrc/overrides/mangathemesia/mirrordesu/src/MirrorDesu.kt index c1c007c77f..3703c33a28 100644 --- a/multisrc/overrides/mangathemesia/mirrordesu/src/MirrorDesu.kt +++ b/multisrc/overrides/mangathemesia/mirrordesu/src/MirrorDesu.kt @@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.mirrordesu import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class MirrorDesu : MangaThemesia("MirrorDesu", "https://mirrordesu.me", "id", "/komik") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index ad6b2180c1..0000000000 Binary files a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 577303ecf9..0000000000 Binary files a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 3ae3810454..0000000000 Binary files a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index cd12ab3527..0000000000 Binary files a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index e771402fe1..0000000000 Binary files a/multisrc/overrides/mangathemesia/modescanlator/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/modescanlator/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/modescanlator/res/web_hi_res_512.png deleted file mode 100644 index cb158b95d8..0000000000 Binary files a/multisrc/overrides/mangathemesia/modescanlator/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/modescanlator/src/ModeScanlator.kt b/multisrc/overrides/mangathemesia/modescanlator/src/ModeScanlator.kt deleted file mode 100644 index fc73005593..0000000000 --- a/multisrc/overrides/mangathemesia/modescanlator/src/ModeScanlator.kt +++ /dev/null @@ -1,26 +0,0 @@ -package eu.kanade.tachiyomi.extension.pt.modescanlator - -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import okhttp3.OkHttpClient -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -class ModeScanlator : MangaThemesia( - "Mode Scanlator", - "https://modescanlator.com", - "pt-BR", - mangaUrlDirectory = "/projetos", - dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), -) { - - // Site changed from Madara to WpMangaReader. - override val versionId: Int = 2 - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override val altNamePrefix = "Nome alternativo: " -} diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..dbb5fef993 Binary files /dev/null and b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..e4ebccf8b7 Binary files /dev/null and b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..c8063898d8 Binary files /dev/null and b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..bc4c9096cb Binary files /dev/null and b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6f01c468e8 Binary files /dev/null and b/multisrc/overrides/mangathemesia/moondaisyscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/moondaisyscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..b4aac86cb8 Binary files /dev/null and b/multisrc/overrides/mangathemesia/moondaisyscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/moondaisyscans/src/MoonDaisyScans.kt b/multisrc/overrides/mangathemesia/moondaisyscans/src/MoonDaisyScans.kt new file mode 100644 index 0000000000..ead5cb7a95 --- /dev/null +++ b/multisrc/overrides/mangathemesia/moondaisyscans/src/MoonDaisyScans.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.tr.moondaisyscans + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale + +class MoonDaisyScans : MangaThemesia( + "Moon Daisy Scans", + "https://moondaisyscans.com", + "tr", + dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("tr")), +) { + override val seriesAuthorSelector = ".tsinfo .imptdt:contains(Yazar) i" + override val seriesArtistSelector = ".tsinfo .imptdt:contains(Çizer) i" + override val seriesStatusSelector = ".tsinfo .imptdt:contains(Durum) i" +} diff --git a/multisrc/overrides/mangathemesia/nekomik/src/Nekomik.kt b/multisrc/overrides/mangathemesia/nekomik/src/Nekomik.kt index 513409e49c..79eb502be3 100644 --- a/multisrc/overrides/mangathemesia/nekomik/src/Nekomik.kt +++ b/multisrc/overrides/mangathemesia/nekomik/src/Nekomik.kt @@ -1,17 +1,49 @@ package eu.kanade.tachiyomi.extension.id.nekomik +import app.cash.quickjs.QuickJs import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.Page +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit +import org.jsoup.nodes.Document -class Nekomik : MangaThemesia("Nekomik", "https://nekomik.com", "id") { +class Nekomik : MangaThemesia("Nekomik", "https://nekomik.me", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() override val hasProjectPage = true + + override fun pageListParse(document: Document): List { + val obfuscatedJs = document.selectFirst("script:containsData(fromCharCode)")?.data() + ?: return super.pageListParse(document) + + val data = QuickJs.create().use { context -> + context.evaluate( + """ + ts_reader = { run: function(...args) { whatever = args[0] } }; + $obfuscatedJs; + JSON.stringify(whatever); + """.trimIndent(), + ) as String + } + + val tsReader = json.decodeFromString(data) + val imageUrls = tsReader.sources.firstOrNull()?.images ?: return emptyList() + return imageUrls.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) } + } + + @Serializable + data class TSReader( + val sources: List, + ) + + @Serializable + data class ReaderImageSource( + val source: String, + val images: List, + ) } diff --git a/multisrc/overrides/zeistmanga/noromax/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/noromax/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/zeistmanga/noromax/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/noromax/res/mipmap-hdpi/ic_launcher.png diff --git a/multisrc/overrides/zeistmanga/noromax/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/noromax/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/zeistmanga/noromax/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/noromax/res/mipmap-mdpi/ic_launcher.png diff --git a/multisrc/overrides/zeistmanga/noromax/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/noromax/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/zeistmanga/noromax/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/noromax/res/mipmap-xhdpi/ic_launcher.png diff --git a/multisrc/overrides/zeistmanga/noromax/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/noromax/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/zeistmanga/noromax/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/noromax/res/mipmap-xxhdpi/ic_launcher.png diff --git a/multisrc/overrides/zeistmanga/noromax/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/noromax/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/zeistmanga/noromax/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/noromax/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/zeistmanga/noromax/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/noromax/res/web_hi_res_512.png similarity index 100% rename from multisrc/overrides/zeistmanga/noromax/res/web_hi_res_512.png rename to multisrc/overrides/mangathemesia/noromax/res/web_hi_res_512.png diff --git a/multisrc/overrides/mangathemesia/noromax/src/Noromax.kt b/multisrc/overrides/mangathemesia/noromax/src/Noromax.kt new file mode 100644 index 0000000000..144bf7e0a6 --- /dev/null +++ b/multisrc/overrides/mangathemesia/noromax/src/Noromax.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.id.noromax + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia + +class Noromax : MangaThemesia("Noromax", "https://noromax.my.id", "id", "/Komik") { + + // Site changed from ZeistManga to MangaThemesia + override val versionId = 2 + + override val hasProjectPage = true +} diff --git a/multisrc/overrides/mangathemesia/opscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/opscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..5956934249 Binary files /dev/null and b/multisrc/overrides/mangathemesia/opscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/opscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/opscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..4a68cdd7ca Binary files /dev/null and b/multisrc/overrides/mangathemesia/opscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/opscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/opscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..4b60784a3b Binary files /dev/null and b/multisrc/overrides/mangathemesia/opscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/opscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/opscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1c2902361d Binary files /dev/null and b/multisrc/overrides/mangathemesia/opscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/opscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/opscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..229940bde0 Binary files /dev/null and b/multisrc/overrides/mangathemesia/opscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/opscans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/opscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..c4e354264a Binary files /dev/null and b/multisrc/overrides/mangathemesia/opscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/origamiorpheans/src/OrigamiOrpheans.kt b/multisrc/overrides/mangathemesia/origamiorpheans/src/OrigamiOrpheans.kt index f778cc0b47..b0b9d79777 100644 --- a/multisrc/overrides/mangathemesia/origamiorpheans/src/OrigamiOrpheans.kt +++ b/multisrc/overrides/mangathemesia/origamiorpheans/src/OrigamiOrpheans.kt @@ -15,7 +15,7 @@ class OrigamiOrpheans : MangaThemesia( // Scanlator migrated from Madara to WpMangaReader. override val versionId = 2 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .build() override val altNamePrefix = "Nomes alternativos: " diff --git a/multisrc/overrides/mangathemesia/ozulscans/src/OzulScans.kt b/multisrc/overrides/mangathemesia/ozulscans/src/OzulScans.kt index 126a4adf0e..37a70281f4 100644 --- a/multisrc/overrides/mangathemesia/ozulscans/src/OzulScans.kt +++ b/multisrc/overrides/mangathemesia/ozulscans/src/OzulScans.kt @@ -6,7 +6,7 @@ import java.util.Locale class OzulScans : MangaThemesia( "Ozul Scans", - "https://ozulscans.com", + "https://ozulscans.xyz", "ar", dateFormat = SimpleDateFormat("MMM d, yyy", Locale("ar")), ) diff --git a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/patatescans/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 06a09308c8..0000000000 Binary files a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/patatescans/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 3f242c2736..0000000000 Binary files a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 3a8b33e8aa..0000000000 Binary files a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 50325d4b40..0000000000 Binary files a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 9564f6f2d3..0000000000 Binary files a/multisrc/overrides/mangathemesia/patatescans/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/patatescans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/patatescans/res/web_hi_res_512.png deleted file mode 100644 index 370bff2517..0000000000 Binary files a/multisrc/overrides/mangathemesia/patatescans/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mangathemesia/patatescans/src/Patatescans.kt b/multisrc/overrides/mangathemesia/patatescans/src/Patatescans.kt deleted file mode 100644 index 8f96ffc4ca..0000000000 --- a/multisrc/overrides/mangathemesia/patatescans/src/Patatescans.kt +++ /dev/null @@ -1,5 +0,0 @@ -package eu.kanade.tachiyomi.extension.fr.patatescans - -import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia - -class Patatescans : MangaThemesia("Patatescans", "https://patatescans.com", "fr") diff --git a/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..22b5f44c46 Binary files /dev/null and b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..d196e814c5 Binary files /dev/null and b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..468199344e Binary files /dev/null and b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..9a780ff02f Binary files /dev/null and b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..86716aa030 Binary files /dev/null and b/multisrc/overrides/mangathemesia/potatomanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/potatomanga/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/potatomanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..5c438036eb Binary files /dev/null and b/multisrc/overrides/mangathemesia/potatomanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/potatomanga/src/PotatoManga.kt b/multisrc/overrides/mangathemesia/potatomanga/src/PotatoManga.kt new file mode 100644 index 0000000000..44eea894cf --- /dev/null +++ b/multisrc/overrides/mangathemesia/potatomanga/src/PotatoManga.kt @@ -0,0 +1,23 @@ +package eu.kanade.tachiyomi.extension.ar.potatomanga + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale + +class PotatoManga : MangaThemesia( + "PotatoManga", + "https://potatomanga.xyz", + "ar", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("ar")), +) { + override val seriesArtistSelector = + ".infotable tr:contains(الرسام) td:last-child, ${super.seriesArtistSelector}" + override val seriesAuthorSelector = + ".infotable tr:contains(المؤلف) td:last-child, ${super.seriesAuthorSelector}" + override val seriesStatusSelector = + ".infotable tr:contains(الحالة) td:last-child, ${super.seriesStatusSelector}" + override val seriesTypeSelector = + ".infotable tr:contains(النوع) td:last-child, ${super.seriesTypeSelector}" + override val seriesAltNameSelector = + ".infotable tr:contains(الأسماء الثانوية) td:last-child, ${super.seriesAltNameSelector}" +} diff --git a/multisrc/overrides/mangathemesia/queenscans/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..847a3610d1 Binary files /dev/null and b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/queenscans/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3c4da6d4a6 Binary files /dev/null and b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..9f080d5d85 Binary files /dev/null and b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..aab37056de Binary files /dev/null and b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..ec2c697900 Binary files /dev/null and b/multisrc/overrides/mangathemesia/queenscans/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/queenscans/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/queenscans/res/web_hi_res_512.png new file mode 100644 index 0000000000..a85e063b40 Binary files /dev/null and b/multisrc/overrides/mangathemesia/queenscans/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/queenscans/src/QueenScans.kt b/multisrc/overrides/mangathemesia/queenscans/src/QueenScans.kt new file mode 100644 index 0000000000..f7144572c0 --- /dev/null +++ b/multisrc/overrides/mangathemesia/queenscans/src/QueenScans.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.extension.en.queenscans + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Request + +class QueenScans : MangaThemesia("Fairy Manga", "https://fairymanga.com", "en") { + override val client = super.client.newBuilder() + .rateLimit(2) + .build() + + override val id = 4680104728999154642 + + override fun mangaDetailsRequest(manga: SManga): Request { + if (manga.url.startsWith("/comics")) { + manga.url.replaceFirst("/comics", mangaUrlDirectory) + } + return super.mangaDetailsRequest(manga) + } + + override fun chapterListRequest(manga: SManga): Request { + if (manga.url.startsWith("/comics")) { + manga.url.replaceFirst("/comics", mangaUrlDirectory) + } + return super.chapterListRequest(manga) + } +} diff --git a/multisrc/overrides/mangathemesia/rawkuma/src/Rawkuma.kt b/multisrc/overrides/mangathemesia/rawkuma/src/Rawkuma.kt index bce788723e..3bc87fcbea 100644 --- a/multisrc/overrides/mangathemesia/rawkuma/src/Rawkuma.kt +++ b/multisrc/overrides/mangathemesia/rawkuma/src/Rawkuma.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.ja.rawkuma import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class Rawkuma : MangaThemesia("Rawkuma", "https://rawkuma.com/", "ja") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/readkomik/src/ReadKomik.kt b/multisrc/overrides/mangathemesia/readkomik/src/ReadKomik.kt index 98a881a7f1..d1b8c75319 100644 --- a/multisrc/overrides/mangathemesia/readkomik/src/ReadKomik.kt +++ b/multisrc/overrides/mangathemesia/readkomik/src/ReadKomik.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.en.readkomik import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class ReadKomik : MangaThemesia("Readkomik", "https://readkomik.com", "en") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/ryukonesia/src/Ryukonesia.kt b/multisrc/overrides/mangathemesia/ryukonesia/src/Ryukonesia.kt index 4e19d66e58..f6a1540aae 100644 --- a/multisrc/overrides/mangathemesia/ryukonesia/src/Ryukonesia.kt +++ b/multisrc/overrides/mangathemesia/ryukonesia/src/Ryukonesia.kt @@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.ryukonesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class Ryukonesia : MangaThemesia("Ryukonesia", "https://ryukonesia.net", "id") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/sektedoujin/src/SekteDoujin.kt b/multisrc/overrides/mangathemesia/sektedoujin/src/SekteDoujin.kt index 92df0e544e..dcac72f8a6 100644 --- a/multisrc/overrides/mangathemesia/sektedoujin/src/SekteDoujin.kt +++ b/multisrc/overrides/mangathemesia/sektedoujin/src/SekteDoujin.kt @@ -5,13 +5,10 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit class SekteDoujin : MangaThemesia("Sekte Doujin", "https://sektedoujin.lol", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id"))) { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() } diff --git a/multisrc/overrides/mangathemesia/sheamanga/src/SheaManga.kt b/multisrc/overrides/mangathemesia/sheamanga/src/SheaManga.kt index fb19b54e82..70b8b4676a 100644 --- a/multisrc/overrides/mangathemesia/sheamanga/src/SheaManga.kt +++ b/multisrc/overrides/mangathemesia/sheamanga/src/SheaManga.kt @@ -6,7 +6,6 @@ import okhttp3.Dns import okhttp3.OkHttpClient import java.text.SimpleDateFormat import java.util.Locale -import java.util.concurrent.TimeUnit class SheaManga : MangaThemesia( "Shea Manga", @@ -15,9 +14,7 @@ class SheaManga : MangaThemesia( dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id")), ) { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .dns(Dns.SYSTEM) .build() diff --git a/multisrc/overrides/mangathemesia/silencescan/src/SilenceScan.kt b/multisrc/overrides/mangathemesia/silencescan/src/SilenceScan.kt index c11b6d211d..959bfd2ace 100644 --- a/multisrc/overrides/mangathemesia/silencescan/src/SilenceScan.kt +++ b/multisrc/overrides/mangathemesia/silencescan/src/SilenceScan.kt @@ -16,9 +16,7 @@ class SilenceScan : MangaThemesia( override val versionId: Int = 2 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .build() override val altNamePrefix = "Nome alternativo: " diff --git a/multisrc/overrides/mangathemesia/skymangas/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..b19685d7ac Binary files /dev/null and b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/skymangas/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..15f2b75bb0 Binary files /dev/null and b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..00fdf99430 Binary files /dev/null and b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..75b4295bda Binary files /dev/null and b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8f3d258119 Binary files /dev/null and b/multisrc/overrides/mangathemesia/skymangas/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/skymangas/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/skymangas/res/web_hi_res_512.png new file mode 100644 index 0000000000..4fd9557d83 Binary files /dev/null and b/multisrc/overrides/mangathemesia/skymangas/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/skymangas/src/SkyMangas.kt b/multisrc/overrides/mangathemesia/skymangas/src/SkyMangas.kt new file mode 100644 index 0000000000..9ef577fc55 --- /dev/null +++ b/multisrc/overrides/mangathemesia/skymangas/src/SkyMangas.kt @@ -0,0 +1,43 @@ +package eu.kanade.tachiyomi.extension.es.skymangas + +import android.util.Base64 +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.source.model.Page +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive +import org.jsoup.nodes.Document +import java.lang.IllegalArgumentException +import java.text.SimpleDateFormat +import java.util.Locale + +class SkyMangas : MangaThemesia( + "SkyMangas", + "https://skymangas.com", + "es", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), +) { + override fun pageListParse(document: Document): List { + val script = document.selectFirst("div.readercontent > div.wrapper > script") + ?: return super.pageListParse(document) + + val scriptSrc = script.attr("src") + + if (scriptSrc.startsWith("data:text/javascript;base64,")) { + val encodedData = scriptSrc.substringAfter("data:text/javascript;base64,") + val decodedData = Base64.decode(encodedData, Base64.DEFAULT).toString(Charsets.UTF_8) + + val imageListJson = JSON_IMAGE_LIST_REGEX.find(decodedData)?.destructured?.toList()?.get(0).orEmpty() + val imageList = try { + json.parseToJsonElement(imageListJson).jsonArray + } catch (_: IllegalArgumentException) { + emptyList() + } + + return imageList.mapIndexed { i, jsonEl -> + Page(i, "", jsonEl.jsonPrimitive.content) + } + } + + return super.pageListParse(document) + } +} diff --git a/multisrc/overrides/mangathemesia/sssscanlator/src/SSSScanlator.kt b/multisrc/overrides/mangathemesia/sssscanlator/src/SSSScanlator.kt new file mode 100644 index 0000000000..cc4c69dae2 --- /dev/null +++ b/multisrc/overrides/mangathemesia/sssscanlator/src/SSSScanlator.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.pt.sssscanlator + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import okhttp3.OkHttpClient +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class SSSScanlator : MangaThemesia( + "SSSScanlator", + "https://sssscanlator.com", + "pt-BR", + dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), +) { + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() +} diff --git a/multisrc/overrides/mangathemesia/starlightscan/src/StarlightScan.kt b/multisrc/overrides/mangathemesia/starlightscan/src/StarlightScan.kt new file mode 100644 index 0000000000..99a51e42cd --- /dev/null +++ b/multisrc/overrides/mangathemesia/starlightscan/src/StarlightScan.kt @@ -0,0 +1,103 @@ +package eu.kanade.tachiyomi.extension.pt.starlightscan + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class StarlightScan : MangaThemesia( + "Starlight Scan", + "https://starligthscan.com", + "pt-BR", + mangaUrlDirectory = "/mangas", + dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale("pt", "BR")), +) { + + override val client: OkHttpClient = super.client.newBuilder() + .rateLimit(1, 2, TimeUnit.SECONDS) + .build() + + override val sendViewCount = false + + override fun latestUpdatesRequest(page: Int): Request = GET(baseUrl, headers) + + override fun latestUpdatesParse(response: Response): MangasPage { + val mangaList = response.asJsoup() + .select(latestUpdatesSelector()) + .map(::latestUpdatesFromElement) + + return MangasPage(mangaList, hasNextPage = false) + } + + override fun latestUpdatesSelector() = "div.mostRecentMangaCard__listContainer article.mostRecentMangaCard" + + override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { + title = element.selectFirst("a.mostRecentMangaCard__title")!!.text() + thumbnail_url = element.selectFirst("img.mostRecentMangaCard__cover")!!.imgAttr() + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = baseUrl.toHttpUrl().newBuilder() + .addPathSegment(if (query.isEmpty()) mangaUrlDirectory.substring(1) else "buscar") + .addQueryParameter("search", query) + .addQueryParameter("page-current", page.toString()) + .build() + + return GET(url, headers) + } + + override fun searchMangaSelector() = "div.bulkMangaList article.bulkMangaCard" + + override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply { + title = element.selectFirst("a.bulkMangaCard__title")!!.text() + thumbnail_url = element.selectFirst("img.bulkMangaCard__cover")!!.imgAttr() + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + } + + override fun searchMangaNextPageSelector() = "footer.base__horizontalList a:contains(Próxima):not([disabled])" + + override val seriesDetailsSelector = "section.mangaDetails" + override val seriesTitleSelector = "h1.mangaDetails__title" + override val seriesAuthorSelector = "span.mangaDetails__author" + override val seriesDescriptionSelector = "span.mangaDetails__description" + override val seriesGenreSelector = "li.mangaTags__item" + override val seriesStatusSelector = "span.base__horizontalList[title^=Status]" + override val seriesThumbnailSelector = "img.mangaDetails__cover" + + override fun String?.parseStatus(): Int = when (this) { + "Publicação Finalizada" -> SManga.COMPLETED + else -> SManga.ONGOING + } + + override fun chapterListParse(response: Response): List { + return response.asJsoup() + .select(chapterListSelector()) + .map(::chapterFromElement) + .filter { it.name.isNotEmpty() } + } + + override fun chapterListSelector() = "div.mangaDetails__episodesContainer div.mangaDetails__episode" + + override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { + name = element.selectFirst("a.mangaDetails__episodeTitle")!!.text() + date_upload = element.selectFirst("span.mangaDetails__episodeReleaseDate")?.text().parseChapterDate() + setUrlWithoutDomain(element.selectFirst("a")!!.absUrl("href")) + } + + override val pageSelector = "div.scanImagesContainer img.scanImage" + + override fun getFilterList(): FilterList = FilterList() +} diff --git a/multisrc/overrides/mangathemesia/summertoon/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..31f2f9c56d Binary files /dev/null and b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/summertoon/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..15ba32e164 Binary files /dev/null and b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..69a9d3fb77 Binary files /dev/null and b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..85d2a85b37 Binary files /dev/null and b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..83a684bf77 Binary files /dev/null and b/multisrc/overrides/mangathemesia/summertoon/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/summertoon/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/summertoon/res/web_hi_res_512.png new file mode 100644 index 0000000000..1d22bf5d17 Binary files /dev/null and b/multisrc/overrides/mangathemesia/summertoon/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/summertoon/src/SummerToon.kt b/multisrc/overrides/mangathemesia/summertoon/src/SummerToon.kt new file mode 100644 index 0000000000..65a68fed5d --- /dev/null +++ b/multisrc/overrides/mangathemesia/summertoon/src/SummerToon.kt @@ -0,0 +1,20 @@ +package eu.kanade.tachiyomi.extension.tr.summertoon + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import java.text.SimpleDateFormat +import java.util.Locale + +class SummerToon : MangaThemesia( + "SummerToon", + "https://summertoon.com", + "tr", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("tr")), +) { + override val client = super.client.newBuilder() + .rateLimit(1, 1) + .build() + + override val seriesStatusSelector = ".tsinfo .imptdt:contains(Durum) i" + override val seriesAuthorSelector = ".fmed b:contains(Yazar)+span" +} diff --git a/multisrc/overrides/mangathemesia/sushiscan/src/SushiScan.kt b/multisrc/overrides/mangathemesia/sushiscan/src/SushiScan.kt index 6a09eed4f1..cca6e6dd2f 100644 --- a/multisrc/overrides/mangathemesia/sushiscan/src/SushiScan.kt +++ b/multisrc/overrides/mangathemesia/sushiscan/src/SushiScan.kt @@ -9,7 +9,7 @@ import org.jsoup.nodes.Document import java.text.SimpleDateFormat import java.util.Locale -class SushiScan : MangaThemesia("Sushi-Scan", "https://sushiscan.net", "fr", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.FRENCH)) { +class SushiScan : MangaThemesia("Sushi-Scan", "https://sushiscan.net", "fr", mangaUrlDirectory = "/catalogue", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.FRENCH)) { override val altNamePrefix = "Nom alternatif : " override val seriesAuthorSelector = ".imptdt:contains(Auteur) i, .fmed b:contains(Auteur)+span" override val seriesStatusSelector = ".imptdt:contains(Statut) i" diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..aabef19589 Binary files /dev/null and b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..462e84620f Binary files /dev/null and b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..42fe15f564 Binary files /dev/null and b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..5ae9300b38 Binary files /dev/null and b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..656df0261a Binary files /dev/null and b/multisrc/overrides/mangathemesia/sushiscanfr/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/sushiscanfr/res/web_hi_res_512.png new file mode 100644 index 0000000000..373e99d426 Binary files /dev/null and b/multisrc/overrides/mangathemesia/sushiscanfr/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/sushiscanfr/src/SushiScanFR.kt b/multisrc/overrides/mangathemesia/sushiscanfr/src/SushiScanFR.kt new file mode 100644 index 0000000000..c34efc31d9 --- /dev/null +++ b/multisrc/overrides/mangathemesia/sushiscanfr/src/SushiScanFR.kt @@ -0,0 +1,24 @@ +package eu.kanade.tachiyomi.extension.fr.sushiscanfr + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.source.model.SManga +import org.jsoup.nodes.Document +import java.text.SimpleDateFormat +import java.util.Locale + +class SushiScanFR : MangaThemesia("Sushiscan.fr", "https://sushiscan.fr", "fr", dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.FRENCH)) { + override val altNamePrefix = "Nom alternatif : " + override val seriesAuthorSelector = ".imptdt:contains(Auteur) i, .fmed b:contains(Auteur)+span" + override val seriesStatusSelector = ".imptdt:contains(Statut) i" + override fun String?.parseStatus(): Int = when { + this == null -> SManga.UNKNOWN + this.contains("En Cours", ignoreCase = true) -> SManga.ONGOING + this.contains("Terminé", ignoreCase = true) -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + + override fun mangaDetailsParse(document: Document): SManga = + super.mangaDetailsParse(document).apply { + status = document.select(seriesStatusSelector).text().parseStatus() + } +} diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..9f0bcaaf7f Binary files /dev/null and b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..a1a5c015e2 Binary files /dev/null and b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..9aad01888f Binary files /dev/null and b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..94d5f1c64e Binary files /dev/null and b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0281691862 Binary files /dev/null and b/multisrc/overrides/mangathemesia/tenkaiscan/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/tenkaiscan/res/web_hi_res_512.png new file mode 100644 index 0000000000..63da273720 Binary files /dev/null and b/multisrc/overrides/mangathemesia/tenkaiscan/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/tenkaiscan/src/TenkaiScan.kt b/multisrc/overrides/mangathemesia/tenkaiscan/src/TenkaiScan.kt new file mode 100644 index 0000000000..8c4703212d --- /dev/null +++ b/multisrc/overrides/mangathemesia/tenkaiscan/src/TenkaiScan.kt @@ -0,0 +1,12 @@ +package eu.kanade.tachiyomi.extension.es.tenkaiscan + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import java.text.SimpleDateFormat +import java.util.Locale + +class TenkaiScan : MangaThemesia( + "TenkaiScan", + "https://tenkaiscan.net", + "es", + dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("en")), +) diff --git a/multisrc/overrides/mangathemesia/tsundokutraducoes/src/TsundokuTraducoes.kt b/multisrc/overrides/mangathemesia/tsundokutraducoes/src/TsundokuTraducoes.kt index 66e4a8bdaa..3211e6c8aa 100644 --- a/multisrc/overrides/mangathemesia/tsundokutraducoes/src/TsundokuTraducoes.kt +++ b/multisrc/overrides/mangathemesia/tsundokutraducoes/src/TsundokuTraducoes.kt @@ -14,7 +14,7 @@ class TsundokuTraducoes : MangaThemesia( dateFormat = SimpleDateFormat("MMMMM d, yyyy", Locale("pt", "BR")), ) { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(1, 2, TimeUnit.SECONDS) .build() diff --git a/multisrc/overrides/mangathemesia/uzaymanga/src/UzayManga.kt b/multisrc/overrides/mangathemesia/uzaymanga/src/UzayManga.kt index 6ce8dcf98f..d7cf87386e 100644 --- a/multisrc/overrides/mangathemesia/uzaymanga/src/UzayManga.kt +++ b/multisrc/overrides/mangathemesia/uzaymanga/src/UzayManga.kt @@ -4,4 +4,7 @@ import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import java.text.SimpleDateFormat import java.util.Locale -class UzayManga : MangaThemesia("Uzay Manga", "https://uzaymanga.com", "tr", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr"))) +class UzayManga : MangaThemesia("Uzay Manga", "https://uzaymanga.com", "tr", dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr"))) { + override fun headersBuilder() = super.headersBuilder() + .add("referer", "$baseUrl/") +} diff --git a/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..d29e1c721f Binary files /dev/null and b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..fad5354685 Binary files /dev/null and b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..948a8a97e1 Binary files /dev/null and b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..2c525ee197 Binary files /dev/null and b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8ceff41dd2 Binary files /dev/null and b/multisrc/overrides/mangathemesia/vexmanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/multisrc/overrides/mangathemesia/vexmanga/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/vexmanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..67ed7b365e Binary files /dev/null and b/multisrc/overrides/mangathemesia/vexmanga/res/web_hi_res_512.png differ diff --git a/multisrc/overrides/mangathemesia/vexmanga/src/VexManga.kt b/multisrc/overrides/mangathemesia/vexmanga/src/VexManga.kt new file mode 100644 index 0000000000..e263f84786 --- /dev/null +++ b/multisrc/overrides/mangathemesia/vexmanga/src/VexManga.kt @@ -0,0 +1,74 @@ +package eu.kanade.tachiyomi.extension.ar.vexmanga + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonPrimitive +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.lang.IllegalArgumentException +import java.util.Calendar + +class VexManga : MangaThemesia( + "فيكس مانجا", + "https://vexmanga.net", + "ar", +) { + override fun searchMangaSelector() = ".listupd .latest-series, ${super.searchMangaSelector()}" + override val sendViewCount = false + override fun chapterListSelector() = ".ulChapterList > a, ${super.chapterListSelector()}" + + override val seriesArtistSelector = + ".tsinfo .imptdt:contains(الرسام) i, ${super.seriesArtistSelector}" + override val seriesAuthorSelector = + ".tsinfo .imptdt:contains(المؤلف) i, ${super.seriesAuthorSelector}" + override val seriesStatusSelector = + ".tsinfo .imptdt:contains(الحالة) i, ${super.seriesStatusSelector}" + override val seriesTypeSelector = + ".tsinfo .imptdt:contains(النوع) i, ${super.seriesTypeSelector}" + + override fun String?.parseStatus() = when { + this == null -> SManga.UNKNOWN + this.contains("مستمر", ignoreCase = true) -> SManga.ONGOING + this.contains("مكتمل", ignoreCase = true) -> SManga.COMPLETED + this.contains("متوقف", ignoreCase = true) -> SManga.ON_HIATUS + else -> SManga.UNKNOWN + } + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + setUrlWithoutDomain(element.attr("href")) + name = element.select(".chapternum").text() + date_upload = element.select(".chapterdate").text().parseRelativeDate() + } + + private fun String.parseRelativeDate(): Long { + val number = Regex("""(\d+)""").find(this)?.value?.toIntOrNull() ?: return 0 + val cal = Calendar.getInstance() + + return when { + this.contains("أيام", true) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number) }.timeInMillis + this.contains("ساعة", true) -> cal.apply { add(Calendar.HOUR, -number) }.timeInMillis + this.contains("دقائق", true) -> cal.apply { add(Calendar.MINUTE, -number) }.timeInMillis + this.contains("أسبوعين", true) -> cal.apply { add(Calendar.DAY_OF_MONTH, -number * 7) }.timeInMillis + this.contains("أشهر", true) -> cal.apply { add(Calendar.MONTH, -number) }.timeInMillis + else -> 0 + } + } + + override fun pageListParse(document: Document): List { + val docString = document.toString() + val imageListJson = JSON_IMAGE_LIST_REGEX.find(docString)?.destructured?.toList()?.get(0).orEmpty() + val imageList = try { + json.parseToJsonElement(imageListJson).jsonArray + } catch (_: IllegalArgumentException) { + emptyList() + } + val scriptPages = imageList.mapIndexed { i, jsonEl -> + Page(i, "", jsonEl.jsonPrimitive.content) + } + + return scriptPages + } +} diff --git a/multisrc/overrides/mangathemesia/westmanga/src/WestManga.kt b/multisrc/overrides/mangathemesia/westmanga/src/WestManga.kt index 2d460ba156..a3181f471a 100644 --- a/multisrc/overrides/mangathemesia/westmanga/src/WestManga.kt +++ b/multisrc/overrides/mangathemesia/westmanga/src/WestManga.kt @@ -3,15 +3,12 @@ package eu.kanade.tachiyomi.extension.id.westmanga import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.network.interceptor.rateLimit import okhttp3.OkHttpClient -import java.util.concurrent.TimeUnit class WestManga : MangaThemesia("West Manga", "https://westmanga.info", "id") { // Formerly "West Manga (WP Manga Stream)" override val id = 8883916630998758688 - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .rateLimit(4) .build() diff --git a/multisrc/overrides/mangathemesia/xcalibrscans/src/xCaliBRScans.kt b/multisrc/overrides/mangathemesia/xcalibrscans/src/xCaliBRScans.kt index 00c8ca8118..4cb3f9e3b0 100644 --- a/multisrc/overrides/mangathemesia/xcalibrscans/src/xCaliBRScans.kt +++ b/multisrc/overrides/mangathemesia/xcalibrscans/src/xCaliBRScans.kt @@ -8,13 +8,10 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import org.jsoup.nodes.Document import org.jsoup.nodes.Element -import java.util.concurrent.TimeUnit class xCaliBRScans : MangaThemesia("xCaliBR Scans", "https://xcalibrscans.com", "en") { - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) + override val client: OkHttpClient = super.client.newBuilder() .addInterceptor(AntiScrapInterceptor()) .rateLimit(2) .build() diff --git a/multisrc/overrides/mmrcms/zahard/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/zahard/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/mmrcms/zahard/res/mipmap-hdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/zahard/res/mipmap-hdpi/ic_launcher.png diff --git a/multisrc/overrides/mmrcms/zahard/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/zahard/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/mmrcms/zahard/res/mipmap-mdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/zahard/res/mipmap-mdpi/ic_launcher.png diff --git a/multisrc/overrides/mmrcms/zahard/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/zahard/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/mmrcms/zahard/res/mipmap-xhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/zahard/res/mipmap-xhdpi/ic_launcher.png diff --git a/multisrc/overrides/mmrcms/zahard/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/zahard/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/mmrcms/zahard/res/mipmap-xxhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/zahard/res/mipmap-xxhdpi/ic_launcher.png diff --git a/multisrc/overrides/mmrcms/zahard/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mangathemesia/zahard/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/mmrcms/zahard/res/mipmap-xxxhdpi/ic_launcher.png rename to multisrc/overrides/mangathemesia/zahard/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/mmrcms/zahard/res/web_hi_res_512.png b/multisrc/overrides/mangathemesia/zahard/res/web_hi_res_512.png similarity index 100% rename from multisrc/overrides/mmrcms/zahard/res/web_hi_res_512.png rename to multisrc/overrides/mangathemesia/zahard/res/web_hi_res_512.png diff --git a/multisrc/overrides/mangathemesia/zahard/src/Zahard.kt b/multisrc/overrides/mangathemesia/zahard/src/Zahard.kt new file mode 100644 index 0000000000..641e9093c7 --- /dev/null +++ b/multisrc/overrides/mangathemesia/zahard/src/Zahard.kt @@ -0,0 +1,36 @@ +package eu.kanade.tachiyomi.extension.en.zahard + +import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request + +class Zahard : MangaThemesia( + "Zahard", + "https://zahard.xyz", + "en", + mangaUrlDirectory = "/library", +) { + override val versionId = 2 + + override val supportsLatest = false + + override val pageSelector = "div#chapter_imgs img" + + override fun searchMangaNextPageSelector() = "a[rel=next]" + + override fun chapterListSelector() = "#chapterlist > ul > a" + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = baseUrl.toHttpUrl().newBuilder() + .addPathSegment(mangaUrlDirectory.substring(1)) + .addQueryParameter("search", query) + .addQueryParameter("page", page.toString()) + .build() + + return GET(url, headers) + } + + override fun getFilterList() = FilterList() +} diff --git a/multisrc/overrides/mangaworld/default/AndroidManifest.xml b/multisrc/overrides/mangaworld/default/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/multisrc/overrides/mangaworld/default/AndroidManifest.xml +++ b/multisrc/overrides/mangaworld/default/AndroidManifest.xml @@ -1,2 +1,2 @@ - + diff --git a/multisrc/overrides/mmrcms/frscan/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mmrcms/frscan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 17656f64fd..0000000000 Binary files a/multisrc/overrides/mmrcms/frscan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/frscan/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mmrcms/frscan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index c463b18fe0..0000000000 Binary files a/multisrc/overrides/mmrcms/frscan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/frscan/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mmrcms/frscan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 3de26b4c6b..0000000000 Binary files a/multisrc/overrides/mmrcms/frscan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/frscan/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mmrcms/frscan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 5f682d92d2..0000000000 Binary files a/multisrc/overrides/mmrcms/frscan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/frscan/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mmrcms/frscan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 45062e7025..0000000000 Binary files a/multisrc/overrides/mmrcms/frscan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/frscan/res/web_hi_res_512.png b/multisrc/overrides/mmrcms/frscan/res/web_hi_res_512.png deleted file mode 100644 index 48ca32cd1d..0000000000 Binary files a/multisrc/overrides/mmrcms/frscan/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/mangafr/src/MangaFR.kt b/multisrc/overrides/mmrcms/mangafr/src/MangaFR.kt new file mode 100644 index 0000000000..30d784282e --- /dev/null +++ b/multisrc/overrides/mmrcms/mangafr/src/MangaFR.kt @@ -0,0 +1,55 @@ +package eu.kanade.tachiyomi.extension.fr.mangafr + +import eu.kanade.tachiyomi.multisrc.mmrcms.MMRCMS +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import okhttp3.Response +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale + +class MangaFR : MMRCMS("Manga-FR", "https://manga-fr.me", "fr") { + override fun mangaDetailsParse(response: Response): SManga { + return super.mangaDetailsParse(response).apply { + title = title.replace("Chapitres ", "") + } + } + + override fun nullableChapterFromElement(element: Element): SChapter? { + val chapter = SChapter.create() + + val titleWrapper = element.select("[class^=chapter-title-rtl]").first()!! + val chapterElement = titleWrapper.getElementsByTag("a")!! + val url = chapterElement.attr("href") + + chapter.url = getUrlWithoutBaseUrl(url) + + // Construct chapter names + // Before -> Scan VF: + // Now -> Chapitre : OR Chapitre + val chapterText = chapterElement.text() + val numberRegex = Regex("""[1-9]\d*(\.\d+)*""") + val chapterNumber = numberRegex.find(chapterText)?.value.orEmpty() + val chapterTitle = titleWrapper.getElementsByTag("em")!!.text() + if (chapterTitle.toIntOrNull() != null) { + chapter.name = "Chapitre $chapterNumber" + } else { + chapter.name = "Chapitre $chapterNumber : $chapterTitle" + } + + // Parse date + val dateText = element.getElementsByClass("date-chapter-title-rtl").text().trim() + + chapter.date_upload = runCatching { + dateFormat.parse(dateText)?.time + }.getOrNull() ?: 0L + + return chapter + } + + companion object { + val dateFormat by lazy { + SimpleDateFormat("d MMM. yyyy", Locale.US) + } + } +} diff --git a/multisrc/overrides/mmrcms/mangascan/src/MangaScan.kt b/multisrc/overrides/mmrcms/mangascan/src/MangaScan.kt new file mode 100644 index 0000000000..ef2fd21ea2 --- /dev/null +++ b/multisrc/overrides/mmrcms/mangascan/src/MangaScan.kt @@ -0,0 +1,17 @@ +package eu.kanade.tachiyomi.extension.fr.mangascan + +import eu.kanade.tachiyomi.multisrc.mmrcms.MMRCMS +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.Page +import okhttp3.Request + +class MangaScan : MMRCMS("Manga-Scan", "https://manga-scan.co", "fr") { + override fun imageRequest(page: Page): Request { + val newHeaders = headersBuilder() + .set("Referer", baseUrl) + .set("Accept", "image/avif,image/webp,*/*") + .build() + + return GET(page.imageUrl!!, newHeaders) + } +} diff --git a/multisrc/overrides/mmrcms/scanone/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/mmrcms/scanone/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 01d62d703e..0000000000 Binary files a/multisrc/overrides/mmrcms/scanone/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/scanone/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/mmrcms/scanone/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 938684b6ce..0000000000 Binary files a/multisrc/overrides/mmrcms/scanone/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/scanone/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/mmrcms/scanone/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 80ba0f8102..0000000000 Binary files a/multisrc/overrides/mmrcms/scanone/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/scanone/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/mmrcms/scanone/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 5f6803bdeb..0000000000 Binary files a/multisrc/overrides/mmrcms/scanone/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/scanone/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/mmrcms/scanone/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index ea931f7edb..0000000000 Binary files a/multisrc/overrides/mmrcms/scanone/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/multisrc/overrides/mmrcms/scanone/res/web_hi_res_512.png b/multisrc/overrides/mmrcms/scanone/res/web_hi_res_512.png deleted file mode 100644 index a0f97bf081..0000000000 Binary files a/multisrc/overrides/mmrcms/scanone/res/web_hi_res_512.png and /dev/null differ diff --git a/multisrc/overrides/monochrome/default/AndroidManifest.xml b/multisrc/overrides/monochrome/default/AndroidManifest.xml index c1dc6e6eaa..37d1fedbdc 100644 --- a/multisrc/overrides/monochrome/default/AndroidManifest.xml +++ b/multisrc/overrides/monochrome/default/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - - + diff --git a/multisrc/overrides/mymangacms/default/AndroidManifest.xml b/multisrc/overrides/mymangacms/default/AndroidManifest.xml index 8a95129799..29f5f8a850 100644 --- a/multisrc/overrides/mymangacms/default/AndroidManifest.xml +++ b/multisrc/overrides/mymangacms/default/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - \ No newline at end of file + diff --git a/multisrc/overrides/nepnep/mangasee/AndroidManifest.xml b/multisrc/overrides/nepnep/mangasee/AndroidManifest.xml index f2bfc55a6a..4dd2c5b71f 100644 --- a/multisrc/overrides/nepnep/mangasee/AndroidManifest.xml +++ b/multisrc/overrides/nepnep/mangasee/AndroidManifest.xml @@ -1,7 +1,6 @@ - - + + - + - + - diff --git a/multisrc/overrides/readallcomics/readallcomicscom/src/ReadAllComicsCom.kt b/multisrc/overrides/readallcomics/readallcomicscom/src/ReadAllComicsCom.kt index 9819226c4b..967ab5a892 100644 --- a/multisrc/overrides/readallcomics/readallcomicscom/src/ReadAllComicsCom.kt +++ b/multisrc/overrides/readallcomics/readallcomicscom/src/ReadAllComicsCom.kt @@ -17,6 +17,8 @@ class ReadAllComicsCom : ReadAllComics("ReadAllComics", "https://readallcomics.c } } + override fun pageListSelector() = "body img:not(body div[id=\"logo\"] img)" + companion object { private val titleRegex = Regex("""^([a-zA-Z_.\s\-–:]*)""") } diff --git a/multisrc/overrides/readerfront/default/AndroidManifest.xml b/multisrc/overrides/readerfront/default/AndroidManifest.xml index 1fce661c89..b53f52f6e4 100644 --- a/multisrc/overrides/readerfront/default/AndroidManifest.xml +++ b/multisrc/overrides/readerfront/default/AndroidManifest.xml @@ -1,6 +1,5 @@ - + { val key = SecretKeySpec("cxNB23W8xzKJV26O".toByteArray(), "AES") val iv = IvParameterSpec("opb4x7z21vg1f3gI".toByteArray()) diff --git a/multisrc/overrides/webtoons/webtoons/AndroidManifest.xml b/multisrc/overrides/webtoons/webtoons/AndroidManifest.xml index 14082a252f..1ce564382b 100644 --- a/multisrc/overrides/webtoons/webtoons/AndroidManifest.xml +++ b/multisrc/overrides/webtoons/webtoons/AndroidManifest.xml @@ -1,6 +1,5 @@ - + img").mapIndexed { i, element -> Page(i, "", element.attr("data-url")) } if (showAuthorsNotesPref()) { - val note = document.select("div.comment_area div.info_area p").text() + val note = document.select("div.creator_note p.author_text").text() if (note.isNotEmpty()) { - val creator = document.select("div.creator_note span.author a").text().trim() + val creator = document.select("div.creator_note a.author_name span").text().trim() pages = pages + Page( pages.size, diff --git a/multisrc/overrides/wpcomics/nettruyen/src/NetTruyen.kt b/multisrc/overrides/wpcomics/nettruyen/src/NetTruyen.kt index d762c8f05e..8858284814 100644 --- a/multisrc/overrides/wpcomics/nettruyen/src/NetTruyen.kt +++ b/multisrc/overrides/wpcomics/nettruyen/src/NetTruyen.kt @@ -9,7 +9,7 @@ import okhttp3.Request import java.text.SimpleDateFormat import java.util.Locale -class NetTruyen : WPComics("NetTruyen", "https://www.nettruyenmax.com", "vi", SimpleDateFormat("dd/MM/yy", Locale.getDefault()), null) { +class NetTruyen : WPComics("NetTruyen", "https://www.nettruyenus.com", "vi", SimpleDateFormat("dd/MM/yy", Locale.getDefault()), null) { override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headersBuilder().add("Referer", baseUrl).build()) override fun getFilterList(): FilterList { diff --git a/multisrc/overrides/wpcomics/nhattruyen/src/NhatTruyen.kt b/multisrc/overrides/wpcomics/nhattruyen/src/NhatTruyen.kt index 87ef107c31..210eb0c087 100644 --- a/multisrc/overrides/wpcomics/nhattruyen/src/NhatTruyen.kt +++ b/multisrc/overrides/wpcomics/nhattruyen/src/NhatTruyen.kt @@ -9,7 +9,7 @@ import okhttp3.Request import java.text.SimpleDateFormat import java.util.Locale -class NhatTruyen : WPComics("NhatTruyen", "https://nhattruyenmin.com", "vi", SimpleDateFormat("dd/MM/yy", Locale.getDefault()), null) { +class NhatTruyen : WPComics("NhatTruyen", "https://nhattruyenplus.com", "vi", SimpleDateFormat("dd/MM/yy", Locale.getDefault()), null) { override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/the-loai?keyword=$query&page=$page", headers) override fun imageRequest(page: Page): Request = GET(page.imageUrl!!, headersBuilder().add("Referer", baseUrl).build()) diff --git a/multisrc/overrides/zeistmanga/datgarscanlation/src/DatGarScanlation.kt b/multisrc/overrides/zeistmanga/datgarscanlation/src/DatGarScanlation.kt index 1f9e3d9602..96ebad5aae 100644 --- a/multisrc/overrides/zeistmanga/datgarscanlation/src/DatGarScanlation.kt +++ b/multisrc/overrides/zeistmanga/datgarscanlation/src/DatGarScanlation.kt @@ -1,39 +1,10 @@ package eu.kanade.tachiyomi.extension.es.datgarscanlation -import eu.kanade.tachiyomi.multisrc.zeistmanga.Language import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga -import org.jsoup.nodes.Document class DatGarScanlation : ZeistManga("DatGarScanlation", "https://datgarscanlation.blogspot.com", "es") { + override val useNewChapterFeed = true override val hasFilters = true - - private val altChapterFeedRegex = """label\s*=\s*'([^']+)'""".toRegex() - private val altScriptSelector = "#latest > script" - - override fun getApiUrl(doc: Document): String { - var chapterRegex = chapterFeedRegex - var script = doc.selectFirst(scriptSelector) - - if (script == null) { - script = doc.selectFirst(altScriptSelector)!! - chapterRegex = altChapterFeedRegex - } - - val feed = chapterRegex - .find(script.html()) - ?.groupValues?.get(1) - ?: throw Exception("Failed to find chapter feed") - - val url = apiUrl(feed) - .addQueryParameter("start-index", "2") // Only get chapters - .addQueryParameter("max-results", "999999") // Get all chapters - .build() - - return url.toString() - } - - override fun getLanguageList(): List = listOf( - Language(intl.all, ""), - ) + override val hasLanguageFilter = false } diff --git a/multisrc/overrides/zeistmanga/hijala/src/Hijala.kt b/multisrc/overrides/zeistmanga/hijala/src/Hijala.kt index a2920a9e8c..0747effa35 100644 --- a/multisrc/overrides/zeistmanga/hijala/src/Hijala.kt +++ b/multisrc/overrides/zeistmanga/hijala/src/Hijala.kt @@ -1,16 +1,12 @@ package eu.kanade.tachiyomi.extension.ar.hijala import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre -import eu.kanade.tachiyomi.multisrc.zeistmanga.Language import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga class Hijala : ZeistManga("Hijala", "https://hijala.blogspot.com", "ar") { override val hasFilters = true - - override fun getLanguageList(): List = listOf( - Language(intl.all, ""), - ) + override val hasLanguageFilter = false override fun getGenreList(): List = listOf( Genre("أكشن", "Action"), diff --git a/multisrc/overrides/zeistmanga/klmanhua/src/KLManhua.kt b/multisrc/overrides/zeistmanga/klmanhua/src/KLManhua.kt index 8b34b50b1c..dc150c0228 100644 --- a/multisrc/overrides/zeistmanga/klmanhua/src/KLManhua.kt +++ b/multisrc/overrides/zeistmanga/klmanhua/src/KLManhua.kt @@ -1,13 +1,9 @@ package eu.kanade.tachiyomi.extension.id.klmanhua -import eu.kanade.tachiyomi.multisrc.zeistmanga.Language import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga class KLManhua : ZeistManga("KLManhua", "https://klmanhua.blogspot.com", "id") { override val hasFilters = true - - override fun getLanguageList(): List = listOf( - Language(intl.all, ""), - ) + override val hasLanguageFilter = false } diff --git a/multisrc/overrides/zeistmanga/mangaailand/src/MangaAiLand.kt b/multisrc/overrides/zeistmanga/mangaailand/src/MangaAiLand.kt index 406781b854..3f87781c2c 100644 --- a/multisrc/overrides/zeistmanga/mangaailand/src/MangaAiLand.kt +++ b/multisrc/overrides/zeistmanga/mangaailand/src/MangaAiLand.kt @@ -1,16 +1,14 @@ package eu.kanade.tachiyomi.extension.ar.mangaailand import eu.kanade.tachiyomi.multisrc.zeistmanga.Genre -import eu.kanade.tachiyomi.multisrc.zeistmanga.Language import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga class MangaAiLand : ZeistManga("Manga Ai Land", "https://manga-ai-land.blogspot.com", "ar") { override val hasFilters = true + override val hasLanguageFilter = false - override fun getLanguageList(): List = listOf( - Language(intl.all, ""), - ) + override val chapterCategory = "فصل" override fun getGenreList(): List = listOf( Genre("تراجيدي", "تراجيدي"), diff --git a/multisrc/overrides/zeistmanga/muslosnosekai/src/MuslosNoSekai.kt b/multisrc/overrides/zeistmanga/muslosnosekai/src/MuslosNoSekai.kt index fbee9f9d99..bd610d3ad7 100644 --- a/multisrc/overrides/zeistmanga/muslosnosekai/src/MuslosNoSekai.kt +++ b/multisrc/overrides/zeistmanga/muslosnosekai/src/MuslosNoSekai.kt @@ -1,13 +1,9 @@ package eu.kanade.tachiyomi.extension.es.muslosnosekai -import eu.kanade.tachiyomi.multisrc.zeistmanga.Language import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga class MuslosNoSekai : ZeistManga("Muslos No Sekai", "https://muslosnosekai.blogspot.com", "es") { override val hasFilters = true - - override fun getLanguageList(): List = listOf( - Language(intl.all, ""), - ) + override val hasLanguageFilter = false } diff --git a/multisrc/overrides/zeistmanga/noromax/src/Noromax.kt b/multisrc/overrides/zeistmanga/noromax/src/Noromax.kt deleted file mode 100644 index 3a535ea84e..0000000000 --- a/multisrc/overrides/zeistmanga/noromax/src/Noromax.kt +++ /dev/null @@ -1,8 +0,0 @@ -package eu.kanade.tachiyomi.extension.id.noromax - -import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga - -class Noromax : ZeistManga("Noromax", "https://www.noromax.xyz", "id") { - - override val hasFilters = true -} diff --git a/multisrc/overrides/zeistmanga/shiyurasub/src/ShiyuraSub.kt b/multisrc/overrides/zeistmanga/shiyurasub/src/ShiyuraSub.kt index 38870cc9e8..313098d16a 100644 --- a/multisrc/overrides/zeistmanga/shiyurasub/src/ShiyuraSub.kt +++ b/multisrc/overrides/zeistmanga/shiyurasub/src/ShiyuraSub.kt @@ -1,13 +1,25 @@ package eu.kanade.tachiyomi.extension.id.shiyurasub -import eu.kanade.tachiyomi.multisrc.zeistmanga.Language import eu.kanade.tachiyomi.multisrc.zeistmanga.ZeistManga +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Response class ShiyuraSub : ZeistManga("ShiyuraSub", "https://shiyurasub.blogspot.com", "id") { override val hasFilters = true + override val hasLanguageFilter = false - override fun getLanguageList(): List = listOf( - Language(intl.all, ""), - ) + override val pageListSelector = "main.content article.container" + + override fun mangaDetailsParse(response: Response): SManga { + val document = response.asJsoup() + val profileManga = document.selectFirst("main.content.post")!! + return SManga.create().apply { + thumbnail_url = profileManga.selectFirst("div.grid img")!!.attr("abs:src") + description = profileManga.select("#synopsis").text() + genre = profileManga.select("div.my-5 > a[rel=tag]") + .joinToString { it.text() } + } + } } diff --git a/multisrc/overrides/zeistmanga/tooncubus/src/Tooncubus.kt b/multisrc/overrides/zeistmanga/tooncubus/src/Tooncubus.kt index fa681425b5..1af3696517 100644 --- a/multisrc/overrides/zeistmanga/tooncubus/src/Tooncubus.kt +++ b/multisrc/overrides/zeistmanga/tooncubus/src/Tooncubus.kt @@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.asJsoup import okhttp3.Response -import org.jsoup.nodes.Document class Tooncubus : ZeistManga("Tooncubus", "https://www.tooncubus.top", "id") { @@ -25,7 +24,8 @@ class Tooncubus : ZeistManga("Tooncubus", "https://www.tooncubus.top", "id") { override fun getChapterUrl(chapter: SChapter) = chapter.url - override fun mangaDetailsParse(document: Document): SManga { + override fun mangaDetailsParse(response: Response): SManga { + val document = response.asJsoup() val profileManga = document.selectFirst(".grid.gtc-235fr")!! return SManga.create().apply { thumbnail_url = profileManga.selectFirst("img")!!.attr("src") diff --git a/multisrc/src/main/AndroidManifest.xml b/multisrc/src/main/AndroidManifest.xml deleted file mode 100644 index 17ee0c14a4..0000000000 --- a/multisrc/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/a3manga/A3MangaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/a3manga/A3MangaGenerator.kt index 9a0500766d..98280cf5fa 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/a3manga/A3MangaGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/a3manga/A3MangaGenerator.kt @@ -13,7 +13,7 @@ class A3MangaGenerator : ThemeSourceGenerator { override val sources = listOf( SingleLang("A3 Manga", "https://www.a3mnga.com", "vi"), - SingleLang("Team Lanh Lung", "https://teamlanhlung.com", "vi", sourceName = "Team Lạnh Lùng"), + SingleLang("Team Lanh Lung", "https://teamlanhlung.vip", "vi", sourceName = "Team Lạnh Lùng", overrideVersionCode = 1), SingleLang("Ngon Phong", "https://www.ngonphong.com", "vi", sourceName = "Ngôn Phong", overrideVersionCode = 1), SingleLang("O Cu Meo", "https://www.ocumeo.com", "vi", sourceName = "Ổ Cú Mèo"), ) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt index ff5325d52b..b271dd90e3 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/bakamanga/BakaMangaGenerator.kt @@ -11,9 +11,6 @@ class BakaMangaGenerator : ThemeSourceGenerator { override val baseVersionCode = 1 override val sources = listOf( - SingleLang("ManhuaManga.net", "https://manhuamanga.net", "en", className = "ManhuaMangaNet", overrideVersionCode = 2), - SingleLang("ManhwaManga.net", "https://manhwamanga.net", "en", isNsfw = true, className = "ManhwaMangaNet", overrideVersionCode = 7), - SingleLang("MWManhwa", "https://mwmanhwa.net", "all", isNsfw = true), SingleLang("Manhwa XXL", "https://manhwaxxl.com", "en", isNsfw = true), ) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScans.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScans.kt new file mode 100644 index 0000000000..cbfb758ab1 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScans.kt @@ -0,0 +1,314 @@ +package eu.kanade.tachiyomi.multisrc.flixscans + +import android.util.Log +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import okhttp3.Call +import okhttp3.Callback +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import rx.Observable +import uy.kohesive.injekt.injectLazy + +abstract class FlixScans( + override val name: String, + override val baseUrl: String, + override val lang: String, + protected val apiUrl: String = baseUrl.replace("://", "://api.").plus("/api/v1"), + protected val cdnUrl: String = baseUrl.replace("://", "://api.").plus("/storage/"), +) : HttpSource() { + + override val supportsLatest = true + + protected open val json: Json by injectLazy() + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(2) + .build() + + // only returns 15 chapters each request, so using higher rate limit + private val chapterClient = network.cloudflareClient.newBuilder() + .rateLimitHost(apiUrl.toHttpUrl(), 1, 2) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", baseUrl) + + override fun fetchPopularManga(page: Int): Observable { + runCatching { fetchGenre() } + + return super.fetchPopularManga(page) + } + + override fun popularMangaRequest(page: Int): Request { + return GET("$apiUrl/webtoon/homepage/home", headers) + } + + override fun popularMangaParse(response: Response): MangasPage { + val result = response.parseAs() + + val entries = (result.hot + result.topAll + result.topMonth + result.topWeek) + .distinctBy { it.id } + .map { it.toSManga(cdnUrl) } + + return MangasPage(entries, false) + } + + override fun fetchLatestUpdates(page: Int): Observable { + runCatching { fetchGenre() } + + return super.fetchLatestUpdates(page) + } + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$apiUrl/search/advance?page=$page&serie_type=webtoon", headers) + } + + override fun latestUpdatesParse(response: Response): MangasPage { + val result = response.parseAs>() + val currentPage = response.request.url.queryParameter("page") + ?.toIntOrNull() ?: 1 + + val entries = result.data.map { it.toSManga(cdnUrl) } + val hasNextPage = result.meta.lastPage > currentPage + + return MangasPage(entries, hasNextPage) + } + + private var fetchGenreList: List = emptyList() + private var fetchGenreCallOngoing = false + private var fetchGenreFailed = false + private var fetchGenreAttempt = 0 + + private fun fetchGenre() { + if (fetchGenreAttempt < 3 && (fetchGenreList.isEmpty() || fetchGenreFailed) && !fetchGenreCallOngoing) { + fetchGenreCallOngoing = true + + // fetch genre asynchronously as it sometimes hangs + client.newCall(fetchGenreRequest()).enqueue(fetchGenreCallback) + } + } + + private val fetchGenreCallback = object : Callback { + override fun onFailure(call: Call, e: okio.IOException) { + fetchGenreAttempt++ + fetchGenreFailed = true + fetchGenreCallOngoing = false + + e.message?.let { Log.e("$name Filters", it) } + } + + override fun onResponse(call: Call, response: Response) { + fetchGenreCallOngoing = false + fetchGenreAttempt++ + + if (!response.isSuccessful) { + fetchGenreFailed = true + response.close() + + return + } + + val parsed = runCatching { + response.use(::fetchGenreParse) + } + + fetchGenreFailed = parsed.isFailure + fetchGenreList = parsed.getOrElse { + Log.e("$name Filters", it.stackTraceToString()) + emptyList() + } + } + } + + private fun fetchGenreRequest(): Request { + return GET("$apiUrl/search/genres", headers) + } + + private fun fetchGenreParse(response: Response): List { + return response.parseAs>() + } + + override fun getFilterList(): FilterList { + val filters: MutableList> = mutableListOf( + Filter.Header("Ignored when using Text Search"), + MainGenreFilter(), + TypeFilter(), + StatusFilter(), + ) + + filters += if (fetchGenreList.isNotEmpty()) { + listOf( + GenreFilter("Genre", fetchGenreList), + ) + } else { + listOf( + Filter.Separator(), + Filter.Header("Press 'reset' to attempt to show Genres"), + ) + } + + return FilterList(filters) + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + runCatching { fetchGenre() } + + return super.fetchSearchManga(page, query, filters) + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + if (query.isNotEmpty()) { + val requestBody = SearchInput(query.trim()) + .let(json::encodeToString) + .toRequestBody(JSON_MEDIA_TYPE) + + val newHeaders = headersBuilder() + .add("Content-Length", requestBody.contentLength().toString()) + .add("Content-Type", requestBody.contentType().toString()) + .build() + + return POST("$apiUrl/search/serie?page=$page", newHeaders, requestBody) + } + + val advSearchUrl = apiUrl.toHttpUrl().newBuilder().apply { + addPathSegments("search/advance") + addQueryParameter("page", page.toString()) + addQueryParameter("serie_type", "webtoon") + + filters.forEach { filter -> + when (filter) { + is GenreFilter -> { + filter.checked.let { + if (it.isNotEmpty()) { + addQueryParameter("genres", it.joinToString(",")) + } + } + } + is MainGenreFilter -> { + if (filter.state > 0) { + addQueryParameter("main_genres", filter.selected) + } + } + is TypeFilter -> { + if (filter.state > 0) { + addQueryParameter("type", filter.selected) + } + } + is StatusFilter -> { + if (filter.state > 0) { + addQueryParameter("status", filter.selected) + } + } + else -> {} + } + } + }.build() + + return GET(advSearchUrl, headers) + } + + override fun searchMangaParse(response: Response) = latestUpdatesParse(response) + + override fun mangaDetailsRequest(manga: SManga): Request { + val id = manga.url.split("-")[1] + + return GET("$apiUrl/webtoon/series/$id", headers) + } + + override fun getMangaUrl(manga: SManga) = baseUrl + manga.url + + override fun mangaDetailsParse(response: Response): SManga { + val result = response.parseAs() + + return result.serie.toSManga(cdnUrl) + } + + override fun fetchChapterList(manga: SManga): Observable> { + return chapterClient.newCall(chapterListRequest(manga)) + .asObservableSuccess() + .map(::chapterListParse) + } + + override fun chapterListRequest(manga: SManga): Request { + val id = manga.url.split("-")[1] + + return paginatedChapterListRequest(id) + } + + private fun paginatedChapterListRequest(seriesID: String, page: Int = 1): Request { + return GET("$apiUrl/webtoon/chapters/$seriesID-asc?page=$page", headers) + } + + override fun chapterListParse(response: Response): List { + val result = response.parseAs>() + + val id = response.request.url.toString() + .substringAfterLast("/") + .substringBefore("-") + + val chapters = result.data.toMutableList() + + var page = 1 + + while (page < result.meta.lastPage) { + page++ + + val newResponse = chapterClient.newCall(paginatedChapterListRequest(id, page)).execute() + + if (!newResponse.isSuccessful) { + newResponse.close() + continue + } + + val newResult = newResponse.parseAs>() + + chapters.addAll(newResult.data) + } + + return chapters.map(Chapter::toSChapter).reversed() + } + + override fun pageListRequest(chapter: SChapter): Request { + val id = chapter.url + .substringAfterLast("/") + .substringBefore("-") + + return GET("$apiUrl/webtoon/chapters/chapter/$id", headers) + } + + override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url + + override fun pageListParse(response: Response): List { + val result = response.parseAs() + + return result.chapter.chapterData.webtoon.mapIndexed { i, img -> + Page(i, "", cdnUrl + img) + } + } + + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not Used") + + private inline fun Response.parseAs(): T = + use { body.string() }.let(json::decodeFromString) + + companion object { + private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansDto.kt new file mode 100644 index 0000000000..637841a38c --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansDto.kt @@ -0,0 +1,146 @@ +package eu.kanade.tachiyomi.multisrc.flixscans + +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import org.jsoup.Jsoup +import java.text.SimpleDateFormat +import java.util.Locale + +@Serializable +data class ApiResponse( + val data: List, + val meta: PageInfo, +) + +@Serializable +data class PageInfo( + @SerialName("last_page") val lastPage: Int, +) + +@Serializable +data class HomeDto( + val hot: List, + val topWeek: List, + val topMonth: List, + val topAll: List, +) + +@Serializable +data class BrowseSeries( + val id: Int, + val title: String, + val slug: String, + val prefix: Int, + val thumbnail: String?, +) { + fun toSManga(cdnUrl: String) = SManga.create().apply { + title = this@BrowseSeries.title + url = "/series/$prefix-$id-$slug" + thumbnail_url = thumbnail?.let { cdnUrl + it } + } +} + +@Serializable +data class SearchInput( + val title: String, +) + +@Serializable +data class GenreHolder( + val name: String, + val id: Int, +) + +@Serializable +data class SeriesResponse( + val serie: Series, +) + +@Serializable +data class Series( + val id: Int, + val title: String, + val slug: String, + val prefix: Int, + val thumbnail: String?, + val story: String?, + val serieType: String?, + val mainGenres: String?, + val otherNames: List? = emptyList(), + val status: String?, + val type: String?, + val authors: List? = emptyList(), + val artists: List? = emptyList(), + val genres: List? = emptyList(), +) { + fun toSManga(cdnUrl: String) = SManga.create().apply { + title = this@Series.title + url = "/series/$prefix-$id-$slug" + thumbnail_url = cdnUrl + thumbnail + author = authors?.joinToString { it.name.trim() } + artist = artists?.joinToString { it.name.trim() } + genre = (otherGenres + genres?.map { it.name.trim() }.orEmpty()) + .distinct().joinToString { it.trim() } + description = story?.let { Jsoup.parse(it).text() } + if (otherNames?.isNotEmpty() == true) { + if (description.isNullOrEmpty()) { + description = "Alternative Names:\n" + } else { + description += "\n\nAlternative Names:\n" + } + description += otherNames.joinToString("\n") { "• ${it.trim()}" } + } + status = when (this@Series.status?.trim()) { + "ongoing" -> SManga.ONGOING + "completed" -> SManga.COMPLETED + "onhold" -> SManga.ON_HIATUS + else -> SManga.UNKNOWN + } + } + + private val otherGenres = listOfNotNull(serieType, mainGenres, type) + .map { word -> + word.trim().replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase(Locale.getDefault()) + } else { + it.toString() + } + } + } +} + +@Serializable +data class Chapter( + val id: Int, + val name: String, + val slug: String, + val createdAt: String? = null, +) { + fun toSChapter() = SChapter.create().apply { + url = "/read/webtoon/$id-$slug" + name = this@Chapter.name + date_upload = runCatching { dateFormat.parse(createdAt!!)!!.time }.getOrDefault(0L) + } + + companion object { + val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'", Locale.ENGLISH) + } +} + +@Serializable +data class PageListResponse( + val chapter: ChapterPages, +) + +@Serializable +data class ChapterPages( + val chapterData: ChapterPageData, +) + +@Serializable +data class ChapterPageData( + val webtoon: List, +) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansGenerator.kt new file mode 100644 index 0000000000..d4d401f077 --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansGenerator.kt @@ -0,0 +1,26 @@ +package eu.kanade.tachiyomi.multisrc.flixscans + +import generator.ThemeSourceData.SingleLang +import generator.ThemeSourceGenerator + +class FlixScansGenerator : ThemeSourceGenerator { + + override val themePkg = "flixscans" + + override val themeClass = "FlixScans" + + override val baseVersionCode: Int = 2 + + override val sources = listOf( + SingleLang("Flix Scans", "https://flixscans.net", "en", className = "FlixScansNet", pkgName = "flixscans"), + SingleLang("جالاكسي مانجا", "https://flixscans.com", "ar", className = "GalaxyManga", overrideVersionCode = 25), + SingleLang("مانجا نون", "https://manjanoon.com", "ar", className = "MangaNoon"), + ) + + companion object { + @JvmStatic + fun main(args: Array) { + FlixScansGenerator().createAll() + } + } +} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansGenre.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansGenre.kt new file mode 100644 index 0000000000..aebbe7813d --- /dev/null +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/flixscans/FlixScansGenre.kt @@ -0,0 +1,62 @@ +package eu.kanade.tachiyomi.multisrc.flixscans + +import eu.kanade.tachiyomi.source.model.Filter + +abstract class SelectFilter( + name: String, + private val options: List, +) : Filter.Select( + name, + options.toTypedArray(), +) { + val selected get() = options[state] +} + +class CheckBoxFilter( + name: String, + val id: String, +) : Filter.CheckBox(name) + +class GenreFilter( + name: String, + private val genres: List, +) : Filter.Group( + name, + genres.map { CheckBoxFilter(it.name.trim(), it.id.toString()) }, +) { + val checked get() = state.filter { it.state }.map { it.id } +} + +class MainGenreFilter : SelectFilter( + "Main Genre", + listOf( + "", + "fantasy", + "romance", + "action", + "drama", + ), +) + +class TypeFilter : SelectFilter( + "Type", + listOf( + "", + "manhwa", + "manhua", + "manga", + "comic", + ), +) + +class StatusFilter : SelectFilter( + "Status", + listOf( + "", + "ongoing", + "completed", + "droped", + "onhold", + "soon", + ), +) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/fmreader/FMReaderGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/fmreader/FMReaderGenerator.kt index 3f9996d177..6ff5091f91 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/fmreader/FMReaderGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/fmreader/FMReaderGenerator.kt @@ -15,14 +15,14 @@ class FMReaderGenerator : ThemeSourceGenerator { override val sources = listOf( MultiLang("Manhwa18.net", "https://manhwa18.net", listOf("en", "ko"), className = "Manhwa18NetFactory", isNsfw = true), SingleLang("Epik Manga", "https://www.epikmanga.com", "tr"), - SingleLang("KissLove", "https://klmanga.com", "ja", overrideVersionCode = 2), + SingleLang("KissLove", "https://klz9.com", "ja", isNsfw = true, overrideVersionCode = 4), SingleLang("Manga-TR", "https://manga-tr.com", "tr", className = "MangaTR", overrideVersionCode = 1), SingleLang("ManhuaRock", "https://manhuarock.net", "vi", overrideVersionCode = 1), SingleLang("Manhwa18", "https://manhwa18.com", "en", isNsfw = true, overrideVersionCode = 2), SingleLang("Say Truyen", "https://saytruyenvip.com", "vi", overrideVersionCode = 3), - SingleLang("WeLoveManga", "https://weloma.art", "ja", pkgName = "rawlh", overrideVersionCode = 4), + SingleLang("WeLoveManga", "https://weloma.art", "ja", pkgName = "rawlh", isNsfw = true, overrideVersionCode = 5), SingleLang("Manga1000", "https://manga1000.top", "ja"), - SingleLang("WeLoveMangaOne", "https://welovemanga.one", "ja"), + SingleLang("WeLoveMangaOne", "https://welovemanga.one", "ja", isNsfw = true, overrideVersionCode = 1), ) companion object { diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLe.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLe.kt index 3c8787a696..3f3b61d608 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLe.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLe.kt @@ -150,14 +150,14 @@ abstract class GroupLe( else -> rawAgeValue } val manga = SManga.create() - manga.title = document.select("h1.names .name").text() + manga.title = document.select(".names > .name").text() manga.author = infoElement.select("span.elem_author").first()?.text() ?: infoElement.select( "span.elem_screenwriter", ).first()?.text() manga.artist = infoElement.select("span.elem_illustrator").first()?.text() manga.genre = ( - "$category, $rawAgeStop, " + infoElement.select("span.elem_genre") - .text() + ", " + infoElement.select("span.elem_tag").text() + "$category, $rawAgeStop, " + infoElement.select("p:contains(Жанры:) a, p:contains(Теги:) a") + .joinToString { it.text() } ).split(", ") .filter { it.isNotEmpty() }.joinToString { it.trim().lowercase() } val altName = if (infoElement.select(".another-names").isNotEmpty()) { @@ -179,6 +179,7 @@ abstract class GroupLe( "продолжается" -> SManga.ONGOING "начат" -> SManga.ONGOING "переведено" -> SManga.COMPLETED + "завершён" -> SManga.COMPLETED "приостановлен" -> SManga.ON_HIATUS else -> SManga.UNKNOWN } @@ -201,7 +202,7 @@ abstract class GroupLe( private fun chapterListParse(response: Response, manga: SManga): List { val document = response.asJsoup() - if ((document.select(".expandable.hide-dn").isNotEmpty() || document.select("img.logo").first()?.attr("title")?.contains("Allhentai") == true) && document.select(".user-avatar").isNullOrEmpty()) { + if ((document.select(".expandable.hide-dn").isNotEmpty() && document.select(".user-avatar").isNullOrEmpty() && document.toString().contains("current_user_country_code = 'RU'")) || (document.select("img.logo").first()?.attr("title")?.contains("Allhentai") == true && document.select(".user-avatar").isNullOrEmpty())) { throw Exception("Для просмотра контента необходима авторизация через WebView\uD83C\uDF0E") } return document.select(chapterListSelector()).map { chapterFromElement(it, manga) } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLeGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLeGenerator.kt index 8d775ba569..3370ae4da2 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLeGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/grouple/GroupLeGenerator.kt @@ -9,7 +9,7 @@ class GroupLeGenerator : ThemeSourceGenerator { override val themeClass = "GroupLe" - override val baseVersionCode = 15 + override val baseVersionCode = 19 override val sources = listOf( SingleLang("ReadManga", "https://readmanga.live", "ru", overrideVersionCode = 46), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt index e0c96d501a..b6171892ac 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCms.kt @@ -1,5 +1,7 @@ package eu.kanade.tachiyomi.multisrc.heancms +import android.app.Application +import android.content.SharedPreferences import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.source.model.Filter @@ -9,16 +11,20 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat import java.util.Locale @@ -29,10 +35,20 @@ abstract class HeanCms( protected val apiUrl: String = baseUrl.replace("://", "://api."), ) : HttpSource() { + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient + protected open val slugStrategy = SlugStrategy.NONE + + protected open val useNewQueryEndpoint = false + + private var seriesSlugMap: Map? = null + /** * Custom Json instance to make usage of `encodeDefaults`, * which is not enabled on the injected instance of the app. @@ -45,24 +61,24 @@ abstract class HeanCms( protected val intl by lazy { HeanCmsIntl(lang) } - protected open val fetchAllTitles: Boolean = false - protected open val coverPath: String = "cover/" protected open val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US) - private var seriesSlugMap: Map? = null - override fun headersBuilder(): Headers.Builder = Headers.Builder() .add("Origin", baseUrl) .add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int): Request { + if (useNewQueryEndpoint) { + return newEndpointPopularMangaRequest(page) + } + val payloadObj = HeanCmsQuerySearchPayloadDto( page = page, order = "desc", orderBy = "total_views", - status = "Ongoing", + status = "All", type = "Comic", ) @@ -76,12 +92,32 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } + protected fun newEndpointPopularMangaRequest(page: Int): Request { + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("query_string", "") + .addQueryParameter("series_status", "All") + .addQueryParameter("order", "desc") + .addQueryParameter("orderBy", "total_views") + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", "12") + .addQueryParameter("tags_ids", "[]") + + return GET(url.build(), headers) + } + override fun popularMangaParse(response: Response): MangasPage { val json = response.body.string() if (json.startsWith("{")) { val result = json.parseAs() - val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } + val mangaList = result.data.map { + if (slugStrategy != SlugStrategy.NONE) { + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { map -> map[it.slug.toPermSlugIfNeeded()] = it.slug } + } + it.toSManga(apiUrl, coverPath, slugStrategy) + } fetchAllTitles() @@ -89,7 +125,13 @@ abstract class HeanCms( } val mangaList = json.parseAs>() - .map { it.toSManga(apiUrl, coverPath) } + .map { + if (slugStrategy != SlugStrategy.NONE) { + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { map -> map[it.slug.toPermSlugIfNeeded()] = it.slug } + } + it.toSManga(apiUrl, coverPath, slugStrategy) + } fetchAllTitles() @@ -97,11 +139,15 @@ abstract class HeanCms( } override fun latestUpdatesRequest(page: Int): Request { + if (useNewQueryEndpoint) { + return newEndpointLatestUpdatesRequest(page) + } + val payloadObj = HeanCmsQuerySearchPayloadDto( page = page, order = "desc", orderBy = "latest", - status = "Ongoing", + status = "All", type = "Comic", ) @@ -115,6 +161,20 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } + protected fun newEndpointLatestUpdatesRequest(page: Int): Request { + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("query_string", "") + .addQueryParameter("series_status", "All") + .addQueryParameter("order", "desc") + .addQueryParameter("orderBy", "latest") + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", "12") + .addQueryParameter("tags_ids", "[]") + + return GET(url.build(), headers) + } + override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { @@ -123,16 +183,38 @@ abstract class HeanCms( } val slug = query.substringAfter(SEARCH_PREFIX) - val manga = SManga.create().apply { url = "/series/$slug" } + val manga = SManga.create().apply { + url = if (slugStrategy != SlugStrategy.NONE) { + val mangaId = getIdBySlug(slug) + "/series/${slug.toPermSlugIfNeeded()}#$mangaId" + } else { + "/series/$slug" + } + } return fetchMangaDetails(manga).map { MangasPage(listOf(it), false) } } + private fun getIdBySlug(slug: String): Int { + val result = runCatching { + val response = client.newCall(GET("$apiUrl/series/$slug", headers)).execute() + val json = response.body.string() + + val seriesDetail = json.parseAs() + + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { it[seriesDetail.slug.toPermSlugIfNeeded()] = seriesDetail.slug } + + seriesDetail.id + } + return result.getOrNull() ?: throw Exception(intl.idNotFoundError + slug) + } + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - /** - * Their query search endpoint doesn't return the thumbnails, so we need to do - * later an special parsing to get the thumbnails as well from the slug map. - */ + if (useNewQueryEndpoint) { + return newEndpointSearchMangaRequest(page, query, filters) + } + if (query.isNotBlank()) { val searchPayloadObj = HeanCmsSearchPayloadDto(query) val searchPayload = json.encodeToString(searchPayloadObj) @@ -170,6 +252,28 @@ abstract class HeanCms( return POST("$apiUrl/series/querysearch", apiHeaders, payload) } + protected fun newEndpointSearchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val sortByFilter = filters.firstInstanceOrNull() + val statusFilter = filters.firstInstanceOrNull() + + val tagIds = filters.firstInstanceOrNull()?.state.orEmpty() + .filter(Genre::state) + .map(Genre::id) + .joinToString(",", prefix = "[", postfix = "]") + + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("query_string", query) + .addQueryParameter("series_status", statusFilter?.selected?.value ?: "All") + .addQueryParameter("order", if (sortByFilter?.state?.ascending == true) "asc" else "desc") + .addQueryParameter("orderBy", sortByFilter?.selected ?: "total_views") + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", "12") + .addQueryParameter("tags_ids", tagIds) + + return GET(url.build(), headers) + } + override fun searchMangaParse(response: Response): MangasPage { val json = response.body.string() @@ -179,14 +283,23 @@ abstract class HeanCms( val result = json.parseAs>() val mangaList = result .filter { it.type == "Comic" } - .map { it.toSManga(apiUrl, coverPath, seriesSlugMap.orEmpty()) } + .map { + it.slug = it.slug.toPermSlugIfNeeded() + it.toSManga(apiUrl, coverPath, seriesSlugMap.orEmpty(), slugStrategy) + } return MangasPage(mangaList, false) } if (json.startsWith("{")) { val result = json.parseAs() - val mangaList = result.data.map { it.toSManga(apiUrl, coverPath) } + val mangaList = result.data.map { + if (slugStrategy != SlugStrategy.NONE) { + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { map -> map[it.slug.toPermSlugIfNeeded()] = it.slug } + } + it.toSManga(apiUrl, coverPath, slugStrategy) + } fetchAllTitles() @@ -194,7 +307,13 @@ abstract class HeanCms( } val mangaList = json.parseAs>() - .map { it.toSManga(apiUrl, coverPath) } + .map { + if (slugStrategy != SlugStrategy.NONE) { + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { map -> map[it.slug.toPermSlugIfNeeded()] = it.slug } + } + it.toSManga(apiUrl, coverPath, slugStrategy) + } fetchAllTitles() @@ -204,17 +323,33 @@ abstract class HeanCms( override fun getMangaUrl(manga: SManga): String { val seriesSlug = manga.url .substringAfterLast("/") - .replace(TIMESTAMP_REGEX, "") + .substringBefore("#") + .toPermSlugIfNeeded() - val currentSlug = seriesSlugMap?.get(seriesSlug)?.slug ?: seriesSlug + val currentSlug = if (slugStrategy != SlugStrategy.NONE) { + preferences.slugMap[seriesSlug] ?: seriesSlug + } else { + seriesSlug + } return "$baseUrl/series/$currentSlug" } override fun mangaDetailsRequest(manga: SManga): Request { + if (slugStrategy != SlugStrategy.NONE && (manga.url.contains(TIMESTAMP_REGEX))) { + throw Exception(intl.urlChangedError(name)) + } + + if (slugStrategy == SlugStrategy.ID && !manga.url.contains("#")) { + throw Exception(intl.urlChangedError(name)) + } + val seriesSlug = manga.url .substringAfterLast("/") - .replace(TIMESTAMP_REGEX, "") + .substringBefore("#") + .toPermSlugIfNeeded() + + val seriesId = manga.url.substringAfterLast("#") fetchAllTitles() @@ -226,17 +361,30 @@ abstract class HeanCms( .add("Accept", ACCEPT_JSON) .build() - return GET("$apiUrl/series/$currentSlug#$currentStatus", apiHeaders) + return if (slugStrategy == SlugStrategy.ID) { + GET("$apiUrl/series/id/$seriesId", apiHeaders) + } else { + GET("$apiUrl/series/$currentSlug#$currentStatus", apiHeaders) + } } override fun mangaDetailsParse(response: Response): SManga { + val mangaStatus = response.request.url.fragment?.toIntOrNull() ?: SManga.UNKNOWN + val result = runCatching { response.parseAs() } - val seriesDetails = result.getOrNull()?.toSManga(apiUrl, coverPath) - ?: throw Exception(intl.urlChangedError(name)) + + val seriesResult = result.getOrNull() ?: throw Exception(intl.urlChangedError(name)) + + if (slugStrategy != SlugStrategy.NONE) { + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { it[seriesResult.slug.toPermSlugIfNeeded()] = seriesResult.slug } + } + + val seriesDetails = seriesResult.toSManga(apiUrl, coverPath, slugStrategy) return seriesDetails.apply { status = status.takeUnless { it == SManga.UNKNOWN } - ?: response.request.url.fragment?.toIntOrNull() ?: SManga.UNKNOWN + ?: mangaStatus } } @@ -244,19 +392,54 @@ abstract class HeanCms( override fun chapterListParse(response: Response): List { val result = response.parseAs() - val seriesSlug = response.request.url.pathSegments.last() + + if (slugStrategy == SlugStrategy.ID) { + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { it[result.slug.toPermSlugIfNeeded()] = result.slug } + } + val currentTimestamp = System.currentTimeMillis() + if (useNewQueryEndpoint) { + return result.seasons.orEmpty() + .flatMap { it.chapters.orEmpty() } + .filterNot { it.price == 1 } + .map { it.toSChapter(result.slug, dateFormat, slugStrategy) } + .filter { it.date_upload <= currentTimestamp } + } + return result.chapters.orEmpty() .filterNot { it.price == 1 } - .map { it.toSChapter(seriesSlug, dateFormat) } + .map { it.toSChapter(result.slug, dateFormat, slugStrategy) } .filter { it.date_upload <= currentTimestamp } .reversed() } - override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url + override fun getChapterUrl(chapter: SChapter): String { + if (slugStrategy == SlugStrategy.NONE) return baseUrl + chapter.url + + val seriesSlug = chapter.url + .substringAfter("/series/") + .substringBefore("/") + .toPermSlugIfNeeded() + + val currentSlug = preferences.slugMap[seriesSlug] ?: seriesSlug + val chapterUrl = chapter.url.replaceFirst(seriesSlug, currentSlug) + + return baseUrl + chapterUrl + } override fun pageListRequest(chapter: SChapter): Request { + if (useNewQueryEndpoint) { + if (slugStrategy != SlugStrategy.NONE) { + val seriesPermSlug = chapter.url.substringAfter("/series/").substringBefore("/") + val seriesSlug = preferences.slugMap[seriesPermSlug] ?: seriesPermSlug + val chapterUrl = chapter.url.replaceFirst(seriesPermSlug, seriesSlug) + return GET(baseUrl + chapterUrl, headers) + } + return GET(baseUrl + chapter.url, headers) + } + val chapterId = chapter.url.substringAfterLast("#") val apiHeaders = headersBuilder() @@ -267,6 +450,17 @@ abstract class HeanCms( } override fun pageListParse(response: Response): List { + if (useNewQueryEndpoint) { + val document = response.asJsoup() + + val images = document.selectFirst("div.min-h-screen > div.container > p.items-center") + + return images?.select("img").orEmpty().mapIndexed { i, img -> + val imageUrl = if (img.hasClass("lazy")) img.absUrl("data-src") else img.absUrl("src") + Page(i, "", imageUrl) + } + } + return response.parseAs().content?.images.orEmpty() .filterNot { imageUrl -> // Their image server returns HTTP 403 for hidden files that starts @@ -293,23 +487,8 @@ abstract class HeanCms( return GET(page.imageUrl!!, imageHeaders) } - protected open fun getStatusList(): List = listOf( - Status(intl.statusOngoing, "Ongoing"), - Status(intl.statusOnHiatus, "Hiatus"), - Status(intl.statusDropped, "Dropped"), - ) - - protected open fun getSortProperties(): List = listOf( - SortProperty(intl.sortByTitle, "title"), - SortProperty(intl.sortByViews, "total_views"), - SortProperty(intl.sortByLatest, "latest"), - SortProperty(intl.sortByRecentlyAdded, "recently_added"), - ) - - protected open fun getGenreList(): List = emptyList() - protected open fun fetchAllTitles() { - if (!seriesSlugMap.isNullOrEmpty() || !fetchAllTitles) { + if (!seriesSlugMap.isNullOrEmpty() || slugStrategy != SlugStrategy.FETCH_ALL) { return } @@ -338,9 +517,20 @@ abstract class HeanCms( } seriesSlugMap = result.getOrNull() + preferences.slugMap = preferences.slugMap.toMutableMap() + .also { it.putAll(seriesSlugMap.orEmpty().mapValues { (_, v) -> v.slug }) } } protected open fun allTitlesRequest(page: Int): Request { + if (useNewQueryEndpoint) { + val url = "$apiUrl/query".toHttpUrl().newBuilder() + .addQueryParameter("series_type", "Comic") + .addQueryParameter("page", page.toString()) + .addQueryParameter("perPage", PER_PAGE_MANGA_TITLES.toString()) + + return GET(url.build(), headers) + } + val payloadObj = HeanCmsQuerySearchPayloadDto( page = page, order = "desc", @@ -373,6 +563,49 @@ abstract class HeanCms( ) } + /** + * Used to store the current slugs for sources that change it periodically and for the + * search that doesn't return the thumbnail URLs. + */ + data class HeanCmsTitle(val slug: String, val thumbnailFileName: String, val status: Int) + + /** + * Used to specify the strategy to use when fetching the slug for a manga. + * This is needed because some sources change the slug periodically. + * [NONE]: Use series_slug without changes. + * [ID]: Use series_id to fetch the slug from the API. + * IMPORTANT: [ID] is only available in the new query endpoint. + * [FETCH_ALL]: Convert the slug to a permanent slug by removing the timestamp. + * At extension start, all the slugs are fetched and stored in a map. + */ + enum class SlugStrategy { + NONE, ID, FETCH_ALL + } + + private fun String.toPermSlugIfNeeded(): String { + return if (slugStrategy != SlugStrategy.NONE) { + this.replace(TIMESTAMP_REGEX, "") + } else { + this + } + } + + protected open fun getStatusList(): List = listOf( + Status(intl.statusAll, "All"), + Status(intl.statusOngoing, "Ongoing"), + Status(intl.statusOnHiatus, "Hiatus"), + Status(intl.statusDropped, "Dropped"), + ) + + protected open fun getSortProperties(): List = listOf( + SortProperty(intl.sortByTitle, "title"), + SortProperty(intl.sortByViews, "total_views"), + SortProperty(intl.sortByLatest, "latest"), + SortProperty(intl.sortByRecentlyAdded, "recently_added"), + ) + + protected open fun getGenreList(): List = emptyList() + override fun getFilterList(): FilterList { val genres = getGenreList() @@ -386,20 +619,26 @@ abstract class HeanCms( return FilterList(filters) } - private inline fun Response.parseAs(): T = use { + protected inline fun Response.parseAs(): T = use { it.body.string().parseAs() } - private inline fun String.parseAs(): T = json.decodeFromString(this) + protected inline fun String.parseAs(): T = json.decodeFromString(this) - private inline fun List<*>.firstInstanceOrNull(): R? = + protected inline fun List<*>.firstInstanceOrNull(): R? = filterIsInstance().firstOrNull() - /** - * Used to store the current slugs for sources that change it periodically and for the - * search that doesn't return the thumbnail URLs. - */ - data class HeanCmsTitle(val slug: String, val thumbnailFileName: String, val status: Int) + protected var SharedPreferences.slugMap: MutableMap + get() { + val jsonMap = getString(PREF_URL_MAP_SLUG, "{}")!! + val slugMap = runCatching { json.decodeFromString>(jsonMap) } + return slugMap.getOrNull()?.toMutableMap() ?: mutableMapOf() + } + set(newSlugMap) { + edit() + .putString(PREF_URL_MAP_SLUG, json.encodeToString(newSlugMap)) + .commit() + } companion object { private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" @@ -407,8 +646,12 @@ abstract class HeanCms( private val JSON_MEDIA_TYPE = "application/json".toMediaType() - val TIMESTAMP_REGEX = "-\\d+$".toRegex() + val TIMESTAMP_REGEX = """-\d{13}$""".toRegex() + + private const val PER_PAGE_MANGA_TITLES = 10000 const val SEARCH_PREFIX = "slug:" + + private const val PREF_URL_MAP_SLUG = "pref_url_map" } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt index 72086cf7c3..f9c2578c4b 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsDto.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.multisrc.heancms +import eu.kanade.tachiyomi.multisrc.heancms.HeanCms.SlugStrategy import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName @@ -26,7 +27,7 @@ data class HeanCmsQuerySearchMetaDto( @Serializable data class HeanCmsSearchDto( val description: String? = null, - @SerialName("series_slug") val slug: String, + @SerialName("series_slug") var slug: String, @SerialName("series_type") val type: String, val title: String, val thumbnail: String? = null, @@ -36,10 +37,10 @@ data class HeanCmsSearchDto( apiUrl: String, coverPath: String, slugMap: Map, + slugStrategy: SlugStrategy, ): SManga = SManga.create().apply { - val slugOnly = slug.replace(HeanCms.TIMESTAMP_REGEX, "") + val slugOnly = slug.toPermSlugIfNeeded(slugStrategy) val thumbnailFileName = slugMap[slugOnly]?.thumbnailFileName - title = this@HeanCmsSearchDto.title thumbnail_url = thumbnail?.toAbsoluteThumbnailUrl(apiUrl, coverPath) ?: thumbnailFileName?.toAbsoluteThumbnailUrl(apiUrl, coverPath) @@ -60,10 +61,16 @@ data class HeanCmsSeriesDto( val title: String, val tags: List? = emptyList(), val chapters: List? = emptyList(), + val seasons: List? = emptyList(), ) { - fun toSManga(apiUrl: String, coverPath: String): SManga = SManga.create().apply { + fun toSManga( + apiUrl: String, + coverPath: String, + slugStrategy: SlugStrategy, + ): SManga = SManga.create().apply { val descriptionBody = this@HeanCmsSeriesDto.description?.let(Jsoup::parseBodyFragment) + val slugOnly = slug.toPermSlugIfNeeded(slugStrategy) title = this@HeanCmsSeriesDto.title author = this@HeanCmsSeriesDto.author?.trim() @@ -77,10 +84,20 @@ data class HeanCmsSeriesDto( thumbnail_url = thumbnail.ifEmpty { null } ?.toAbsoluteThumbnailUrl(apiUrl, coverPath) status = this@HeanCmsSeriesDto.status?.toStatus() ?: SManga.UNKNOWN - url = "/series/${slug.replace(HeanCms.TIMESTAMP_REGEX, "")}" + url = if (slugStrategy != SlugStrategy.NONE) { + "/series/$slugOnly#$id" + } else { + "/series/$slug" + } } } +@Serializable +data class HeanCmsSeasonsDto( + val index: Int, + val chapters: List? = emptyList(), +) + @Serializable data class HeanCmsTagDto(val name: String) @@ -93,12 +110,16 @@ data class HeanCmsChapterDto( @SerialName("created_at") val createdAt: String, val price: Int? = null, ) { - - fun toSChapter(seriesSlug: String, dateFormat: SimpleDateFormat): SChapter = SChapter.create().apply { + fun toSChapter( + seriesSlug: String, + dateFormat: SimpleDateFormat, + slugStrategy: SlugStrategy, + ): SChapter = SChapter.create().apply { + val seriesSlugOnly = seriesSlug.toPermSlugIfNeeded(slugStrategy) name = this@HeanCmsChapterDto.name.trim() date_upload = runCatching { dateFormat.parse(createdAt)?.time } .getOrNull() ?: 0L - url = "/series/$seriesSlug/$slug#$id" + url = "/series/$seriesSlugOnly/$slug#$id" } } @@ -129,6 +150,14 @@ private fun String.toAbsoluteThumbnailUrl(apiUrl: String, coverPath: String): St return if (startsWith("https://")) this else "$apiUrl/$coverPath$this" } +private fun String.toPermSlugIfNeeded(slugStrategy: SlugStrategy): String { + return if (slugStrategy != SlugStrategy.NONE) { + this.replace(HeanCms.TIMESTAMP_REGEX, "") + } else { + this + } +} + fun String.toStatus(): Int = when (this) { "Ongoing" -> SManga.ONGOING "Hiatus" -> SManga.ON_HIATUS diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt index 840a468aa8..d78723d934 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsGenerator.kt @@ -9,13 +9,14 @@ class HeanCmsGenerator : ThemeSourceGenerator { override val themeClass = "HeanCms" - override val baseVersionCode: Int = 13 + override val baseVersionCode: Int = 18 override val sources = listOf( SingleLang("Glorious Scan", "https://gloriousscan.com", "pt-BR", overrideVersionCode = 17), SingleLang("Omega Scans", "https://omegascans.org", "en", isNsfw = true, overrideVersionCode = 17), - SingleLang("Reaper Scans", "https://reaperscans.net", "pt-BR", overrideVersionCode = 35), - SingleLang("YugenMangas", "https://yugenmangas.net", "es", isNsfw = true, overrideVersionCode = 4), + SingleLang("Perf Scan", "https://perf-scan.fr", "fr"), + SingleLang("Reaper Scans", "https://reaperscans.net", "pt-BR", overrideVersionCode = 36), + SingleLang("YugenMangas", "https://yugenmangas.net", "es", isNsfw = true, overrideVersionCode = 8), ) companion object { diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsIntl.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsIntl.kt index 433a13b0fb..5b00741455 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsIntl.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/heancms/HeanCmsIntl.kt @@ -16,6 +16,12 @@ class HeanCmsIntl(lang: String) { else -> "Status" } + val statusAll: String = when (availableLang) { + BRAZILIAN_PORTUGUESE -> "Todos" + SPANISH -> "Todos" + else -> "All" + } + val statusOngoing: String = when (availableLang) { BRAZILIAN_PORTUGUESE -> "Em andamento" SPANISH -> "En curso" @@ -82,6 +88,12 @@ class HeanCmsIntl(lang: String) { "to $sourceName to update the URL." } + val idNotFoundError: String = when (availableLang) { + BRAZILIAN_PORTUGUESE -> "Falha ao obter o ID do slug: " + SPANISH -> "No se pudo encontrar el ID para: " + else -> "Failed to get the ID for slug: " + } + companion object { const val BRAZILIAN_PORTUGUESE = "pt-BR" const val ENGLISH = "en" diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt index 0585c1f06b..a64a8ac7a7 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/Kemono.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.multisrc.kemono import android.app.Application import androidx.preference.ListPreference import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.ConfigurableSource @@ -20,7 +21,6 @@ import okhttp3.Callback import okhttp3.Request import okhttp3.Response import okio.blackholeSink -import org.jsoup.nodes.Element import org.jsoup.select.Evaluator import rx.Observable import uy.kohesive.injekt.Injekt @@ -32,12 +32,12 @@ import kotlin.math.min open class Kemono( override val name: String, - override val baseUrl: String, + private val defaultUrl: String, override val lang: String = "all", ) : HttpSource(), ConfigurableSource { override val supportsLatest = true - private val isNewDesign get() = name == "Kemono" + private val mirrorUrls get() = arrayOf(defaultUrl, defaultUrl.removeSuffix(".party") + ".su") override val client = network.client.newBuilder().rateLimit(2).build() @@ -46,47 +46,33 @@ open class Kemono( private val json: Json by injectLazy() - private val preferences by lazy { + private val preferences = Injekt.get().getSharedPreferences("source_$id", 0x0000) - } - override fun popularMangaRequest(page: Int): Request = - GET("$baseUrl/artists?o=${PAGE_SIZE * (page - 1)}", headers) - - override fun popularMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - val cardList = document.selectFirst(Evaluator.Class("card-list"))!! - val creators = cardList.select(Evaluator.Tag("article")).map { - val children = it.children() - val avatar = children[0].selectFirst(Evaluator.Tag("img"))!!.attr("src") - val link = children[1].child(0) - val service = children[2].ownText() - SManga.create().apply { - url = link.attr("href") - title = link.ownText() - author = service - thumbnail_url = baseUrl + avatar - description = PROMPT - initialized = true - } - }.filterUnsupported() - return MangasPage(creators, document.hasNextPage()) - } + override val baseUrl = preferences.getString(BASE_URL_PREF, defaultUrl)!! + + private val imgCdnUrl = when (name) { + "Kemono" -> baseUrl + else -> defaultUrl + }.replace("//", "//img.") - override fun latestUpdatesRequest(page: Int): Request = - GET("$baseUrl/artists/updated?o=${PAGE_SIZE * (page - 1)}", headers) + private fun String.formatAvatarUrl(): String = removePrefix("https://").replaceBefore('/', imgCdnUrl) - override fun latestUpdatesParse(response: Response) = popularMangaParse(response) + override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException() + + override fun popularMangaParse(response: Response) = throw UnsupportedOperationException() + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException() + + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException() override fun fetchPopularManga(page: Int): Observable { - if (!isNewDesign) return super.fetchPopularManga(page) return Observable.fromCallable { fetchNewDesignListing(page, "/artists", compareByDescending { it.favorited }) } } override fun fetchLatestUpdates(page: Int): Observable { - if (!isNewDesign) return super.fetchLatestUpdates(page) return Observable.fromCallable { fetchNewDesignListing(page, "/artists/updated", compareByDescending { it.updatedDate }) } @@ -106,7 +92,7 @@ open class Kemono( url = it.attr("href") title = it.selectFirst(Evaluator.Class("user-card__name"))!!.ownText() author = it.selectFirst(Evaluator.Class("user-card__service"))!!.ownText() - thumbnail_url = baseUrl + it.selectFirst(Evaluator.Tag("img"))!!.attr("src") + thumbnail_url = it.selectFirst(Evaluator.Tag("img"))!!.absUrl("src").formatAvatarUrl() description = PROMPT initialized = true } @@ -135,14 +121,14 @@ open class Kemono( page: Int, block: (ArrayList) -> List, ): MangasPage { - val baseUrl = this.baseUrl + val imgCdnUrl = this.imgCdnUrl val response = client.newCall(GET("$baseUrl/api/creators", headers)).execute() val allCreators = block(response.parseAs()) val count = allCreators.size val fromIndex = (page - 1) * NEW_PAGE_SIZE val toIndex = min(count, fromIndex + NEW_PAGE_SIZE) val creators = allCreators.subList(fromIndex, toIndex) - .map { it.toSManga(baseUrl) } + .map { it.toSManga(imgCdnUrl) } .filterUnsupported() return MangasPage(creators, toIndex < count) } @@ -160,12 +146,15 @@ open class Kemono( client.newCall(GET("$baseUrl/api/creators", headers)).enqueue(callback) } - override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not used.") - override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException() + override fun searchMangaParse(response: Response) = throw UnsupportedOperationException() - override fun fetchMangaDetails(manga: SManga): Observable = Observable.just(manga) + override fun fetchMangaDetails(manga: SManga): Observable { + manga.thumbnail_url = manga.thumbnail_url!!.formatAvatarUrl() + return Observable.just(manga) + } - override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun mangaDetailsParse(response: Response) = throw UnsupportedOperationException() override fun fetchChapterList(manga: SManga): Observable> = Observable.fromCallable { KemonoPostDto.dateFormat.timeZone = when (manga.author) { @@ -187,7 +176,7 @@ open class Kemono( result } - override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun chapterListParse(response: Response) = throw UnsupportedOperationException() override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl/api${chapter.url}", headers) @@ -197,7 +186,19 @@ open class Kemono( return post[0].images.mapIndexed { i, path -> Page(i, imageUrl = baseUrl + path) } } - override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.") + override fun imageRequest(page: Page): Request { + val imageUrl = page.imageUrl!! + if (!preferences.getBoolean(USE_LOW_RES_IMG, false)) return GET(imageUrl, headers) + val index = imageUrl.indexOf('/', startIndex = 8) // https:// + val url = buildString { + append(imageUrl, 0, index) + append("/thumbnail") + append(imageUrl, index, imageUrl.length) + } + return GET(url, headers) + } + + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException() private inline fun Response.parseAs(): T = use { json.decodeFromStream(it.body.byteStream()) @@ -214,10 +215,25 @@ open class Kemono( }.toTypedArray() setDefaultValue(POST_PAGES_DEFAULT) }.let { screen.addPreference(it) } + + ListPreference(screen.context).apply { + key = BASE_URL_PREF + title = "Mirror URL" + summary = "%s\nRequires app restart to take effect" + entries = mirrorUrls + entryValues = mirrorUrls + setDefaultValue(defaultUrl) + }.let(screen::addPreference) + + SwitchPreferenceCompat(screen.context).apply { + key = USE_LOW_RES_IMG + title = "Use low resolution images" + summary = "Reduce load time significantly. When turning off, clear chapter cache to remove cached low resolution images." + setDefaultValue(false) + }.let(screen::addPreference) } companion object { - private const val PAGE_SIZE = 25 private const val NEW_PAGE_SIZE = 50 const val PROMPT = "You can change how many posts to load in the extension preferences." @@ -226,11 +242,9 @@ open class Kemono( private const val POST_PAGES_DEFAULT = "1" private const val POST_PAGES_MAX = 50 - private fun Element.hasNextPage(): Boolean { - val pagination = selectFirst(Evaluator.Class("paginator"))!! - return pagination.selectFirst("a[title=Next page]") != null - } - private fun List.filterUnsupported() = filterNot { it.author == "Discord" } + + private const val BASE_URL_PREF = "BASE_URL" + private const val USE_LOW_RES_IMG = "USE_LOW_RES_IMG" } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt index 4a7bcbc4e4..90b4320958 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoDto.kt @@ -21,11 +21,11 @@ class KemonoCreatorDto( else -> (updated.double * 1000).toLong() } - fun toSManga(baseUrl: String) = SManga.create().apply { + fun toSManga(imgCdnUrl: String) = SManga.create().apply { url = "/$service/user/$id" // should be /server/ for Discord but will be filtered anyway title = name author = service.serviceName() - thumbnail_url = "$baseUrl/icons/$service/$id" + thumbnail_url = "$imgCdnUrl/icons/$service/$id" description = Kemono.PROMPT initialized = true } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt index 8fdc9c010f..2043a0e90d 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/kemono/KemonoGenerator.kt @@ -9,7 +9,7 @@ class KemonoGenerator : ThemeSourceGenerator { override val themePkg = "kemono" - override val baseVersionCode = 5 + override val baseVersionCode = 7 override val sources = listOf( SingleLang("Kemono", "https://kemono.party", "all", isNsfw = true), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGenerator.kt index b76e5389ba..37628a4752 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGenerator.kt @@ -9,7 +9,7 @@ class LibGenerator : ThemeSourceGenerator { override val themeClass = "LibGroup" - override val baseVersionCode: Int = 21 + override val baseVersionCode: Int = 22 override val sources = listOf( SingleLang("MangaLib", "https://mangalib.me", "ru", overrideVersionCode = 74), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGroup.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGroup.kt index 558d7a45dc..3a316b15fc 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGroup.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/libgroup/LibGroup.kt @@ -410,7 +410,7 @@ abstract class LibGroup( chapter.scanlator = if (teams?.size == 1) teams[0].jsonObject["name"]?.jsonPrimitive?.content else if (isScanlatorId.orEmpty().isNotEmpty()) isScanlatorId!![0].jsonObject["name"]?.jsonPrimitive?.content else branches?.let { getScanlatorTeamName(it, chapterItem) } ?: if ((preferences.getBoolean(isScan_USER, false)) || (chaptersList?.distinctBy { it.jsonObject["username"]!!.jsonPrimitive.content }?.size == 1)) chapterItem.jsonObject["username"]!!.jsonPrimitive.content else null chapter.name = if (nameChapter.isNullOrBlank()) fullNameChapter else "$fullNameChapter - $nameChapter" chapter.date_upload = simpleDateFormat.parse(chapterItem.jsonObject["chapter_created_at"]!!.jsonPrimitive.content.substringBefore(" "))?.time ?: 0L - chapter.chapter_number = number.toFloat() + chapter.chapter_number = number.toFloatOrNull() ?: -1f return chapter } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt index 249fd7724b..87a14ffd8b 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/Madara.kt @@ -3,12 +3,12 @@ package eu.kanade.tachiyomi.multisrc.madara import android.app.Application import android.content.SharedPreferences import android.util.Base64 -import android.util.Log -import android.widget.Toast -import androidx.preference.EditTextPreference import androidx.preference.PreferenceScreen -import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES +import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen +import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA +import eu.kanade.tachiyomi.lib.randomua.getPrefUAType +import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservable @@ -21,16 +21,13 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import okhttp3.CacheControl import okhttp3.FormBody -import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -40,7 +37,6 @@ import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy -import java.io.IOException import java.text.ParseException import java.text.SimpleDateFormat import java.util.Calendar @@ -60,88 +56,19 @@ abstract class Madara( override val supportsLatest = true - // override with true if you want useRandomUserAgentByDefault to be on by default for some source - protected open val useRandomUserAgentByDefault: Boolean = false - - /** - * override include/exclude user-agent string if needed - * some example: - * listOf("chrome") - * listOf("linux", "windows") - * listOf("108") - */ - protected open val filterIncludeUserAgent: List = listOf() - protected open val filterExcludeUserAgent: List = listOf() - - private var userAgent: String? = null - private var checkedUa = false - - private val hasUaIntercept by lazy { - client.interceptors.toString().contains("uaIntercept") - } - - protected val uaIntercept = object : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val useRandomUa = preferences.getBoolean(PREF_KEY_RANDOM_UA, false) - val customUa = preferences.getString(PREF_KEY_CUSTOM_UA, "") - - try { - if (hasUaIntercept && (useRandomUa || customUa!!.isNotBlank())) { - Log.i("Extension_setting", "$TITLE_RANDOM_UA or $TITLE_CUSTOM_UA option is ENABLED") - - if (customUa!!.isNotBlank() && useRandomUa.not()) { - userAgent = customUa - } - - if (userAgent.isNullOrBlank() && !checkedUa) { - val uaResponse = chain.proceed(GET(UA_DB_URL)) - - if (uaResponse.isSuccessful) { - var listUserAgentString = - json.decodeFromString>>(uaResponse.body.string())["desktop"] - - if (filterIncludeUserAgent.isNotEmpty()) { - listUserAgentString = listUserAgentString!!.filter { - filterIncludeUserAgent.any { filter -> - it.contains(filter, ignoreCase = true) - } - } - } - if (filterExcludeUserAgent.isNotEmpty()) { - listUserAgentString = listUserAgentString!!.filterNot { - filterExcludeUserAgent.any { filter -> - it.contains(filter, ignoreCase = true) - } - } - } - userAgent = listUserAgentString!!.random() - checkedUa = true - } - - uaResponse.close() - } - - if (userAgent.isNullOrBlank().not()) { - val newRequest = chain.request().newBuilder() - .header("User-Agent", userAgent!!.trim()) - .build() - - return chain.proceed(newRequest) - } - } - - return chain.proceed(chain.request()) - } catch (e: Exception) { - throw IOException(e.message) - } - } + override val client: OkHttpClient by lazy { + network.cloudflareClient.newBuilder() + .setRandomUserAgent( + preferences.getPrefUAType(), + preferences.getPrefCustomUA(), + ) + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() } - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(uaIntercept) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .build() + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") protected open val json: Json by injectLazy() @@ -187,9 +114,6 @@ abstract class Madara( */ protected open val mangaSubString = "manga" - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("Referer", "$baseUrl/") - // Popular Manga override fun popularMangaParse(response: Response): MangasPage { @@ -1017,57 +941,6 @@ abstract class Madara( } } - override fun setupPreferenceScreen(screen: PreferenceScreen) { - if (hasUaIntercept) { - val prefUserAgent = SwitchPreferenceCompat(screen.context).apply { - key = PREF_KEY_RANDOM_UA - title = TITLE_RANDOM_UA - summary = if (preferences.getBoolean(PREF_KEY_RANDOM_UA, useRandomUserAgentByDefault)) userAgent else "" - setDefaultValue(useRandomUserAgentByDefault) - - setOnPreferenceChangeListener { _, newValue -> - val useRandomUa = newValue as Boolean - preferences.edit().putBoolean(PREF_KEY_RANDOM_UA, useRandomUa).apply() - if (!useRandomUa) { - Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show() - } else { - userAgent = null - if (preferences.getString(PREF_KEY_CUSTOM_UA, "").isNullOrBlank().not()) { - Toast.makeText(screen.context, SUMMARY_CLEANING_CUSTOM_UA, Toast.LENGTH_LONG).show() - } - } - - preferences.edit().putString(PREF_KEY_CUSTOM_UA, "").apply() - // prefCustomUserAgent.summary = "" - true - } - } - screen.addPreference(prefUserAgent) - - val prefCustomUserAgent = EditTextPreference(screen.context).apply { - key = PREF_KEY_CUSTOM_UA - title = TITLE_CUSTOM_UA - summary = preferences.getString(PREF_KEY_CUSTOM_UA, "")!!.trim() - setOnPreferenceChangeListener { _, newValue -> - val customUa = newValue as String - preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply() - if (customUa.isBlank()) { - Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show() - } else { - userAgent = null - } - summary = customUa.trim() - prefUserAgent.summary = "" - prefUserAgent.isChecked = false - true - } - } - screen.addPreference(prefCustomUserAgent) - } else { - Toast.makeText(screen.context, DOESNOT_SUPPORT_STRING, Toast.LENGTH_LONG).show() - } - } - // https://stackoverflow.com/a/66614516 private fun String.decodeHex(): ByteArray { check(length % 2 == 0) { "Must have an even length" } @@ -1077,20 +950,12 @@ abstract class Madara( .toByteArray() } - companion object { - const val TITLE_RANDOM_UA = "Use Random Latest User-Agent" - const val PREF_KEY_RANDOM_UA = "pref_key_random_ua" - - const val TITLE_CUSTOM_UA = "Custom User-Agent" - const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua" - - const val SUMMARY_CLEANING_CUSTOM_UA = "$TITLE_CUSTOM_UA cleared." + override fun setupPreferenceScreen(screen: PreferenceScreen) { + addRandomUAPreferenceToScreen(screen) + } - const val RESTART_APP_STRING = "Restart Tachiyomi to apply new setting." - const val DOESNOT_SUPPORT_STRING = "This extension doesn't support User-Agent options." + companion object { const val URL_SEARCH_PREFIX = "slug:" - private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json" - val SALTED = "Salted__".toByteArray(Charsets.UTF_8) } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt index 76679aee87..75ccb150fb 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/madara/MadaraGenerator.kt @@ -10,7 +10,7 @@ class MadaraGenerator : ThemeSourceGenerator { override val themeClass = "Madara" - override val baseVersionCode: Int = 30 + override val baseVersionCode: Int = 31 override val sources = listOf( @@ -31,45 +31,48 @@ class MadaraGenerator : ThemeSourceGenerator { // Extenções Oficiais: MultiLang("Atlantis Scan", "https://atlantisscan.com", listOf("es", "pt-BR"), isNsfw = true), - MultiLang("Leviatan Scans", "https://leviatanscans.com", listOf("en", "es"), className = "LeviatanScansFactory", overrideVersionCode = 14), MultiLang("MangaForFree.net", "https://mangaforfree.net", listOf("en", "ko", "all"), isNsfw = true, className = "MangaForFreeFactory", pkgName = "mangaforfree", overrideVersionCode = 1), MultiLang("Manhwa18.cc", "https://manhwa18.cc", listOf("en", "ko", "all"), isNsfw = true, className = "Manhwa18CcFactory", pkgName = "manhwa18cc", overrideVersionCode = 4), MultiLang("Reaper Scans", "https://reaperscans.com", listOf("fr", "tr"), className = "ReaperScansFactory", pkgName = "reaperscans", overrideVersionCode = 12), - SingleLang("1st Kiss Manga.love", "https://1stkissmanga.love", "en", className = "FirstKissMangaLove", overrideVersionCode = 2), - SingleLang("1st Kiss Manhua", "https://1stkissmanhua.com", "en", className = "FirstKissManhua", overrideVersionCode = 4), - SingleLang("1st Kiss", "https://1stkissmanga.me", "en", className = "FirstKissManga", pkgName = "firstkissmanga", overrideVersionCode = 9), + SingleLang("1st Kiss-Manga (unoriginal)", "https://1stkiss-manga.com", "en", isNsfw = false, className = "FirstKissDashManga"), + SingleLang("1st Manhwa", "https://1stmanhwa.com", "en", isNsfw = true, className = "FirstManhwa"), + SingleLang("1stKissManga.blog", "https://1stkissmanga.blog", "en", isNsfw = true, className = "FirstKissMangaBlog"), SingleLang("1stKissManga.Club", "https://1stkissmanga.club", "en", className = "FirstKissMangaClub", overrideVersionCode = 2), + SingleLang("1stKissManga.tv", "https://1stkissmanga.tv", "en", isNsfw = true, className = "FirstKissMangaTv"), SingleLang("247Manga", "https://247manga.com", "en", className = "Manga247", overrideVersionCode = 1), SingleLang("365Manga", "https://365manga.com", "en", className = "ThreeSixtyFiveManga", overrideVersionCode = 1), SingleLang("Adonis Fansub", "https://manga.adonisfansub.com", "tr", overrideVersionCode = 1), SingleLang("Adult Webtoon", "https://adultwebtoon.com", "en", isNsfw = true, overrideVersionCode = 1), - SingleLang("AiYuManga", "https://aiyumangascanlation.com", "es", overrideVersionCode = 1), + SingleLang("AiYuManga", "https://aiyumanga.com", "es", isNsfw = true, overrideVersionCode = 2), SingleLang("Akuma no Tenshi", "https://akumanotenshi.com", "pt-BR", className = "AkumaNoTenshi"), SingleLang("AkuManga", "https://akumanga.com", "ar", overrideVersionCode = 1), SingleLang("Akuzenai Arts", "https://akuzenaiarts.org", "en"), - SingleLang("Aleatória Scan", "https://aleatoriascan.xyz", "pt-BR", className = "AleatoriaScan"), SingleLang("AllPornComic", "https://allporncomic.com", "en", isNsfw = true), SingleLang("Aln Scans", "https://alnscans.com", "en"), SingleLang("Amuy", "https://apenasmaisumyaoi.com", "pt-BR", isNsfw = true, overrideVersionCode = 1), SingleLang("Anikiga", "https://anikiga.com", "tr"), SingleLang("Anisa Manga", "https://anisamanga.com", "tr"), - SingleLang("Ansh Scans", "https://anshscans.org", "en"), + SingleLang("Ansh Scans", "https://anshscans.org", "en", overrideVersionCode = 1), SingleLang("ApollComics", "https://apollcomics.xyz", "es", isNsfw = true, overrideVersionCode = 2), SingleLang("Apolltoons", "https://apolltoons.xyz", "es", isNsfw = true), SingleLang("Aqua Manga", "https://aquamanga.com", "en", overrideVersionCode = 3), SingleLang("ArazNovel", "https://www.araznovel.com", "tr", overrideVersionCode = 3), + SingleLang("ArcheR Scans", "https://www.archerscans.com", "en", isNsfw = false), SingleLang("Arthur Scan", "https://arthurscan.xyz", "pt-BR", overrideVersionCode = 4), SingleLang("Astral Library", "https://www.astrallibrary.net", "en", overrideVersionCode = 2), SingleLang("Astral-Manga", "https://astral-manga.fr", "fr", className = "AstralManga"), + SingleLang("Astrum Scans", "https://astrumscans.xyz", "pt-BR", isNsfw = true), + SingleLang("Asura Scans.us (unoriginal)", "https://asurascans.us", "en", isNsfw = false, className = "AsuraScansUs"), SingleLang("Atikrost", "https://atikrost.com", "tr", overrideVersionCode = 1), SingleLang("AZManhwa", "https://azmanhwa.net", "en"), SingleLang("Azora", "https://azoranov.com", "ar", overrideVersionCode = 6), + SingleLang("Babel Wuxia", "https://read.babelwuxia.com", "en"), SingleLang("Bakaman", "https://bakaman.net", "th", overrideVersionCode = 1), SingleLang("Banana Cítrica", "https://bananacitrica.com", "pt-BR", isNsfw = true, pkgName = "bananamecanica", className = "BananaCitrica", overrideVersionCode = 4), + SingleLang("Banana Manga", "https://bananamanga.net", "en", isNsfw = true), SingleLang("BestManga", "https://bestmanga.club", "ru", overrideVersionCode = 1), SingleLang("BestManhua", "https://bestmanhua.com", "en", overrideVersionCode = 2), - SingleLang("Bichen Traduções", "https://bichentraducoes.com", "pt-BR", isNsfw = true, className = "BichenTraducoes"), - SingleLang("BL Manhwa Club", "https://blmanhwa.club", "pt-BR", isNsfw = true, className = "BlManhwaClub", overrideVersionCode = 2), + SingleLang("BirdToon", "https://birdtoon.net", "id", isNsfw = true), SingleLang("BlogManga", "https://blogmanga.net", "en"), SingleLang("Blue Solo", "https://www1.bluesolo.org", "fr", isNsfw = true), SingleLang("BokugenTranslation", "https://bokugents.com", "es", overrideVersionCode = 1), @@ -79,19 +82,21 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("CAT-translator", "https://cats-translator.com/manga", "th", className = "CatTranslator", overrideVersionCode = 2), SingleLang("Cat300", "https://cat300.com", "th", isNsfw = true, className = "Cat300", overrideVersionCode = 1), SingleLang("CatOnHeadTranslations", "https://catonhead.com", "en", overrideVersionCode = 2), - SingleLang("Cerise Scans", "https://cerisescans.com", "pt-BR", overrideVersionCode = 5), + SingleLang("Cerise Scan", "https://cerisescan.com/home1", "pt-BR", pkgName = "cerisescans", isNsfw = true, overrideVersionCode = 6), SingleLang("Chibi Manga", "https://www.cmreader.info", "en", overrideVersionCode = 1), SingleLang("Clover Manga", "https://clover-manga.com", "tr", overrideVersionCode = 2), + SingleLang("Coco Rip", "https://cocorip.net", "es"), SingleLang("Coffee Manga", "https://coffeemanga.io", "en", overrideVersionCode = 1), - SingleLang("Colored Manga", "https://coloredmanga.com", "en", overrideVersionCode = 1), + SingleLang("CoffeeManga.top (unoriginal)", "https://coffeemanga.top", "en", isNsfw = true, className = "CoffeeMangaTop"), + SingleLang("Colored Manga", "https://coloredmanga.com", "en", overrideVersionCode = 2), + SingleLang("Comic Scans", "https://www.comicscans.org", "en", isNsfw = false), SingleLang("ComicKiba", "https://comickiba.com", "en", overrideVersionCode = 1), SingleLang("Comics Valley", "https://comicsvalley.com", "hi", isNsfw = true, overrideVersionCode = 1), SingleLang("ComicsWorld", "https://comicsworld.in", "hi"), SingleLang("Comictoon", "https://comictoonthaith-new.com", "th", isNsfw = true), - SingleLang("CookieToon", "https://cookietoon.online", "pt-BR"), + SingleLang("Comicz.net v2", "https://v2.comiz.net", "all", isNsfw = true, className = "ComiczNetV2"), SingleLang("Cookie Kiara", "https://18.kiara.cool", "en", isNsfw = true), SingleLang("CopyPasteScan", "https://copypastescan.xyz", "es", overrideVersionCode = 1), - SingleLang("Cronos Scan", "https://cronosscan.net", "pt-BR", overrideVersionCode = 1), SingleLang("DapRob", "https://daprob.com", "es"), SingleLang("Dark Scans", "https://darkscans.com", "en"), SingleLang("Decadence Scans", "https://reader.decadencescans.com", "en", isNsfw = true, overrideVersionCode = 2), @@ -100,67 +105,72 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("DokkoManga", "https://dokkomanga.com", "es", overrideVersionCode = 1), SingleLang("Doodmanga", "https://www.doodmanga.com", "th"), SingleLang("DoujinHentai", "https://doujinhentai.net", "es", isNsfw = true, overrideVersionCode = 1), - SingleLang("DragonTea", "https://dragontea.ink", "en"), + SingleLang("DragonTea", "https://dragontea.ink", "en", overrideVersionCode = 2), SingleLang("DragonTranslation.net", "https://dragontranslation.net", "es", isNsfw = true, className = "DragonTranslationNet"), - SingleLang("Drake Scans", "https://drakescans.com", "en", overrideVersionCode = 3), + SingleLang("Drake Scans", "https://drakescans.com", "en", overrideVersionCode = 4), SingleLang("Dream Manga", "https://www.swarmmanga.com", "en", overrideVersionCode = 3), SingleLang("EGY Manga", "https://egymanga.net", "ar", overrideVersionCode = 1), + SingleLang("Elite Manga", "https://www.elitemanga.org", "en", isNsfw = false), SingleLang("Emperor Scan", "https://emperorscan.com", "es", overrideVersionCode = 1), SingleLang("Empire Webtoon", "https://webtoonempire.com", "ar", isNsfw = true, overrideVersionCode = 2), SingleLang("Eromiau", "https://www.eromiau.com", "es", isNsfw = true), SingleLang("Esomanga", "https://esomanga.com", "tr", overrideVersionCode = 1), - SingleLang("Estufa de Cristal", "https://scanestufadecristal.site", "pt-BR", className = "EstufaDeCristal"), SingleLang("EvaScans", "https://evascans.com", "tr"), - SingleLang("FreeMangaTop", "https://freemangatop.com", "en", overrideVersionCode = 2), + SingleLang("FactManga", "https://factmanga.com", "en", isNsfw = false), SingleLang("FaeStorm", "https://faestormmanga.com", "tr"), SingleLang("Fay Scans", "https://fayscans.com.br", "pt-BR", overrideVersionCode = 1), - SingleLang("FDM Scan", "https://fdmscan.com", "pt-BR", overrideVersionCode = 3), - SingleLang("Fiz Manga", "https://fizmanga.com", "en"), + SingleLang("Fiz Manga", "https://fizmanga.com", "en", overrideVersionCode = 1), SingleLang("Fleur Blanche", "https://fbsquads.com", "pt-BR", isNsfw = true, overrideVersionCode = 2), SingleLang("Flex Tape Scans", "https://flextapescans.com", "en", isNsfw = true), SingleLang("Fox White", "https://foxwhite.com.br", "pt-BR"), + SingleLang("FR-Scan", "https://fr-scan.cc", "fr", pkgName = "frdashscan", className = "FRScan", overrideVersionCode = 2), SingleLang("Free Manga", "https://freemanga.me", "en", isNsfw = true, overrideVersionCode = 3), + SingleLang("Free Manhwa", "https://manhwas.com", "en", isNsfw = false), + SingleLang("FreeMangaTop", "https://freemangatop.com", "en", overrideVersionCode = 2), SingleLang("FreeWebtoonCoins", "https://freewebtooncoins.com", "en", overrideVersionCode = 1), - SingleLang("FR-Scan", "https://fr-scan.com", "fr", pkgName = "frdashscan", className = "FRScan", overrideVersionCode = 1), SingleLang("Fug Manga", "https://fugmanga.net", "ar", overrideVersionCode = 1), SingleLang("Fukushuu no Yuusha", "https://fny-scantrad.com", "fr", overrideVersionCode = 2), - SingleLang("Furio Scans", "https://furioscans.com", "pt-BR", overrideVersionCode = 4), SingleLang("GalaxyDegenScans", "https://gdscans.com", "en", overrideVersionCode = 4), SingleLang("Gatemanga", "https://gatemanga.com", "ar", overrideVersionCode = 1), SingleLang("GeassToon", "https://geasstoon.com", "tr"), SingleLang("Gekkou Hentai", "https://hentai.gekkouscans.com.br", "pt-BR", isNsfw = true), SingleLang("Gekkou Scans", "https://gekkou.com.br", "pt-BR", isNsfw = true, pkgName = "gekkouscan"), + SingleLang("Ghost Scan", "https://ghostscan.com.br", "pt-BR", isNsfw = true), + SingleLang("Girls Love Manga!", "https://glmanga.com", "en", isNsfw = true, className = "GirlsLoveManga"), SingleLang("Glory Manga", "https://glorymanga.com", "tr"), + SingleLang("Good Girls Scan", "https://goodgirls.moe", "en", isNsfw = true), SingleLang("Goof Fansub", "https://gooffansub.com", "pt-BR", isNsfw = true), + SingleLang("Grabber Zone", "https://grabber.zone", "all"), SingleLang("GuncelManga", "https://guncelmanga.com", "tr", overrideVersionCode = 1), - SingleLang("Hreads", "https://hreads.net", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Hades no Fansub Hentai", "https://h.mangareaderpro.com", "es", isNsfw = true), SingleLang("Hades no Fansub", "https://mangareaderpro.com", "es", overrideVersionCode = 1), - SingleLang("Harimanga", "https://harimanga.com", "en", overrideVersionCode = 2), + SingleLang("Harimanga", "https://harimanga.com", "en", overrideVersionCode = 3), SingleLang("Hatachi Manga", "https://hachiraw.com", "ja", isNsfw = true, overrideVersionCode = 2), SingleLang("Hattori Manga", "https://hattorimanga.com", "tr", isNsfw = true), SingleLang("Hayalistic", "https://hayalistic.com", "tr"), - SingleLang("Hela Scan", "https://helascan.com", "pt-BR", isNsfw = true), - SingleLang("Hentai CB", "https://cubeteam.xyz", "vi", isNsfw = true, overrideVersionCode = 5, pkgName = "hentaicube"), + SingleLang("Hentai CB", "https://hentaicube.net", "vi", isNsfw = true, overrideVersionCode = 6, pkgName = "hentaicube"), SingleLang("Hentai Manga", "https://hentaimanga.me", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Hentai Teca", "https://hentaiteca.net", "pt-BR", isNsfw = true, overrideVersionCode = 1), + SingleLang("Hentai-Scantrad", "https://hentai.scantrad-vf.cc", "fr", isNsfw = true, className = "HentaiScantrad", overrideVersionCode = 1), SingleLang("Hentai20", "https://hentai20.io", "en", isNsfw = true, overrideVersionCode = 3), + SingleLang("Hentai3z", "https://hentai3z.xyz", "en", isNsfw = true), + SingleLang("Hentai4Free", "https://hentai4free.net", "en", isNsfw = true), SingleLang("HentaiRead", "https://hentairead.com", "en", isNsfw = true, className = "Hentairead", overrideVersionCode = 3), - SingleLang("Hentai-Scantrad", "https://hentai.scantrad-vf.cc", "fr", isNsfw = true, className = "HentaiScantrad", overrideVersionCode = 1), SingleLang("HentaiWebtoon", "https://hentaiwebtoon.com", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("HentaiXComic", "https://hentaixcomic.com", "en", isNsfw = true), + SingleLang("HentaiXDickgirl", "https://hentaixdickgirl.com", "en", isNsfw = true), SingleLang("HentaiXYuri", "https://hentaixyuri.com", "en", isNsfw = true), - SingleLang("HentaiZone", "https://hentaizone.xyz", "fr", isNsfw = true), + SingleLang("HentaiZone", "https://hentaizone.xyz", "fr", isNsfw = true, overrideVersionCode = 1), SingleLang("HerenScan", "https://herenscan.com", "es"), SingleLang("HipercooL", "https://hipercool.xyz", "pt-BR", isNsfw = true, className = "Hipercool"), - SingleLang("Hiperdex", "https://1sthiperdex.com", "en", isNsfw = true, overrideVersionCode = 9), + SingleLang("Hiperdex", "https://hiperdex.com", "en", isNsfw = true, overrideVersionCode = 10), SingleLang("HistoireDHentai", "https://hhentai.fr", "fr", isNsfw = true), SingleLang("Hizomanga", "https://hizomanga.com", "ar", overrideVersionCode = 1), SingleLang("HM2D", "https://mangadistrict.com/hdoujin", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("hManhwa", "https://hmanhwa.com", "en", isNsfw = true, overrideVersionCode = 1), + SingleLang("Hreads", "https://hreads.net", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Hscans", "https://hscans.com", "en", overrideVersionCode = 3), SingleLang("I Love Manhwa", "https://ilovemanhwa.com", "en", isNsfw = true), - SingleLang("Ichirin No Hana Yuri", "https://ichirinnohanayuriscan.com", "pt-BR", isNsfw = true, overrideVersionCode = 4), SingleLang("Ikifeng", "https://ikifeng.com", "es", isNsfw = true), SingleLang("Illusion Scan", "https://illusionscan.com", "pt-BR", isNsfw = true), SingleLang("Immortal Updates", "https://immortalupdates.com", "en", overrideVersionCode = 6), @@ -170,63 +180,80 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("Inmortal Scan", "https://manga.mundodrama.site", "es"), SingleLang("InstaManhwa", "https://www.instamanhwa.com", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("IsekaiScan.com", "https://isekaiscan.com", "en", className = "IsekaiScanCom", overrideVersionCode = 4), - SingleLang("IsekaiScan.to (unoriginal)", "https://isekaiscan.to", "en", pkgName = "isekaiscaneu", className = "IsekaiScanTo", overrideVersionCode = 2), - SingleLang("IsekaiScan.top (unoriginal)", "https://isekaiscan.top", "en", pkgName = "isekaiscantop", className = "IsekaiScanTop"), + SingleLang("IsekaiScan.to (unoriginal)", "https://m.isekaiscan.to", "en", isNsfw = true, pkgName = "isekaiscaneu", className = "IsekaiScanTo", overrideVersionCode = 3), + SingleLang("IsekaiScan.top (unoriginal)", "https://isekaiscan.top", "en", pkgName = "isekaiscantop", className = "IsekaiScanTop", overrideVersionCode = 1), SingleLang("IsekaiScanManga (unoriginal)", "https://isekaiscanmanga.com", "en", className = "IsekaiScanManga", overrideVersionCode = 1), SingleLang("Its Your Right Manhua", "https://itsyourightmanhua.com", "en", className = "ItsYourRightManhua", overrideVersionCode = 2), SingleLang("Izakaya", "https://leitorizakaya.net", "pt-BR", isNsfw = true, overrideVersionCode = 1), SingleLang("Jiangzaitoon", "https://jiangzaitoon.co", "tr", isNsfw = true, overrideVersionCode = 2), - SingleLang("Kalango Scan", "https://kalangoscan.online", "pt-BR"), + SingleLang("Jimanga", "https://jimanga.com", "en", isNsfw = false), SingleLang("Kami Sama Explorer", "https://leitor.kamisama.com.br", "pt-BR", overrideVersionCode = 2), SingleLang("Karatcam Scans", "https://karatcam-scans.fr", "fr", isNsfw = true), + SingleLang("Kataitake", "https://www.kataitake.fr", "fr", isNsfw = true), SingleLang("KawaScans", "https://kawascans.com", "en", overrideVersionCode = 1), SingleLang("Kiara", "https://kiara.cool", "en"), + SingleLang("Kings-Manga", "https://www.kings-manga.co", "th", isNsfw = false, className = "KingsManga"), SingleLang("Kissmanga.in", "https://kissmanga.in", "en", className = "KissmangaIn", overrideVersionCode = 3), SingleLang("KlikManga", "https://klikmanga.id", "id", overrideVersionCode = 2), - SingleLang("KomikRame", "https://komikra.me", "id"), + SingleLang("Koinobori Scan", "https://koinoboriscan.com", "es", isNsfw = true, className = "KoinoboriScan"), SingleLang("Komik Chan", "https://komikchan.com", "en", className = "KomikChan", overrideVersionCode = 1), + SingleLang("Komik Gue", "https://komikgue.pro", "id", isNsfw = true), + SingleLang("KomikRame", "https://komikra.me", "id"), SingleLang("KSGroupScans", "https://ksgroupscans.com", "en"), SingleLang("Kun Manga", "https://kunmanga.com", "en", overrideVersionCode = 1), SingleLang("Lady Manga", "https://ladymanga.com", "en"), SingleLang("Lala Manga", "https://lalamanga.com", "en", isNsfw = true), SingleLang("Lara Manga", "https://laramanga.love", "en", overrideVersionCode = 1), - SingleLang("Legion Scan", "https://legionscans.com", "es"), + SingleLang("Last Knight Translation", "https://lkscanlation.com", "es", isNsfw = true, className = "LKScanlation"), SingleLang("Ler Yaoi", "https://leryaoi.com", "pt-BR", isNsfw = true), SingleLang("LHTranslation", "https://lhtranslation.net", "en", overrideVersionCode = 1), SingleLang("Lily Manga", "https://lilymanga.net", "en", isNsfw = true, overrideVersionCode = 3), - SingleLang("Lima Scans", "http://limascans.xyz/v2", "pt-BR", isNsfw = true, overrideVersionCode = 2), SingleLang("Lolicon", "https://lolicon.mobi", "en", isNsfw = true, overrideVersionCode = 2), SingleLang("Lord Manga", "https://lordmanga.com", "en"), + SingleLang("Leviatan Scans", "https://lscomic.com", "en", overrideVersionCode = 15), + SingleLang("Luffy Manga", "https://luffymanga.com", "en", isNsfw = false), SingleLang("LuxManga", "https://luxmanga.com", "en"), SingleLang("MadaraDex", "https://madaradex.org", "en", isNsfw = true, overrideVersionCode = 1), + SingleLang("Maid Scan", "https://maidscan.com.br", "pt-BR"), + SingleLang("Manga 18h", "https://manga18h.com", "en", isNsfw = true), + SingleLang("Manga 18x", "https://manga18x.net", "en", isNsfw = true), SingleLang("Manga Action", "https://mangaaction.com", "en", overrideVersionCode = 2), - SingleLang("Manga Bilgini", "https://mangabilgini.com", "tr"), + SingleLang("Manga Bee", "https://mangabee.net", "en", isNsfw = true), SingleLang("Manga Bin", "https://mangabin.com", "en", overrideVersionCode = 1), SingleLang("Manga Chill", "https://toonchill.com", "en", overrideVersionCode = 7), - SingleLang("Manga Crab", "https://mangacrab.com", "es", overrideVersionCode = 4), - SingleLang("Manga District", "https://mangadistrict.com", "en", isNsfw = true, overrideVersionCode = 1), + SingleLang("Manga Crab", "https://manga-crab.com", "es", overrideVersionCode = 6), + SingleLang("Manga District", "https://mangadistrict.com", "en", isNsfw = true, overrideVersionCode = 2), SingleLang("Manga Diyari", "https://manga-diyari.com", "tr", overrideVersionCode = 2), SingleLang("Manga Fenix", "https://manga-fenix.com", "es", overrideVersionCode = 2), SingleLang("Manga Galaxy", "https://mangagalaxy.me", "en", overrideVersionCode = 1), - SingleLang("Manga Hentai", "https://mangahentai.me", "en", isNsfw = true, overrideVersionCode = 2), + SingleLang("Manga Hentai", "https://mangahentai.me", "en", isNsfw = true, overrideVersionCode = 3), SingleLang("Manga Keyfi", "https://mangakeyfi.net", "tr"), - SingleLang("Manga Kio", "https://mangakio.com", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Manga Kiss", "https://mangakiss.org", "en", overrideVersionCode = 1), + SingleLang("Manga Kitsu", "https://mangakitsu.com", "en", isNsfw = false), SingleLang("Manga Leveling", "https://mangaleveling.com", "en", overrideVersionCode = 1), SingleLang("Manga Lord", "https://mangalord.com", "en", overrideVersionCode = 1), + SingleLang("Manga Mammy", "https://mangamammy.ru", "ru", isNsfw = true), SingleLang("Manga Mitsu", "https://mangamitsu.com", "en", isNsfw = true, overrideVersionCode = 2), + SingleLang("Manga Nerds", "https://manganerds.com", "en", isNsfw = false), SingleLang("Manga One Love", "https://mangaonelove.site/", "ru", isNsfw = true), SingleLang("Manga Online Team", "https://mangaonlineteam.com", "en"), + SingleLang("Manga Queen.com", "https://mangaqueen.com", "en", isNsfw = true, className = "MangaQueenCom"), + SingleLang("Manga Queen.online (unoriginal)", "https://mangaqueen.online", "en", isNsfw = true, className = "MangaQueenOnline"), SingleLang("Manga Queen", "https://mangaqueen.net", "en"), SingleLang("Manga Read", "https://mangaread.co", "en", overrideVersionCode = 1), SingleLang("Manga Rock Team", "https://mangarockteam.com", "en", overrideVersionCode = 1), + SingleLang("Manga Rock.team (unoriginal)", "https://mangarock.team", "en", isNsfw = false, className = "MangaRockTeamUnoriginal"), SingleLang("Manga Rocky", "https://mangarocky.com", "en", overrideVersionCode = 1), - SingleLang("Manga Starz", "https://mangastarz.com", "ar", overrideVersionCode = 3), + SingleLang("Manga Rose", "https://mangarose.net", "ar"), + SingleLang("Manga Şehri", "https://mangasehri.com", "tr", className = "MangaSehri", isNsfw = true), + SingleLang("Manga Starz", "https://mangalike.org", "ar", overrideVersionCode = 4), SingleLang("Manga Too", "https://mangatoo.com", "en", overrideVersionCode = 1), + SingleLang("Manga Tx.gg (unoriginal)", "https://mangatx.gg", "en", isNsfw = false, className = "MangaTxGg"), SingleLang("Manga Weebs", "https://mangaweebs.in", "en", overrideVersionCode = 7), - SingleLang("Manga Şehri", "https://mangasehri.com", "tr", className = "MangaSehri", isNsfw = true), + SingleLang("Manga-1001.com", "https://manga-1001.com", "en", isNsfw = false, className = "MangaDash1001Com"), SingleLang("Manga-fast.com", "https://manga-fast.com", "en", className = "Mangafastcom", overrideVersionCode = 3), SingleLang("Manga-Online.co", "https://www.manga-online.co", "th", className = "MangaOnlineCo"), + SingleLang("Manga-Raw.info (unoriginal)", "https://manga-raw.info", "en", isNsfw = true, className = "MangaRawInfo"), SingleLang("Manga-Scantrad", "https://manga-scantrad.io", "fr", className = "MangaScantrad", overrideVersionCode = 3), SingleLang("Manga-TX", "https://manga-tx.com", "en", className = "Mangatxunoriginal"), SingleLang("Manga18fx", "https://manga18fx.com", "en", isNsfw = true, overrideVersionCode = 5), @@ -237,23 +264,28 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("Manga68", "https://manga68.com", "en", overrideVersionCode = 1), SingleLang("MangaBaz", "https://mangabaz.net", "en"), SingleLang("MangaBob", "https://mangabob.com", "en", overrideVersionCode = 1), - SingleLang("MangaVisa", "https://mangavisa.com", "en", pkgName = "mangaboss", className = "MangaVisa", overrideVersionCode = 1), SingleLang("MangaCC", "https://mangacc.com", "en"), + SingleLang("MangaClash.tv (unoriginal)", "https://mangaclash.tv", "en", isNsfw = true, className = "MangaClashTv"), SingleLang("MangaClash", "https://mangaclash.com", "en", overrideVersionCode = 3), + SingleLang("MangaCrazy", "https://mangacrazy.net", "all", isNsfw = true), SingleLang("MangaCultivator", "https://mangacultivator.com", "en", overrideVersionCode = 2), SingleLang("MangaCV", "https://mangacv.com", "en", isNsfw = true), SingleLang("MangaDeemak", "https://mangadeemak.com", "th", overrideVersionCode = 2), - SingleLang("MangaDods", "https://www.mangadods.com", "en", overrideVersionCode = 2), + SingleLang("MangaDino.top (unoriginal)", "https://mangadino.top", "en", isNsfw = true, className = "MangaDinoTop"), + SingleLang("MangaDods", "https://mangadods.com", "en", overrideVersionCode = 3), SingleLang("MangaDol", "https://mangadol.com", "en"), SingleLang("MangaEffect", "https://mangaeffect.com", "en", overrideVersionCode = 1), SingleLang("Mangaforfree.com", "https://mangaforfree.com", "en", isNsfw = true, className = "Mangaforfreecom"), SingleLang("MangaFoxFull", "https://mangafoxfull.com", "en"), SingleLang("MangaFreak.online", "https://mangafreak.online", "en", className = "MangaFreakOnline"), SingleLang("MangaGG", "https://mangagg.com", "en", overrideVersionCode = 2), - SingleLang("MangaGreat", "https://mangagreat.com", "en", overrideVersionCode = 3), - SingleLang("MangaHub.fr", "https://mangahub.fr", "fr", isNsfw = true, className = "MangaHubFr", pkgName = "mangahubfr"), + SingleLang("MangaGo Yaoi", "https://mangagoyaoi.com", "en", isNsfw = true), + SingleLang("MangaGreat", "https://mangagreat.com", "en", overrideVersionCode = 4), + SingleLang("MangaHub.fr", "https://mangahub.fr", "fr", isNsfw = true, className = "MangaHubFr", pkgName = "mangahubfr", overrideVersionCode = 1), SingleLang("MangaHZ", "https://www.mangahz.com", "en", isNsfw = true, overrideVersionCode = 2), SingleLang("MangaK2", "https://mangak2.com", "en", isNsfw = true), + SingleLang("Mangakakalot.io (unoriginal)", "https://mangakakalot.io", "en", isNsfw = true, className = "MangakakalotIo"), + SingleLang("Mangakakalot.one (unoriginal)", "https://mangakakalot.one", "en", isNsfw = true, className = "MangakakalotOne"), SingleLang("Mangakik", "https://mangakik.net", "en", overrideVersionCode = 1), SingleLang("MangaKing", "https://mangaking.net", "en"), SingleLang("MangaKitsune", "https://mangakitsune.com", "en", isNsfw = true, overrideVersionCode = 4), @@ -262,24 +294,38 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("MangaLionz", "https://mangalionz.com", "ar", overrideVersionCode = 1), SingleLang("MangaManhua", "https://mangamanhua.online", "en", overrideVersionCode = 1), SingleLang("MangaManiacs", "https://mangamaniacs.org", "en", isNsfw = true), - SingleLang("MangaMe", "https://mangame.org", "en", overrideVersionCode = 1), + SingleLang("Manganelo.biz", "https://manganelo.biz", "en", isNsfw = true, className = "ManganeloBiz"), + SingleLang("Manganelo.website (unoriginal)", "https://manganelo.website", "en", isNsfw = true, className = "ManganeloWebsite"), + SingleLang("MangaOnline.team (unoriginal)", "https://mangaonline.team", "en", isNsfw = false, className = "MangaOnlineTeamUnoriginal"), + SingleLang("MangaOwl.blog (unoriginal)", "https://mangaowl.blog", "en", isNsfw = true, className = "MangaOwlBlog"), + SingleLang("MangaOwl.io (unoriginal)", "https://mangaowl.io", "en", isNsfw = true, className = "MangaOwlIo"), + SingleLang("MangaOwl.one (unoriginal)", "https://mangaowl.one", "en", isNsfw = true, className = "MangaOwlOne"), + SingleLang("MangaOwl.us (unoriginal)", "https://mangaowl.us", "en", isNsfw = true, className = "MangaOwlUs"), SingleLang("MangaPT", "https://mangapt.com", "es", isNsfw = true), + SingleLang("MangaPure", "https://mangapure.net", "en", isNsfw = true), SingleLang("MangaRabic", "https://mangaarabics.com", "ar", overrideVersionCode = 1), SingleLang("MangaRead.org", "https://www.mangaread.org", "en", className = "MangaReadOrg", overrideVersionCode = 1), SingleLang("MangaRolls", "https://mangarolls.com", "en"), + SingleLang("MangaRosie", "https://mangarosie.in", "en", isNsfw = true), + SingleLang("MangaRuby.com", "https://mangaruby.com", "en", isNsfw = true, className = "MangaRubyCom"), + SingleLang("Mangaryu", "https://mangaryu.com", "en", isNsfw = true), SingleLang("Mangas Origines X", "https://x.mangas-origines.fr", "fr", isNsfw = true), SingleLang("Mangas Origines", "https://mangas-origines.xyz", "fr", isNsfw = true, overrideVersionCode = 4), + SingleLang("Mangas-Origines.fr", "https://mangas-origines.fr", "fr", className = "MangasOriginesFr"), SingleLang("MangaSco", "https://manhwasco.net", "en", overrideVersionCode = 2), SingleLang("MangaSiro", "https://mangasiro.com", "en", isNsfw = true), - SingleLang("MangaSpark", "https://mangaspark.com", "ar", overrideVersionCode = 2), + SingleLang("MangaSpark", "https://mangaspark.com", "ar", overrideVersionCode = 3), SingleLang("MangaStic", "https://mangastic9.com", "en", overrideVersionCode = 2), - SingleLang("MangasTK18", "https://mangastk18.com", "es", isNsfw = true), SingleLang("Mangasushi", "https://mangasushi.org", "en", overrideVersionCode = 3), SingleLang("MangaTone", "https://mangatone.com", "en"), + SingleLang("MangaTop.site", "https://mangatop.site", "all", isNsfw = true, className = "MangaTopSite"), SingleLang("MangaToRead", "https://mangatoread.com", "en"), SingleLang("MangaTX", "https://mangatx.com", "en", overrideVersionCode = 1), + SingleLang("MangaTyrant", "https://mangatyrant.com", "en", isNsfw = false), + SingleLang("MangaUpdates.top (unoriginal)", "https://mangaupdates.top", "en", isNsfw = true, className = "MangaUpdatesTop"), SingleLang("Mangauptocats", "https://manga-uptocats.com", "th", overrideVersionCode = 4), SingleLang("MangaUS", "https://mangaus.xyz", "en", overrideVersionCode = 2), + SingleLang("MangaVisa", "https://mangavisa.com", "en", pkgName = "mangaboss", className = "MangaVisa", overrideVersionCode = 1), SingleLang("MangaX1", "https://mangax1.com", "en"), SingleLang("MangaXP", "https://mangaxp.com", "en", overrideVersionCode = 1), SingleLang("MangaYami", "https://www.mangayami.club", "en", overrideVersionCode = 2), @@ -288,99 +334,110 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("Manhua Kiss", "https://manhuakiss.com", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Manhua Plus", "https://manhuaplus.com", "en", overrideVersionCode = 6), SingleLang("Manhua SY", "https://www.manhuasy.com", "en", overrideVersionCode = 2), + SingleLang("Manhua Zonghe", "https://manhuazonghe.com", "en", isNsfw = true), SingleLang("ManhuaBox", "https://manhuabox.net", "en", overrideVersionCode = 2), SingleLang("ManhuaChill", "https://manhuachill.com", "en"), + SingleLang("ManhuaDex", "https://manhuadex.com", "en", isNsfw = false), + SingleLang("ManhuaFast.net (unoriginal)", "https://manhuafast.net", "en", isNsfw = false, className = "ManhuaFastNet"), SingleLang("ManhuaFast", "https://manhuafast.com", "en", overrideVersionCode = 2), SingleLang("Manhuaga", "https://manhuaga.com", "en", overrideVersionCode = 2), SingleLang("ManhuaHot", "https://manhuahot.com", "en"), + SingleLang("ManhuaManhwa.online", "https://manhuamanhwa.online", "en", isNsfw = false, className = "ManhuaManhwaOnline"), + SingleLang("ManhuaManhwa", "https://manhuamanhwa.com", "en", isNsfw = true), SingleLang("Manhuas.net", "https://manhuas.net", "en", className = "Manhuasnet", overrideVersionCode = 2), + SingleLang("ManhuaScan.info (unoriginal)", "https://manhuascan.info", "en", isNsfw = true, className = "ManhuaScanInfo"), SingleLang("ManhuaUS", "https://manhuaus.com", "en", overrideVersionCode = 5), SingleLang("ManhuaZone", "https://manhuazone.com", "en"), SingleLang("Manhwa Raw", "https://manhwaraw.com", "ko", isNsfw = true, overrideVersionCode = 1), - SingleLang("Manhwa-Latino", "https://manhwa-latino.com", "es", isNsfw = true, className = "ManhwaLatino", overrideVersionCode = 3), + SingleLang("Manhwa-Latino", "https://manhwa-latino.com", "es", isNsfw = true, className = "ManhwaLatino", overrideVersionCode = 4), SingleLang("Manhwa-raw", "https://manhwa-raw.com", "all", isNsfw = true, className = "ManhwaDashRaw"), SingleLang("Manhwa18.app", "https://manhwa18.app", "en", isNsfw = true, className = "Manhwa18app"), SingleLang("Manhwa18.org", "https://manhwa18.org", "en", isNsfw = true, className = "Manhwa18Org", overrideVersionCode = 2), + SingleLang("Manhwa2Read", "https://manhwa2read.com", "en", isNsfw = false), SingleLang("Manhwa365", "https://manhwa365.com", "en", isNsfw = true), SingleLang("Manhwa68", "https://manhwa68.com", "en", isNsfw = true, overrideVersionCode = 3), SingleLang("ManhwaBookShelf", "https://manhwabookshelf.com", "en"), SingleLang("ManhwaClan", "https://manhwaclan.com", "en"), SingleLang("Manhwafull", "https://manhwafull.com", "en", overrideVersionCode = 1), SingleLang("Manhwahentai.me", "https://manhwahentai.me", "en", className = "ManhwahentaiMe", isNsfw = true, overrideVersionCode = 3), + SingleLang("ManhwaManhua", "https://manhwamanhua.com", "en", isNsfw = true), + SingleLang("ManhwaNew", "https://manhwanew.com", "en", isNsfw = true), SingleLang("Manhwas Men", "https://manhwas.men", "en", className = "ManhwasMen", isNsfw = true), SingleLang("ManhwaTime", "https://manhwatime.com", "ar"), SingleLang("Manhwatop", "https://manhwatop.com", "en", overrideVersionCode = 2), SingleLang("ManhwaWorld", "https://manhwaworld.com", "en"), SingleLang("Manhwua.fans", "https://manhwua.fans", "en", isNsfw = true, className = "Manhwuafans"), + SingleLang("Mantraz Scan", "https://mantrazscan.com", "es"), SingleLang("ManyComic", "https://manycomic.com", "en", isNsfw = true, overrideVersionCode = 1), - SingleLang("ManyToon", "https://manytoon.com", "en", isNsfw = true, overrideVersionCode = 5), SingleLang("ManyToon.me", "https://manytoon.me", "en", isNsfw = true, className = "ManyToonMe", overrideVersionCode = 4), + SingleLang("ManyToon", "https://manytoon.com", "en", isNsfw = true, overrideVersionCode = 5), SingleLang("ManyToonClub", "https://manytoon.club", "ko", isNsfw = true, overrideVersionCode = 1), - SingleLang("MG Komik", "https://mgkomik.com", "id", overrideVersionCode = 4), - SingleLang("MHentais", "https://mhentais.com", "pt-BR", isNsfw = true, overrideVersionCode = 1), + SingleLang("MG Komik", "https://mgkomik.id", "id", overrideVersionCode = 6), SingleLang("Midnight Mess Scans", "https://midnightmess.org", "en", isNsfw = true, overrideVersionCode = 6), SingleLang("MidnightManga", "http://midnightmanga.com", "es"), SingleLang("Milftoon", "https://milftoon.xxx", "en", isNsfw = true, overrideVersionCode = 2), - SingleLang("Mirad Scanlator", "https://miradscanlator.site", "pt-BR", overrideVersionCode = 1), + SingleLang("MiniTwo Scan", "https://minitwoscan.com", "pt-BR"), SingleLang("Mixed Manga", "https://mixedmanga.com", "en", overrideVersionCode = 1), - SingleLang("MMScans", "https://mm-scans.org", "en", overrideVersionCode = 5), + SingleLang("MMScans", "https://mm-scans.org", "en", overrideVersionCode = 7), SingleLang("MonarcaManga", "https://monarcamanga.com", "es"), - SingleLang("MoonLovers Scan", "https://moonloversscan.com.br", "pt-BR", isNsfw = true), SingleLang("Moon Witch In Love", "https://moonwitchinlovescan.com", "pt-BR"), + SingleLang("MoonLovers Scan", "https://moonloversscan.com.br", "pt-BR", isNsfw = true), SingleLang("Mortals Groove", "https://mortalsgroove.com", "en", overrideVersionCode = 1), SingleLang("MR Yaoi Fansub", "https://mrbenne.com", "pt-BR", isNsfw = true, className = "MrYaoiFansub", overrideVersionCode = 1), SingleLang("Muctau", "https://bibimanga.com", "en", isNsfw = true, overrideVersionCode = 4), SingleLang("MurimScan", "https://murimscan.run", "en", isNsfw = true), SingleLang("My Manhwa", "https://mymanhwa.net", "en"), - SingleLang("My Universe Scanlator", "https://muscan.com.br", "pt-BR"), SingleLang("Mystical Merries", "https://mysticalmerries.com", "en", overrideVersionCode = 2), SingleLang("NeatManga", "https://neatmanga.com", "en", overrideVersionCode = 2), + SingleLang("NekoPost.co (unoriginal)", "https://www.nekopost.co", "th", isNsfw = false, className = "NekoPostCo"), SingleLang("NekoScan", "https://nekoscan.com", "en", overrideVersionCode = 2), SingleLang("Night Comic", "https://www.nightcomic.com", "en", overrideVersionCode = 1), SingleLang("Niji Translations", "https://niji-translations.com", "ar", overrideVersionCode = 1), - SingleLang("Ninja Scan", "https://ninjascan.xyz", "pt-BR", overrideVersionCode = 1), SingleLang("Nitro Scans", "https://nitroscans.com", "en"), SingleLang("No Index Scan", "https://noindexscan.com", "pt-BR", isNsfw = true), - SingleLang("Nocturne Summer", "https://nocsummer.com.br", "pt-BR", isNsfw = true), SingleLang("Noblesse Translations", "https://www.noblessetranslations.com", "es"), + SingleLang("Nocturne Summer", "https://nocsummer.com.br", "pt-BR", isNsfw = true), SingleLang("NovelCrow", "https://novelcrow.com", "en", isNsfw = true), SingleLang("NovelMic", "https://novelmic.com", "en", overrideVersionCode = 1), SingleLang("Novels Town", "https://novelstown.cyou", "ar"), SingleLang("Oh No Manga", "https://ohnomanga.com", "en", isNsfw = true), + SingleLang("OnlyManhwa", "https://onlymanhwa.org", "en", isNsfw = true), SingleLang("Painful Nightz Scan", "https://painfulnightz.com", "en", overrideVersionCode = 1), SingleLang("Pantheon Scan", "https://pantheon-scan.com", "fr", overrideVersionCode = 1), - SingleLang("Peach Scan", "https://www.peachscan.com", "pt-BR", isNsfw = true), + SingleLang("Paragon Scans", "https://paragonscans.com", "en", isNsfw = true), + SingleLang("Paw Manga", "https://pawmanga.com", "en", isNsfw = true), SingleLang("Petrotechsociety", "https://www.petrotechsociety.org", "en", isNsfw = true), - SingleLang("Phoenix Fansub", "https://phoenixmangas.com", "es"), SingleLang("Pian Manga", "https://pianmanga.me", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Pink Sea Unicorn", "https://psunicorn.com", "pt-BR", isNsfw = true), SingleLang("Pirulito Rosa", "https://pirulitorosa.site", "pt-BR", isNsfw = true), SingleLang("Platinum Crown", "https://platinumscans.com", "en", overrideVersionCode = 1), SingleLang("Pojok Manga", "https://pojokmanga.net", "id", overrideVersionCode = 4), SingleLang("PoManga", "https://pomanga.com", "en"), - SingleLang("Pornhwa18", "https://pornhwa18.com", "id", isNsfw = true), + SingleLang("Pony Manga", "https://ponymanga.com", "en", isNsfw = true), SingleLang("PornComix", "https://www.porncomixonline.net", "en", isNsfw = true, overrideVersionCode = 1), + SingleLang("Pornhwa18", "https://pornhwa18.com", "id", isNsfw = true), SingleLang("Pornwha", "https://pornwha.com", "en", isNsfw = true, overrideVersionCode = 1), SingleLang("Portal Yaoi", "https://portalyaoi.com", "pt-BR", isNsfw = true), SingleLang("Prisma Hentais", "https://prismahentai.com", "pt-BR", isNsfw = true), SingleLang("Prisma Scans", "https://prismascans.net", "pt-BR", overrideVersionCode = 2), SingleLang("Projeto Scanlator", "https://projetoscanlator.com", "pt-BR", overrideVersionCode = 3), - SingleLang("ROG Mangás", "https://rogmangas.com", "pt-BR", pkgName = "mangasoverall", className = "RogMangas", overrideVersionCode = 1), SingleLang("Ragnarok Scanlation", "https://ragnarokscanlation.com", "es", className = "RagnarokScanlation"), SingleLang("RagnarokScan", "https://ragnarokscan.com", "es", overrideVersionCode = 1), SingleLang("Raijin Scans", "https://raijinscans.fr", "fr"), SingleLang("Rainbow Fairy Scan", "https://rainbowfairyscan.com", "pt-BR"), - SingleLang("Random Scan", "https://randomscans.com", "pt-BR", overrideVersionCode = 5), + SingleLang("Random Scan", "https://randomscanlators.net", "pt-BR", overrideVersionCode = 6), SingleLang("RawDEX", "https://rawdex.net", "ko", isNsfw = true, overrideVersionCode = 1), SingleLang("ReadAdult", "https://readadult.net", "en", isNsfw = true, overrideVersionCode = 1), + SingleLang("ReaderGen", "https://fr.readergen.fr", "fr"), SingleLang("Readers Point", "https://readers-point.space", "en"), SingleLang("ReadFreeComics", "https://readfreecomics.com", "en"), SingleLang("ReadMangaFree", "https://readmangafree.net", "en", isNsfw = true), SingleLang("ReadManhua", "https://readmanhua.net", "en", overrideVersionCode = 2), - SingleLang("Rh2PlusManga", "https://www.rh2plusmanga.com", "th", overrideVersionCode = 4), + SingleLang("Rh2PlusManga", "https://www.rh2plusmanga.com", "th", isNsfw = true, overrideVersionCode = 5), + SingleLang("Rightdark Scan", "https://rightdark-scan.com", "es"), SingleLang("Rio2 Manga", "https://rio2manga.com", "en"), + SingleLang("ROG Mangás", "https://rogmangas.com", "pt-BR", pkgName = "mangasoverall", className = "RogMangas", overrideVersionCode = 1), SingleLang("Romantik Manga", "https://romantikmanga.com", "tr"), - SingleLang("RWBY Scan", "https://rwbyscan.site", "pt-BR", isNsfw = true, className = "RwbyScan"), SingleLang("Rüya Manga", "https://www.ruyamanga.com", "tr", className = "RuyaManga", overrideVersionCode = 1), SingleLang("S2Manga", "https://s2manga.com", "en", overrideVersionCode = 1), SingleLang("Sagrado Império da Britannia", "https://imperiodabritannia.com", "pt-BR", className = "ImperioDaBritannia"), @@ -391,13 +448,13 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("Scans Raw", "https://scansraw.com", "en", overrideVersionCode = 1), SingleLang("Scantrad-VF", "https://scantrad-vf.co", "fr", className = "ScantradVF"), SingleLang("Sdl scans", "https://sdlscans.com", "es", className = "SdlScans"), - SingleLang("Sensaina Yuri", "https://sensainayuri.dropescan.com", "pt-BR", isNsfw = true, overrideVersionCode = 2), + SingleLang("Shadowtrad", "https://shadowtrad.net", "fr"), SingleLang("ShavelProiection", "https://www.shavelproiection.com", "it", true), SingleLang("Shayami", "https://shayami.com", "es"), + SingleLang("Shiba Manga", "https://shibamanga.com", "en"), SingleLang("Shield Manga", "https://shieldmanga.io", "en", overrideVersionCode = 3), SingleLang("Shimada Scans", "https://shimadascans.com", "en"), - SingleLang("Shinigami", "https://shinigami.id", "id", overrideVersionCode = 1), - SingleLang("Shirai Scans", "https://shiraiscans.com.br", "pt-BR"), + SingleLang("Shinigami", "https://shinigami.ae", "id", overrideVersionCode = 2), SingleLang("Shooting Star Scans", "https://shootingstarscans.com", "en"), SingleLang("ShoujoHearts", "https://shoujohearts.com", "en", overrideVersionCode = 2), SingleLang("Sinensis Scan", "https://sinensisscans.com", "pt-BR", pkgName = "sinensis", overrideVersionCode = 5), @@ -406,48 +463,46 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("SkyManga.xyz", "https://skymanga.xyz", "en", isNsfw = true, className = "SkyMangaXyz"), SingleLang("Sleeping Knight Scans", "https://skscans.com", "en", overrideVersionCode = 2), SingleLang("Sleepy Translations", "https://sleepytranslations.com", "en", overrideVersionCode = 1), - SingleLang("SodaScan", "https://sodascan.xyz", "pt-BR", isNsfw = true, overrideVersionCode = 1), SingleLang("Solo Leveling", "https://readsololeveling.online", "en"), SingleLang("Sugar Babies", "https://sugarbbscan.com", "en", overrideVersionCode = 2), SingleLang("Summanga", "https://summanga.com", "en", isNsfw = true), SingleLang("Sunshine Butterfly Scans", "https://sunshinebutterflyscan.com", "en", isNsfw = true, overrideVersionCode = 1), - SingleLang("Sweet Desire Scan", "https://sweetdesire.com.br", "pt-BR", isNsfw = true), SingleLang("Sweet Time Scan", "https://sweetscan.net", "pt-BR", overrideVersionCode = 2), SingleLang("Tankou Hentai", "https://tankouhentai.com", "pt-BR", isNsfw = true), SingleLang("TappyToon.Net", "https://tappytoon.net", "en", className = "Tappytoonnet"), SingleLang("Tatakae Scan", "https://tatakaescan.com", "pt-BR", isNsfw = true, overrideVersionCode = 2), SingleLang("Taurus Fansub", "https://taurusfansub.com", "es"), SingleLang("Tecno Scan", "https://tecnoscann.com", "es"), - SingleLang("TeenManhua", "https://teenmanhua.com", "en"), - SingleLang("Temple Scan", "https://templescan.net", "en"), + SingleLang("TeenManhua", "https://teenmanhua.com", "en", overrideVersionCode = 1), + SingleLang("TempleScan", "https://templescanesp.com", "es", isNsfw = true, className = "TempleScanEsp", overrideVersionCode = 1), SingleLang("The Beginning After The End", "https://www.thebeginningaftertheend.fr", "fr", overrideVersionCode = 1), SingleLang("The Guild", "https://theguildscans.com", "en"), - SingleLang("The Sugar", "https://thesugarscan.com", "pt-BR"), - SingleLang("Three Queens Scanlator", "https://tqscan.com.br", "pt-BR", overrideVersionCode = 3), SingleLang("Time Naight", "https://timenaight.com", "tr"), SingleLang("Todaymic", "https://todaymic.com", "en", overrideVersionCode = 1), SingleLang("TonizuToon", "https://tonizutoon.com", "tr", isNsfw = true), SingleLang("ToonChill", "https://toonchill.com", "en", overrideVersionCode = 1), SingleLang("ToonGod", "https://www.toongod.com", "en", isNsfw = true, overrideVersionCode = 4), - SingleLang("Toonily", "https://toonily.com", "en", isNsfw = true, overrideVersionCode = 11), SingleLang("Toonily.net", "https://toonily.net", "en", isNsfw = true, className = "Toonilynet", overrideVersionCode = 2), + SingleLang("Toonily", "https://toonily.com", "en", isNsfw = true, overrideVersionCode = 11), + SingleLang("Toonizy", "https://toonizy.com", "en", isNsfw = true), SingleLang("ToonMany", "https://toonmany.com", "en", isNsfw = true), SingleLang("Top Manhua", "https://topmanhua.com", "en", overrideVersionCode = 2), SingleLang("Tortuga Ceviri", "https://tortuga-ceviri.com", "tr"), + SingleLang("Traducciones Moonlight", "https://traduccionesmoonlight.com", "es"), SingleLang("Trap Scans", "https://trapscans.com", "en"), SingleLang("TreeManga", "https://treemanga.com", "en", overrideVersionCode = 1), SingleLang("TritiniaScans", "https://tritinia.org", "en", overrideVersionCode = 4), - SingleLang("Tudo Quadrinhos", "https://tudoquadrinhos.com.br", "pt-BR"), SingleLang("Tumangaonline.site", "https://tumangaonline.site", "es", isNsfw = true, className = "TumangaonlineSite", pkgName = "tumangaonlinesite"), SingleLang("Türkçe Manga", "https://turkcemanga.com", "tr", className = "TurkceManga", overrideVersionCode = 2), + SingleLang("Unitoon", "https://lectorunitoon.com", "es"), + SingleLang("Unitoon Oficial", "https://unitoonoficial.com", "es"), SingleLang("Valkyrie Scan", "https://valkyriescan.com", "pt-BR", isNsfw = true), SingleLang("Ver Manhwas", "https://vermanhwa.es", "es", isNsfw = true, overrideVersionCode = 1), SingleLang("VinManga", "https://vinload.com", "en", isNsfw = true), - SingleLang("Visbellum", "https://visbellum.com", "pt-BR", overrideVersionCode = 2), - SingleLang("VoirComic", "https://voircomic.com", "fr"), SingleLang("Wakamics", "https://wakamics.net", "en"), SingleLang("Wakascan", "https://wakascan.com", "fr", overrideVersionCode = 1), - SingleLang("War Queen Scan", "https://wqscan.com", "pt-BR", overrideVersionCode = 6), + SingleLang("Webdex Scans", "https://webdexscans.com", "en", isNsfw = false), + SingleLang("Webtoon City", "https://webtooncity.com", "en", isNsfw = false), SingleLang("Webtoon Hatti", "https://webtoonhatti.com", "tr", overrideVersionCode = 1), SingleLang("Webtoon TR", "https://webtoon-tr.com", "tr", overrideVersionCode = 1), SingleLang("WebToonily", "https://webtoonily.com", "en", isNsfw = true, overrideVersionCode = 1), @@ -455,6 +510,7 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("WebtoonsTOP", "https://webtoons.top", "en", isNsfw = true), SingleLang("WebtoonUK", "https://webtoon.uk", "en", overrideVersionCode = 2), SingleLang("WebtoonXYZ", "https://www.webtoon.xyz", "en", isNsfw = true, overrideVersionCode = 3), + SingleLang("Wicked Witch Scan", "https://wickedwitchscan.com", "pt-BR"), SingleLang("Winter Scan", "https://winterscan.com", "pt-BR", overrideVersionCode = 4), SingleLang("Wonderland Scan", "https://wonderlandscan.com", "pt-BR", overrideVersionCode = 3), SingleLang("WoopRead", "https://woopread.com", "en", overrideVersionCode = 1), @@ -472,6 +528,7 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("Yuri Verso", "https://yuri.live", "pt-BR", overrideVersionCode = 3), SingleLang("Zandy no Fansub", "https://zandynofansub.aishiteru.org", "en"), SingleLang("ZinChanManga", "https://zinchanmanga.com", "en", isNsfw = true), + SingleLang("ZinManga.top (unoriginal)", "https://zinmanga.top", "en", isNsfw = false, className = "ZinMangaTop"), SingleLang("Zinmanga", "https://zinmanga.com", "en", overrideVersionCode = 1), SingleLang("Zinmanhwa", "https://zinmanhwa.com", "en"), SingleLang("ZuttoManga", "https://zuttomanga.com", "en", overrideVersionCode = 1), @@ -479,10 +536,12 @@ class MadaraGenerator : ThemeSourceGenerator { SingleLang("شبكة كونان العربية", "https://manga.detectiveconanar.com", "ar", className = "DetectiveConanAr", overrideVersionCode = 2), SingleLang("عرب تونز", "https://arabtoons.net", "ar", isNsfw = true, className = "ArabToons"), SingleLang("مانجا العاشق", "https://3asq.org", "ar", className = "Manga3asq", overrideVersionCode = 2), - SingleLang("مانجا العرب Manga Alarab", "https://manga-alarab.com", "ar", className = "MangAlarab", overrideVersionCode = 1), SingleLang("مانجا عرب تيم Manga Arab Team", "https://mangaarbteam.com", "ar", className = "MangaArabTeam", overrideVersionCode = 1), SingleLang("مانجا ليك", "https://mangalek.com", "ar", className = "Mangalek", overrideVersionCode = 2), + SingleLang("مانجا ليكس", "https://mangaleks.com", "ar", className = "MangaLeks"), SingleLang("مانجا لينك", "https://mangalink.io", "ar", className = "MangaLinkio", overrideVersionCode = 3), + SingleLang("كوميك العرب", "https://comicarab.com", "ar", isNsfw = true, className = "ComicArab"), + SingleLang("فالكون مانجا", "https://falconmanga.com", "ar", className = "FalconManga"), SingleLang("巴卡漫画", "https://bakamh.com", "zh", isNsfw = true, className = "Bakamh"), ) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangacatalog/MangaCatalogGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangacatalog/MangaCatalogGenerator.kt index 94ad066701..0bb77d672d 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangacatalog/MangaCatalogGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangacatalog/MangaCatalogGenerator.kt @@ -14,6 +14,7 @@ class MangaCatalogGenerator : ThemeSourceGenerator { override val sources = listOf( SingleLang("Read Attack on Titan Shingeki no Kyojin Manga", "https://ww8.readsnk.com", "en", className = "ReadAttackOnTitanShingekiNoKyojinManga", overrideVersionCode = 4), SingleLang("Read Berserk Manga", "https://readberserk.com", "en"), + SingleLang("Read Black Clover Manga Online", "https://ww7.readblackclover.com", "en"), SingleLang("Read Boku no Hero Academia My Hero Academia Manga", "https://ww6.readmha.com", "en", className = "ReadBokuNoHeroAcademiaMyHeroAcademiaManga", overrideVersionCode = 2), SingleLang("Read Chainsaw Man Manga Online", "https://ww1.readchainsawman.com", "en"), SingleLang("Read Dr. Stone Manga Online", "https://ww3.readdrstone.com", "en", className = "ReadDrStoneMangaOnline"), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt index 00df4ebdcc..7589f1ef0a 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHub.kt @@ -1,5 +1,7 @@ package eu.kanade.tachiyomi.multisrc.mangahub +import eu.kanade.tachiyomi.lib.randomua.UserAgentType +import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.interceptor.rateLimit @@ -48,7 +50,10 @@ abstract class MangaHub( private var baseCdnUrl = "https://imgx.mghubcdn.com" override val client: OkHttpClient = super.client.newBuilder() - .addInterceptor(::uaIntercept) + .setRandomUserAgent( + userAgentType = UserAgentType.DESKTOP, + filterInclude = listOf("chrome"), + ) .addInterceptor(::apiAuthInterceptor) .rateLimit(1) .build() @@ -65,35 +70,6 @@ abstract class MangaHub( open val json: Json by injectLazy() - private var userAgent: String? = null - private var checkedUa = false - - private fun uaIntercept(chain: Interceptor.Chain): Response { - if (userAgent == null && !checkedUa) { - val uaResponse = chain.proceed(GET(UA_DB_URL)) - - if (uaResponse.isSuccessful) { - // only using desktop chromium-based browsers, apparently they refuse to load(403) if not chrome(ium) - val uaList = json.decodeFromString>>(uaResponse.body.string()) - val chromeUserAgentString = uaList["desktop"]!!.filter { it.contains("chrome", ignoreCase = true) } - userAgent = chromeUserAgentString.random() - checkedUa = true - } - - uaResponse.close() - } - - if (userAgent != null) { - val newRequest = chain.request().newBuilder() - .header("User-Agent", userAgent!!) - .build() - - return chain.proceed(newRequest) - } - - return chain.proceed(chain.request()) - } - private fun apiAuthInterceptor(chain: Interceptor.Chain): Response { val originalRequest = chain.request() @@ -514,8 +490,4 @@ abstract class MangaHub( Genre("Wuxia", "wuxia"), Genre("Yuri", "yuri"), ) - - companion object { - private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json" - } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt index 43279683ad..3b05e98dfa 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangahub/MangaHubGenerator.kt @@ -9,7 +9,7 @@ class MangaHubGenerator : ThemeSourceGenerator { override val themeClass = "MangaHub" - override val baseVersionCode: Int = 21 + override val baseVersionCode: Int = 22 override val sources = listOf( // SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawGenerator.kt index 1c7c343cad..afc41109e9 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawGenerator.kt @@ -8,12 +8,11 @@ class MangaRawGenerator : ThemeSourceGenerator { override val themePkg = "mangaraw" - override val baseVersionCode = 4 + override val baseVersionCode = 5 override val sources = listOf( SingleLang("SyoSetu", "https://syosetu.top", "ja"), - SingleLang("MangaRaw", "https://manga1001.in", "ja", pkgName = "manga9co", overrideVersionCode = 2), - SingleLang("MangaRawRU", "https://mangaraw.ru", "ja", overrideVersionCode = 1), + SingleLang("MangaRaw", "https://manga1001.in", "ja", pkgName = "manga9co", isNsfw = true, overrideVersionCode = 2), ) companion object { diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawTheme.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawTheme.kt index bf463a527b..c84d214cb3 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawTheme.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangaraw/MangaRawTheme.kt @@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import okhttp3.HttpUrl.Companion.toHttpUrl import org.jsoup.nodes.Document import org.jsoup.nodes.Element import org.jsoup.select.Evaluator @@ -23,7 +24,11 @@ abstract class MangaRawTheme( override fun popularMangaFromElement(element: Element) = SManga.create().apply { setUrlWithoutDomain(element.selectFirst(Evaluator.Tag("a"))!!.attr("href")) - title = element.selectFirst(Evaluator.Tag("h3"))!!.text().sanitizeTitle() + + // Title could be missing. Uses URL as a last effort, ex: okaeri-alice-raw + title = element.selectFirst(Evaluator.Tag("h3"))?.text()?.sanitizeTitle() + ?: (baseUrl + url).toHttpUrl().pathSegments.first() + thumbnail_url = element.selectFirst(Evaluator.Tag("img"))?.absUrl("data-src") } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt deleted file mode 100644 index 34e35e203e..0000000000 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSar.kt +++ /dev/null @@ -1,301 +0,0 @@ -package eu.kanade.tachiyomi.multisrc.mangasar - -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.booleanOrNull -import kotlinx.serialization.json.floatOrNull -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import okhttp3.FormBody -import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Element -import org.jsoup.parser.Parser -import rx.Observable -import uy.kohesive.injekt.injectLazy -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.concurrent.TimeUnit - -abstract class MangaSar( - override val name: String, - override val baseUrl: String, - override val lang: String, -) : HttpSource() { - - override val supportsLatest = true - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(::searchIntercept) - .rateLimit(1, 2, TimeUnit.SECONDS) - .build() - - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("Accept", ACCEPT_HTML) - .add("Accept-Language", ACCEPT_LANGUAGE) - .add("Referer", "$baseUrl/") - - protected fun apiHeadersBuilder(): Headers.Builder = headersBuilder() - .set("Accept", ACCEPT) - .add("X-Requested-With", "XMLHttpRequest") - - private val apiHeaders: Headers by lazy { apiHeadersBuilder().build() } - - protected val json: Json by injectLazy() - - override fun popularMangaRequest(page: Int): Request { - return GET(baseUrl, headers) - } - - override fun popularMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - - val mangas = document.select(popularMangaSelector()) - .map(::popularMangaFromElement) - - return MangasPage(mangas, hasNextPage = false) - } - - protected open fun popularMangaSelector(): String = - "div:contains(Populares) ~ ul.mangasList li div.gridbox" - - protected open fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.select("div.title a").first()!!.text() - thumbnail_url = element.select("div.thumb img").first()!!.attr("abs:src") - setUrlWithoutDomain(element.select("a").first()!!.attr("href")) - } - - override fun latestUpdatesRequest(page: Int): Request { - val form = FormBody.Builder() - .add("pagina", page.toString()) - .build() - - val newHeaders = apiHeadersBuilder() - .add("Content-Length", form.contentLength().toString()) - .add("Content-Type", form.contentType().toString()) - .build() - - return POST("$baseUrl/jsons/news/chapters.json", newHeaders, form) - } - - override fun latestUpdatesParse(response: Response): MangasPage { - val result = response.parseAs() - - val latestMangas = result.releases - .map(::latestUpdatesFromObject) - .distinctBy { it.url } - - val hasNextPage = result.page.toInt() < result.totalPage!! - - return MangasPage(latestMangas, hasNextPage) - } - - protected fun latestUpdatesFromObject(release: MangaSarReleaseDto) = SManga.create().apply { - title = release.name.withoutEntities() - thumbnail_url = release.image - url = release.link - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val url = "$baseUrl/wp-json/site/search/".toHttpUrlOrNull()!!.newBuilder() - .addQueryParameter("keyword", query) - .addQueryParameter("type", "undefined") - .toString() - - return GET(url, apiHeaders) - } - - override fun searchMangaParse(response: Response): MangasPage { - val result = response.parseAs>() - - val searchResults = result.values.map(::searchMangaFromObject) - - return MangasPage(searchResults, hasNextPage = false) - } - - private fun searchMangaFromObject(manga: MangaSarTitleDto) = SManga.create().apply { - title = manga.title - thumbnail_url = manga.image - setUrlWithoutDomain(manga.url) - } - - override fun mangaDetailsParse(response: Response): SManga { - val document = response.asJsoup() - val infoElement = document.selectFirst("div.manga-single div.dados")!! - - return SManga.create().apply { - title = infoElement.selectFirst("h1")!!.text() - thumbnail_url = infoElement.selectFirst("div.thumb img")!!.attr("abs:src") - description = infoElement.selectFirst("div.sinopse")!!.text() - genre = infoElement.select("ul.generos li a span.button").joinToString { it.text() } - } - } - - override fun chapterListRequest(manga: SManga): Request = chapterListPaginatedRequest(manga.url) - - protected open fun chapterListPaginatedRequest(mangaUrl: String, page: Int = 1): Request { - val mangaId = mangaUrl.substringAfterLast("/") - - val newHeaders = apiHeadersBuilder() - .set("Referer", baseUrl + mangaUrl) - .build() - - val url = "$baseUrl/jsons/series/chapters_list.json".toHttpUrlOrNull()!!.newBuilder() - .addQueryParameter("page", page.toString()) - .addQueryParameter("order", "desc") - .addQueryParameter("id_s", mangaId) - .toString() - - return GET(url, newHeaders) - } - - override fun chapterListParse(response: Response): List { - val mangaUrl = response.request.header("Referer")!!.substringAfter(baseUrl) - - var result = response.parseAs() - - if (result.chapters.isNullOrEmpty()) { - return emptyList() - } - - val chapters = result.chapters!! - .map(::chapterFromObject) - .toMutableList() - - var page = result.page!! + 1 - val lastPage = result.totalPages - - while (++page <= lastPage!!) { - val nextPageRequest = chapterListPaginatedRequest(mangaUrl, page) - result = client.newCall(nextPageRequest).execute().parseAs() - - chapters += result.chapters!! - .map(::chapterFromObject) - .toMutableList() - } - - return chapters - } - - private fun chapterFromObject(chapter: MangaSarChapterDto): SChapter = SChapter.create().apply { - name = "Cap. " + (if (chapter.number.booleanOrNull != null) "0" else chapter.number.content) + - (if (chapter.name.isString) " - " + chapter.name.content else "") - chapter_number = chapter.number.floatOrNull ?: -1f - date_upload = chapter.dateCreated.substringBefore("T").toDate() - setUrlWithoutDomain(chapter.link) - } - - protected open fun pageListApiRequest(chapterUrl: String, serieId: String, token: String): Request { - val newHeaders = apiHeadersBuilder() - .set("Referer", chapterUrl) - .build() - - val url = "$baseUrl/jsons/series/images_list.json".toHttpUrlOrNull()!!.newBuilder() - .addQueryParameter("id_serie", serieId) - .addQueryParameter("secury", token) - .toString() - - return GET(url, newHeaders) - } - - override fun pageListParse(response: Response): List { - val document = response.asJsoup() - val apiParams = document.selectFirst("script:containsData(id_serie)")?.data() - ?: throw Exception(TOKEN_NOT_FOUND) - - val chapterUrl = response.request.url.toString() - val serieId = apiParams.substringAfter("\"") - .substringBefore("\"") - val token = TOKEN_REGEX.find(apiParams)!!.groupValues[1] - - val apiRequest = pageListApiRequest(chapterUrl, serieId, token) - val apiResponse = client.newCall(apiRequest).execute().parseAs() - - return apiResponse.images - .filter { it.url.startsWith("http") } - .mapIndexed { i, page -> Page(i, chapterUrl, page.url) } - } - - override fun fetchImageUrl(page: Page): Observable = Observable.just(page.imageUrl!!) - - override fun imageUrlParse(response: Response): String = "" - - override fun imageRequest(page: Page): Request { - val newHeaders = headersBuilder() - .set("Accept", ACCEPT_IMAGE) - .set("Referer", page.url) - .build() - - return GET(page.imageUrl!!, newHeaders) - } - - protected fun searchIntercept(chain: Interceptor.Chain): Response { - if (chain.request().url.toString().contains("/search/")) { - val homeRequest = popularMangaRequest(1) - val document = chain.proceed(homeRequest).asJsoup() - - val apiParams = document.select("script:containsData(pAPI)").first()!!.data() - .substringAfter("pAPI = ") - .substringBeforeLast(";") - .let { json.parseToJsonElement(it) } - .jsonObject - - val newUrl = chain.request().url.newBuilder() - .addQueryParameter("nonce", apiParams["nonce"]!!.jsonPrimitive.content) - .build() - - val newRequest = chain.request().newBuilder() - .url(newUrl) - .build() - - return chain.proceed(newRequest) - } - - return chain.proceed(chain.request()) - } - - protected inline fun Response.parseAs(): T = use { - json.decodeFromString(body.string()) - } - - protected fun String.toDate(): Long { - return try { - DATE_FORMATTER.parse(this)?.time ?: 0L - } catch (e: ParseException) { - 0L - } - } - - private fun String.withoutEntities(): String { - return Parser.unescapeEntities(this, true) - } - - companion object { - private const val ACCEPT = "application/json, text/plain, */*" - private const val ACCEPT_HTML = "text/html,application/xhtml+xml,application/xml;q=0.9," + - "image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9" - private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/*,*/*;q=0.8" - private const val ACCEPT_LANGUAGE = "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6,gl;q=0.5" - - private val TOKEN_REGEX = "token\\s+= \"(.*)\"".toRegex() - - private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } - - const val TOKEN_NOT_FOUND = "Não foi possível obter o token de leitura." - } -} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt deleted file mode 100644 index 432f64be2c..0000000000 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarDto.kt +++ /dev/null @@ -1,52 +0,0 @@ -package eu.kanade.tachiyomi.multisrc.mangasar - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonNames -import kotlinx.serialization.json.JsonPrimitive - -@Serializable -data class MangaSarLatestDto( - val page: String, - @JsonNames("lancamentos") val releases: List = emptyList(), - @SerialName("total_page") val totalPage: Int? = 0, -) - -@Serializable -data class MangaSarReleaseDto( - val image: String, - val link: String, - val name: String, -) - -@Serializable -data class MangaSarTitleDto( - @SerialName("img") val image: String, - val title: String, - val url: String, -) - -@Serializable -data class MangaSarPaginatedChaptersDto( - val chapters: List? = emptyList(), - @SerialName("pagina") val page: Int? = -1, - @SerialName("total_pags") val totalPages: Int? = -1, -) - -@Serializable -data class MangaSarChapterDto( - @SerialName("date_created") val dateCreated: String, - val link: String, - @SerialName("chapter_name") val name: JsonPrimitive, - val number: JsonPrimitive, -) - -@Serializable -data class MangaSarReaderDto( - val images: List = emptyList(), -) - -@Serializable -data class MangaSarPageDto( - val url: String, -) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt deleted file mode 100644 index 89d49bb0c4..0000000000 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangasar/MangaSarGenerator.kt +++ /dev/null @@ -1,26 +0,0 @@ -package eu.kanade.tachiyomi.multisrc.mangasar - -import generator.ThemeSourceData.SingleLang -import generator.ThemeSourceGenerator - -class MangaSarGenerator : ThemeSourceGenerator { - - override val themePkg = "mangasar" - - override val themeClass = "MangaSar" - - override val baseVersionCode: Int = 7 - - override val sources = listOf( - SingleLang("Mangazim", "https://mangazim.com", "pt-BR"), - SingleLang("MangásUp", "https://mangasup.net", "pt-BR", className = "MangasUp"), - SingleLang("Seemangas", "https://seemangas.com", "pt-BR", isNsfw = true), - ) - - companion object { - @JvmStatic - fun main(args: Array) { - MangaSarGenerator().createAll() - } - } -} diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt index d29b8fb2a8..b57b844d01 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesia.kt @@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.multisrc.mangathemesia import android.app.Application import android.content.SharedPreferences -import android.util.Log -import android.widget.Toast -import androidx.preference.EditTextPreference import androidx.preference.PreferenceScreen -import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen +import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA +import eu.kanade.tachiyomi.lib.randomua.getPrefUAType +import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.source.ConfigurableSource @@ -18,7 +18,6 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonPrimitive @@ -26,7 +25,6 @@ import okhttp3.FormBody import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull -import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -37,7 +35,6 @@ import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy -import java.io.IOException import java.lang.IllegalArgumentException import java.text.SimpleDateFormat import java.util.Locale @@ -60,82 +57,17 @@ abstract class MangaThemesia( override val supportsLatest = true - // override with true if you want useRandomUserAgentByDefault to be on by default for some source - protected open val useRandomUserAgentByDefault: Boolean = false - - protected open val filterIncludeUserAgent: List = listOf() - protected open val filterExcludeUserAgent: List = listOf() - - private var userAgent: String? = null - private var checkedUa = false - - protected val hasUaIntercept by lazy { - client.interceptors.toString().contains("uaIntercept") - } - - protected val uaIntercept = object : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val useRandomUa = preferences.getBoolean(PREF_KEY_RANDOM_UA, false) - val customUa = preferences.getString(PREF_KEY_CUSTOM_UA, "") - - try { - if (hasUaIntercept && (useRandomUa || customUa!!.isNotBlank())) { - Log.i("Extension_setting", "$TITLE_RANDOM_UA or $TITLE_CUSTOM_UA option is ENABLED") - - if (customUa!!.isNotBlank() && useRandomUa.not()) { - userAgent = customUa - } - - if (userAgent.isNullOrBlank() && !checkedUa) { - val uaResponse = chain.proceed(GET(UA_DB_URL)) - - if (uaResponse.isSuccessful) { - var listUserAgentString = - json.decodeFromString>>(uaResponse.body.string())["desktop"] - - if (filterIncludeUserAgent.isNotEmpty()) { - listUserAgentString = listUserAgentString!!.filter { - filterIncludeUserAgent.any { filter -> - it.contains(filter, ignoreCase = true) - } - } - } - if (filterExcludeUserAgent.isNotEmpty()) { - listUserAgentString = listUserAgentString!!.filterNot { - filterExcludeUserAgent.any { filter -> - it.contains(filter, ignoreCase = true) - } - } - } - userAgent = listUserAgentString!!.random() - checkedUa = true - } - - uaResponse.close() - } - - if (userAgent.isNullOrBlank().not()) { - val newRequest = chain.request().newBuilder() - .header("User-Agent", userAgent!!.trim()) - .build() - - return chain.proceed(newRequest) - } - } - - return chain.proceed(chain.request()) - } catch (e: Exception) { - throw IOException(e.message) - } - } + override val client: OkHttpClient by lazy { + network.cloudflareClient.newBuilder() + .setRandomUserAgent( + preferences.getPrefUAType(), + preferences.getPrefCustomUA(), + ) + .connectTimeout(10, TimeUnit.SECONDS) + .readTimeout(30, TimeUnit.SECONDS) + .build() } - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .addInterceptor(uaIntercept) - .connectTimeout(10, TimeUnit.SECONDS) - .readTimeout(30, TimeUnit.SECONDS) - .build() - open val projectPageString = "/project" // Popular (Search with popular order and nothing else) @@ -524,7 +456,9 @@ abstract class MangaThemesia( val links = response.asJsoup().select("a[itemprop=item]") // near the top of page: home > manga > current chapter if (links.size == 3) { - return links[1].attr("href").toHttpUrlOrNull()?.encodedPath + val newUrl = links[1].attr("href").toHttpUrlOrNull() ?: return null + val isNewMangaUrl = (baseMangaUrl.host == newUrl.host && pathLengthIs(newUrl, 2) && newUrl.pathSegments[0] == baseMangaUrl.pathSegments[0]) + if (isNewMangaUrl) return newUrl.pathSegments[1] } } } @@ -566,58 +500,7 @@ abstract class MangaThemesia( override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used") override fun setupPreferenceScreen(screen: PreferenceScreen) { - addRandomAndCustomUserAgentPreferences(screen) - } - - protected fun addRandomAndCustomUserAgentPreferences(screen: PreferenceScreen) { - if (!hasUaIntercept) { - return // Unable to change the user agent. Therefore the preferences won't be displayed. - } - - val prefRandomUserAgent = SwitchPreferenceCompat(screen.context).apply { - key = PREF_KEY_RANDOM_UA - title = TITLE_RANDOM_UA - summary = if (preferences.getBoolean(PREF_KEY_RANDOM_UA, useRandomUserAgentByDefault)) userAgent else "" - setDefaultValue(useRandomUserAgentByDefault) - - setOnPreferenceChangeListener { _, newValue -> - val useRandomUa = newValue as Boolean - preferences.edit().putBoolean(PREF_KEY_RANDOM_UA, useRandomUa).apply() - if (!useRandomUa) { - Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show() - } else { - userAgent = null - if (preferences.getString(PREF_KEY_CUSTOM_UA, "").isNullOrBlank().not()) { - Toast.makeText(screen.context, SUMMARY_CLEANING_CUSTOM_UA, Toast.LENGTH_LONG).show() - } - } - - preferences.edit().putString(PREF_KEY_CUSTOM_UA, "").apply() - true - } - } - - val prefCustomUserAgent = EditTextPreference(screen.context).apply { - key = PREF_KEY_CUSTOM_UA - title = TITLE_CUSTOM_UA - summary = preferences.getString(PREF_KEY_CUSTOM_UA, "")!!.trim() - setOnPreferenceChangeListener { _, newValue -> - val customUa = newValue as String - preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply() - if (customUa.isBlank()) { - Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show() - } else { - userAgent = null - } - summary = customUa.trim() - prefRandomUserAgent.summary = "" - prefRandomUserAgent.isChecked = false - true - } - } - - screen.addPreference(prefRandomUserAgent) - screen.addPreference(prefCustomUserAgent) + addRandomUAPreferenceToScreen(screen) } companion object { @@ -629,16 +512,5 @@ abstract class MangaThemesia( private val CHAPTER_PAGE_ID_REGEX = "chapter_id\\s*=\\s*(\\d+);".toRegex() val JSON_IMAGE_LIST_REGEX = "\"images\"\\s*:\\s*(\\[.*?])".toRegex() - - const val TITLE_RANDOM_UA = "Use Random Latest User-Agent" - const val PREF_KEY_RANDOM_UA = "pref_key_random_ua" - - const val TITLE_CUSTOM_UA = "Custom User-Agent" - const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua" - - const val SUMMARY_CLEANING_CUSTOM_UA = "$TITLE_CUSTOM_UA cleared." - - const val RESTART_APP_STRING = "Restart Tachiyomi to apply new setting." - private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json" } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt index 073eba557f..37ae587fe2 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mangathemesia/MangaThemesiaGenerator.kt @@ -11,7 +11,7 @@ class MangaThemesiaGenerator : ThemeSourceGenerator { override val themeClass = "MangaThemesia" - override val baseVersionCode: Int = 25 + override val baseVersionCode: Int = 26 override val sources = listOf( @@ -23,28 +23,27 @@ class MangaThemesiaGenerator : ThemeSourceGenerator { SingleLang("Origami Orpheans", "https://origami-orpheans.com.br", "pt-BR", overrideVersionCode = 10), // Extenções Oficiais: - MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 23), + MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 26), MultiLang("Flame Scans", "https://flamescans.org", listOf("en"), className = "FlameScansFactory", pkgName = "flamescans", overrideVersionCode = 4), MultiLang("Komik Lab", "https://komiklab.com", listOf("en", "id"), className = "KomikLabFactory", pkgName = "komiklab", overrideVersionCode = 2), - MultiLang("Miau Scan", "https://miauscan.com", listOf("es", "pt-BR")), + MultiLang("Miau Scan", "https://miauscans.com", listOf("es", "pt-BR"), overrideVersionCode = 1), SingleLang("Animated Glitched Scans", "https://anigliscans.com", "en"), - SingleLang("Arcane scan", "https://arcanescan.fr", "fr"), SingleLang("Arena Scans", "https://arenascans.net", "en", overrideVersionCode = 1), + SingleLang("Arkham Scan", "https://arkhamscan.com", "pt-BR"), SingleLang("Azure Scans", "https://azuremanga.com", "en", overrideVersionCode = 1), SingleLang("Banana-Scan", "https://banana-scan.com", "fr", className = "BananaScan", isNsfw = true), + SingleLang("Beast Scans", "https://beast-scans.com", "ar"), SingleLang("Boosei", "https://boosei.net", "id", overrideVersionCode = 2), - SingleLang("Babel Wuxia", "https://babelwuxia.com", "en", overrideVersionCode = 1), SingleLang("Cartel de Manhwas", "https://carteldemanhwas.com", "es", overrideVersionCode = 5), - SingleLang("Clayrer", "https://clayrer.net", "es"), - SingleLang("Constellar Scans", "https://constellarscans.com", "en", isNsfw = true, overrideVersionCode = 14), SingleLang("Cosmic Scans", "https://cosmicscans.com", "en", overrideVersionCode = 1), SingleLang("CosmicScans.id", "https://cosmicscans.id", "id", overrideVersionCode = 1, className = "CosmicScansID"), SingleLang("Diskus Scan", "https://diskusscan.com", "pt-BR", overrideVersionCode = 7), SingleLang("Dojing.net", "https://dojing.net", "id", isNsfw = true, className = "DojingNet"), - SingleLang("DragonTranslation", "https://dragontranslation.com", "es", isNsfw = true, overrideVersionCode = 9), - SingleLang("DuniaKomik.id", "https://duniakomik.id", "id", className = "DuniaKomikId"), + SingleLang("DuniaKomik.id", "https://duniakomik.org", "id", className = "DuniaKomikId", overrideVersionCode = 2), SingleLang("ElarcPage", "https://elarcpage.com", "en"), + SingleLang("EnryuManga", "https://enryumanga.com", "en"), SingleLang("Epsilon Scan", "https://epsilonscan.fr", "fr", isNsfw = true), + SingleLang("Fairy Manga", "https://fairymanga.com", "en", className = "QueenScans", overrideVersionCode = 1), SingleLang("Franxx Mangás", "https://franxxmangas.net", "pt-BR", className = "FranxxMangas", isNsfw = true), SingleLang("Gecenin Lordu", "https://geceninlordu.com", "tr", overrideVersionCode = 1), SingleLang("GoGoManga", "https://gogomanga.fun", "en", overrideVersionCode = 1), @@ -56,10 +55,10 @@ class MangaThemesiaGenerator : ThemeSourceGenerator { SingleLang("KataKomik", "https://katakomik.online", "id"), SingleLang("Komik Seru", "https://komikseru.me", "id", isNsfw = true), SingleLang("Kanzenin", "https://kanzenin.xyz", "id", isNsfw = true), - SingleLang("KomikSan", "https://komiksan.ml", "id", overrideVersionCode = 1), + SingleLang("Komiksan", "https://komiksan.link", "id", overrideVersionCode = 2), SingleLang("Kiryuu", "https://kiryuu.id", "id", overrideVersionCode = 6), SingleLang("Komik AV", "https://komikav.com", "id", overrideVersionCode = 1), - SingleLang("Komik Cast", "https://komikcast.io", "id", overrideVersionCode = 19), + SingleLang("Komik Cast", "https://komikcast.io", "id", overrideVersionCode = 20), SingleLang("KomikDewasa", "https://komikdewasa.org", "id", isNsfw = true), SingleLang("Komik Station", "https://komikstation.co", "id", overrideVersionCode = 3), SingleLang("KomikIndo.co", "https://komikindo.co", "id", className = "KomikindoCo", overrideVersionCode = 3), @@ -69,43 +68,49 @@ class MangaThemesiaGenerator : ThemeSourceGenerator { SingleLang("Komiku.com", "https://komiku.com", "id", className = "KomikuCom"), SingleLang("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en", className = "KumaScans", overrideVersionCode = 1), SingleLang("Legacy Scans", "https://legacy-scans.com", "fr", pkgName = "flamescansfr"), + SingleLang("Legion Scan", "https://legionscans.com", "es", overrideVersionCode = 6), + SingleLang("Lelmanga", "https://www.lelmanga.com", "fr"), SingleLang("LianScans", "https://www.lianscans.my.id", "id", isNsfw = true), SingleLang("Lunar Scans", "https://lunarscan.org", "en", isNsfw = true), + SingleLang("LynxScans", "https://lynxscans.com", "en"), SingleLang("Magus Manga", "https://magusmanga.com", "ar"), SingleLang("Manga Indo.me", "https://mangaindo.me", "id", className = "MangaIndoMe"), SingleLang("Manga Raw.org", "https://mangaraw.org", "ja", className = "MangaRawOrg", overrideVersionCode = 1), SingleLang("Mangacim", "https://www.mangacim.com", "tr", overrideVersionCode = 1), SingleLang("MangaKita", "https://mangakita.net", "id", overrideVersionCode = 1), - SingleLang("Mangakyo", "https://mangakyo.id", "id", overrideVersionCode = 1), + SingleLang("Mangakyo", "https://mangakyo.org", "id", overrideVersionCode = 2), + SingleLang("MangaShiina", "https://mangashiina.com", "es"), SingleLang("Mangasusu", "https://mangasusuku.xyz/", "id", isNsfw = true, overrideVersionCode = 2), SingleLang("MangaTale", "https://mangatale.co", "id"), SingleLang("MangaWT", "https://mangawt.com", "tr", overrideVersionCode = 5), SingleLang("Mangayaro", "https://mangayaro.net", "id"), SingleLang("Manhwa Lover", "https://manhwalover.com", "en", isNsfw = true, overrideVersionCode = 1), - SingleLang("MangaSwat", "https://swatmanga.me", "ar", overrideVersionCode = 9), + SingleLang("MangaSwat", "https://swatmanga.co", "ar", overrideVersionCode = 13), SingleLang("MangKomik", "https://mangkomik.net", "id", overrideVersionCode = 1), - SingleLang("Manhwa Freak", "https://manhwafreak.com", "en", overrideVersionCode = 1), + SingleLang("Manhwa Freak", "https://manhwa-freak.com", "en", overrideVersionCode = 2), + SingleLang("ManhwaFreak", "https://manhwafreak.fr", "fr", className = "ManhwaFreakFR"), SingleLang("ManhwaDesu", "https://manhwadesu.org", "id", isNsfw = true, overrideVersionCode = 3), SingleLang("ManhwaIndo", "https://manhwaindo.id", "id", isNsfw = true, overrideVersionCode = 2), SingleLang("ManhwaLand.mom", "https://manhwaland.us", "id", isNsfw = true, className = "ManhwaLandMom", overrideVersionCode = 4), SingleLang("ManhwaList", "https://manhwalist.xyz", "id", overrideVersionCode = 3), SingleLang("Manhwax", "https://manhwax.com", "en", isNsfw = true), SingleLang("Mareceh", "https://mareceh.com", "id", isNsfw = true, pkgName = "mangceh", overrideVersionCode = 10), - SingleLang("MasterKomik", "https://masterkomik.com", "id", overrideVersionCode = 1), SingleLang("MELOKOMIK", "https://melokomik.xyz", "id"), SingleLang("Mihentai", "https://mihentai.com", "all", isNsfw = true, overrideVersionCode = 2), SingleLang("MirrorDesu", "https://mirrordesu.me", "id", isNsfw = true), - SingleLang("Mode Scanlator", "https://modescanlator.com", "pt-BR", overrideVersionCode = 8), - SingleLang("Nekomik", "https://nekomik.com", "id"), + SingleLang("Moon Daisy Scans", "https://moondaisyscans.com", "tr", isNsfw = true), + SingleLang("Nekomik", "https://nekomik.me", "id", overrideVersionCode = 1), SingleLang("Ngomik", "https://ngomik.net", "id", overrideVersionCode = 2), SingleLang("NIGHT SCANS", "https://nightscans.org", "en", isNsfw = true, className = "NightScans", overrideVersionCode = 1), SingleLang("Nocturnal Scans", "https://nocturnalscans.com", "en", overrideVersionCode = 1), - SingleLang("Ozul Scans", "https://ozulscans.com", "ar"), - SingleLang("Patatescans", "https://patatescans.com", "fr", isNsfw = true, overrideVersionCode = 2), + SingleLang("Noromax", "https://noromax.my.id", "id"), + SingleLang("OPSCANS", "https://opscans.com", "all"), + SingleLang("Ozul Scans", "https://ozulscans.xyz", "ar", overrideVersionCode = 1), SingleLang("Phantom Scans", "https://phantomscans.com", "en", overrideVersionCode = 1), SingleLang("PhenixScans", "https://phenixscans.fr", "fr", className = "PhenixScans", overrideVersionCode = 1), SingleLang("Pi Scans", "https://piscans.in", "id", overrideVersionCode = 1), SingleLang("PMScans", "https://rackusreads.com", "en", overrideVersionCode = 3), + SingleLang("PotatoManga", "https://potatomanga.xyz", "ar"), SingleLang("Raiki Scan", "https://raikiscan.com", "es"), SingleLang("Raven Scans", "https://ravenscans.com", "en", overrideVersionCode = 1), SingleLang("Rawkuma", "https://rawkuma.com/", "ja"), @@ -117,21 +122,31 @@ class MangaThemesiaGenerator : ThemeSourceGenerator { SingleLang("Senpai Ediciones", "http://senpaiediciones.com", "es", overrideVersionCode = 1), SingleLang("Shadow Mangas", "https://shadowmangas.com", "es", overrideVersionCode = 1), SingleLang("Shea Manga", "https://sheakomik.com", "id", overrideVersionCode = 4), + SingleLang("SkyMangas", "https://skymangas.com", "es"), SingleLang("Snudae Scans", "https://snudaescans.com", "en", isNsfw = true, className = "BatotoScans", overrideVersionCode = 1), + SingleLang("SSSScanlator", "https://sssscanlator.com", "pt-BR"), + SingleLang("Starlight Scan", "https://starligthscan.com", "pt-BR", isNsfw = true), SingleLang("Summer Fansub", "https://smmr.in", "pt-BR", isNsfw = true), + SingleLang("SummerToon", "https://summertoon.com", "tr"), SingleLang("Surya Scans", "https://suryascans.com", "en"), - SingleLang("Sushi-Scan", "https://sushiscan.net", "fr", className = "SushiScan", overrideVersionCode = 5), + SingleLang("Sushi-Scan", "https://sushiscan.net", "fr", className = "SushiScan", overrideVersionCode = 6), + SingleLang("Sushiscan.fr", "https://sushiscan.fr", "fr", className = "SushiScanFR"), SingleLang("Tarot Scans", "https://www.tarotscans.com", "tr"), + SingleLang("TenkaiScan", "https://tenkaiscan.net", "es", isNsfw = true), + SingleLang("Tenshi.id", "https://tenshi.id", "id", className = "TenshiId", pkgName = "masterkomik", overrideVersionCode = 3), SingleLang("The Apollo Team", "https://theapollo.team", "en"), SingleLang("Tsundoku Traduções", "https://tsundoku.com.br", "pt-BR", className = "TsundokuTraducoes", overrideVersionCode = 9), SingleLang("TukangKomik", "https://tukangkomik.id", "id", overrideVersionCode = 1), SingleLang("TurkToon", "https://turktoon.com", "tr"), - SingleLang("Uzay Manga", "https://uzaymanga.com", "tr", overrideVersionCode = 4), + SingleLang("Uzay Manga", "https://uzaymanga.com", "tr", overrideVersionCode = 5), SingleLang("Walpurgi Scan", "https://www.walpurgiscan.com", "it", overrideVersionCode = 6, className = "WalpurgisScan", pkgName = "walpurgisscan"), SingleLang("West Manga", "https://westmanga.info", "id", overrideVersionCode = 1), SingleLang("World Romance Translation", "https://wrt.my.id", "id", overrideVersionCode = 10), SingleLang("xCaliBR Scans", "https://xcalibrscans.com", "en", overrideVersionCode = 4), + SingleLang("Zahard", "https://zahard.xyz", "en"), SingleLang("สดใสเมะ", "https://www.xn--l3c0azab5a2gta.com", "th", isNsfw = true, className = "Sodsaime", overrideVersionCode = 1), + SingleLang("أريا مانجا", "https://www.areascans.net", "ar", className = "AreaManga"), + SingleLang("فيكس مانجا", "https://vexmanga.net", "ar", className = "VexManga"), ) companion object { diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/MMRCMSGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/MMRCMSGenerator.kt index b398c91c1f..a012c97b4f 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/MMRCMSGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/MMRCMSGenerator.kt @@ -15,22 +15,18 @@ class MMRCMSGenerator : ThemeSourceGenerator { SingleLang("مانجا اون لاين", "https://onma.top", "ar", className = "onma"), SingleLang("Read Comics Online", "https://readcomicsonline.ru", "en"), SingleLang("Fallen Angels", "https://manga.fascans.com", "en", overrideVersionCode = 2), - SingleLang("Zahard", "https://zahard.xyz", "en", overrideVersionCode = 2), SingleLang("Scan FR", "https://www.scan-fr.org", "fr", overrideVersionCode = 2), SingleLang("Scan VF", "https://www.scan-vf.net", "fr", overrideVersionCode = 1), - SingleLang("Scan OP", "https://scan-op.cc", "fr"), SingleLang("Komikid", "https://www.komikid.com", "id"), SingleLang("Mangadoor", "https://mangadoor.com", "es", overrideVersionCode = 1), SingleLang("Utsukushii", "https://manga.utsukushii-bg.com", "bg", overrideVersionCode = 1), SingleLang("Phoenix-Scans", "https://phoenix-scans.pl", "pl", className = "PhoenixScans", overrideVersionCode = 1), - SingleLang("Scan-1", "https://wwv.scan-1.com", "fr", className = "ScanOne", overrideVersionCode = 2), - SingleLang("Lelscan-VF", "https://lelscanvf.com", "fr", className = "LelscanVF", overrideVersionCode = 1), + SingleLang("Lelscan-VF", "https://lelscanvf.cc", "fr", className = "LelscanVF", overrideVersionCode = 2), SingleLang("AnimaRegia", "https://animaregia.net", "pt-BR", overrideVersionCode = 4), SingleLang("MangaID", "https://mangaid.click", "id", overrideVersionCode = 1), - SingleLang("Jpmangas", "https://jpmangas.cc", "fr", overrideVersionCode = 1), - SingleLang("Op-VF", "https://www.op-vf.com", "fr", className = "OpVF"), - SingleLang("FR Scan", "https://frscan.ws", "fr", overrideVersionCode = 2), - SingleLang("Manga-FR", "https://manga-fr.me", "fr", className = "MangaFR"), + SingleLang("Jpmangas", "https://jpmangas.xyz", "fr", overrideVersionCode = 2), + SingleLang("Manga-FR", "https://manga-fr.me", "fr", className = "MangaFR", overrideVersionCode = 1), + SingleLang("Manga-Scan", "https://manga-scan.co", "fr", className = "MangaScan"), SingleLang("Ama Scans", "https://amascan.com", "pt-BR", isNsfw = true, overrideVersionCode = 2), // NOTE: THIS SOURCE CONTAINS A CUSTOM LANGUAGE SYSTEM (which will be ignored)! SingleLang("HentaiShark", "https://www.hentaishark.com", "all", isNsfw = true), diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/SourceData.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/SourceData.kt index d80fd3330a..7f3a07a494 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/SourceData.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/mmrcms/SourceData.kt @@ -11,23 +11,20 @@ object SourceData { "https://zahard.xyz" -> """{"name":"Zahard","base_url":"https://zahard.xyz","supports_latest":true,"item_url":"https://zahard.xyz/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" "https://www.scan-fr.org" -> """{"name":"Scan FR","base_url":"https://www.scan-fr.org","supports_latest":true,"item_url":"https://www.scan-fr.org/manga/","categories":[{"id":"1","name":"Comedy"},{"id":"2","name":"Doujinshi"},{"id":"3","name":"Drama"},{"id":"4","name":"Ecchi"},{"id":"5","name":"Fantasy"},{"id":"6","name":"Gender Bender"},{"id":"7","name":"Josei"},{"id":"8","name":"Mature"},{"id":"9","name":"Mecha"},{"id":"10","name":"Mystery"},{"id":"11","name":"One Shot"},{"id":"12","name":"Psychological"},{"id":"13","name":"Romance"},{"id":"14","name":"School Life"},{"id":"15","name":"Sci-fi"},{"id":"16","name":"Seinen"},{"id":"17","name":"Shoujo"},{"id":"18","name":"Shoujo Ai"},{"id":"19","name":"Shounen"},{"id":"20","name":"Shounen Ai"},{"id":"21","name":"Slice of Life"},{"id":"22","name":"Sports"},{"id":"23","name":"Supernatural"},{"id":"24","name":"Tragedy"},{"id":"25","name":"Yaoi"},{"id":"26","name":"Yuri"},{"id":"27","name":"Comics"},{"id":"28","name":"Autre"},{"id":"29","name":"BD Occidentale"},{"id":"30","name":"Manhwa"},{"id":"31","name":"Action"},{"id":"32","name":"Aventure"}]}""" "https://www.scan-vf.net" -> """{"name":"Scan VF","base_url":"https://www.scan-vf.net","supports_latest":true,"item_url":"https://www.scan-vf.net/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" - "https://scan-op.cc" -> """{"name":"Scan OP","base_url":"https://scan-op.cc","supports_latest":true,"item_url":"https://scan-op.cc/manga/","categories":[{"id":"1","name":"Comedy"},{"id":"2","name":"Doujinshi"},{"id":"3","name":"Drama"},{"id":"4","name":"Ecchi"},{"id":"5","name":"Fantasy"},{"id":"6","name":"Gender Bender"},{"id":"7","name":"Josei"},{"id":"8","name":"Mature"},{"id":"9","name":"Mecha"},{"id":"10","name":"Mystery"},{"id":"11","name":"One Shot"},{"id":"12","name":"Psychological"},{"id":"13","name":"Romance"},{"id":"14","name":"School Life"},{"id":"15","name":"Sci-fi"},{"id":"16","name":"Seinen"},{"id":"17","name":"Shoujo"},{"id":"18","name":"Shoujo Ai"},{"id":"19","name":"Shounen"},{"id":"20","name":"Shounen Ai"},{"id":"21","name":"Slice of Life"},{"id":"22","name":"Sports"},{"id":"23","name":"Supernatural"},{"id":"24","name":"Tragedy"},{"id":"25","name":"Yaoi"},{"id":"26","name":"Yuri"},{"id":"27","name":"Comics"},{"id":"28","name":"Autre"}],"tags":[{"id":"nouveau","name":"nouveau"}]}""" "https://www.komikid.com" -> """{"name":"Komikid","base_url":"https://www.komikid.com","supports_latest":true,"item_url":"https://www.komikid.com/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Fantasy"},{"id":"7","name":"Gender Bender"},{"id":"8","name":"Historical"},{"id":"9","name":"Horror"},{"id":"10","name":"Josei"},{"id":"11","name":"Martial Arts"},{"id":"12","name":"Mature"},{"id":"13","name":"Mecha"},{"id":"14","name":"Mystery"},{"id":"15","name":"One Shot"},{"id":"16","name":"Psychological"},{"id":"17","name":"Romance"},{"id":"18","name":"School Life"},{"id":"19","name":"Sci-fi"},{"id":"20","name":"Seinen"},{"id":"21","name":"Shoujo"},{"id":"22","name":"Shoujo Ai"},{"id":"23","name":"Shounen"},{"id":"24","name":"Shounen Ai"},{"id":"25","name":"Slice of Life"},{"id":"26","name":"Sports"},{"id":"27","name":"Supernatural"},{"id":"28","name":"Tragedy"},{"id":"29","name":"Yaoi"},{"id":"30","name":"Yuri"}]}""" "http://azbivo.webd.pro" -> """{"name":"Nikushima","base_url":"http://azbivo.webd.pro","supports_latest":false,"item_url":"\u003chtml\u003e \n \u003chead\u003e \n \u003cmeta http-equiv\u003d\"Content-Language\" content\u003d\"pl\"\u003e \n \u003cmeta http-equiv name\u003d\"pragma\" content\u003d\"no-cache\"\u003e \n \u003clink href\u003d\"style/style.css\" rel\u003d\"stylesheet\" type\u003d\"text/css\"\u003e \n \u003cmeta http-equiv\u003d\"Refresh\" content\u003d\"0; url\u003dhttps://www.webd.pl/_errnda.php?utm_source\u003dwn07\u0026amp;utm_medium\u003dwww\u0026amp;utm_campaign\u003dblock\"\u003e \n \u003cmeta name\u003d\"Robots\" content\u003d\"index, follow\"\u003e \n \u003cmeta name\u003d\"revisit-after\" content\u003d\"2 days\"\u003e \n \u003cmeta name\u003d\"rating\" content\u003d\"general\"\u003e \n \u003cmeta name\u003d\"keywords\" content\u003d\"STRONA ZAWIESZONA, WEBD, DOMENY, DOMENA, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, NET, .COM, .ORG, TANIE, PHP+MySQL, DOMENY, DOMENA, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, DOMENY, DOMENA, NET, .COM, .ORG, TANIE, PHP+MySQL, HOSTING, SERWER, INTERNET, PHP, MySQL, FTP, WEBMASTER, SERWERY WIRTUALNE, WWW, KONTO, MAIL, POCZTA, E-MAIL, NET, .COM, .ORG, TANIE, PHP+MySQL\"\u003e \n \u003cmeta name\u003d\"description\" content\u003d\"STRONA ZAWIESZONA - Oferujemy profesjonalny hosting z PHP + MySQL, rejestrujemy domeny. Sprawdz nasz hosting i przetestuj nasze serwery. Kupuj tanio domeny i serwery!\"\u003e \n \u003ctitle\u003eSTRONA ZAWIESZONA - WEBD.PL - Tw�j profesjonalny hosting za jedyne 4.99PLN! Serwery z PHP+MySQL, tanie domeny, serwer + domena .pl - taniej sie nie da!\u003c/title\u003e \n \u003cscript type\u003d\"text/javascript\"\u003e\nfunction init() {\n if (!document.getElementById) return\n var imgOriginSrc;\n var imgTemp \u003d new Array();\n var imgarr \u003d document.getElementsByTagName(\u0027img\u0027);\n for (var i \u003d 0; i \u003c imgarr.length; i++) {\n if (imgarr[i].getAttribute(\u0027hsrc\u0027)) {\n imgTemp[i] \u003d new Image();\n imgTemp[i].src \u003d imgarr[i].getAttribute(\u0027hsrc\u0027);\n imgarr[i].onmouseover \u003d function() {\n imgOriginSrc \u003d this.getAttribute(\u0027src\u0027);\n this.setAttribute(\u0027src\u0027,this.getAttribute(\u0027hsrc\u0027))\n }\n imgarr[i].onmouseout \u003d function() {\n this.setAttribute(\u0027src\u0027,imgOriginSrc)\n }\n }\n }\n}\nonload\u003dinit;\n\u003c/script\u003e \n \u003c/head\u003e \n \u003cbody\u003e\n Trwa przekierowanie .... \u0026gt;\u0026gt;\u0026gt;\u0026gt; \u003c!--\n--\u003e \n \u003c/body\u003e\n\u003c/html\u003e/","categories":[]}""" "https://mangadoor.com" -> """{"name":"Mangadoor","base_url":"https://mangadoor.com","supports_latest":true,"item_url":"https://mangadoor.com/manga/","categories":[{"id":"1","name":"Acción"},{"id":"2","name":"Aventura"},{"id":"3","name":"Comedia"},{"id":"4","name":"Drama"},{"id":"5","name":"Ecchi"},{"id":"6","name":"Fantasía"},{"id":"7","name":"Gender Bender"},{"id":"8","name":"Harem"},{"id":"9","name":"Histórico"},{"id":"10","name":"Horror"},{"id":"11","name":"Josei"},{"id":"12","name":"Artes Marciales"},{"id":"13","name":"Maduro"},{"id":"14","name":"Mecha"},{"id":"15","name":"Misterio"},{"id":"16","name":"One Shot"},{"id":"17","name":"Psicológico"},{"id":"18","name":"Romance"},{"id":"19","name":"Escolar"},{"id":"20","name":"Ciencia Ficción"},{"id":"21","name":"Seinen"},{"id":"22","name":"Shoujo"},{"id":"23","name":"Shoujo Ai"},{"id":"24","name":"Shounen"},{"id":"25","name":"Shounen Ai"},{"id":"26","name":"Recuentos de la vida"},{"id":"27","name":"Deportes"},{"id":"28","name":"Supernatural"},{"id":"29","name":"Tragedia"},{"id":"30","name":"Yaoi"},{"id":"31","name":"Yuri"},{"id":"32","name":"Demonios"},{"id":"33","name":"Juegos"},{"id":"34","name":"Policial"},{"id":"35","name":"Militar"},{"id":"36","name":"Thriller"},{"id":"37","name":"Autos"},{"id":"38","name":"Música"},{"id":"39","name":"Vampiros"},{"id":"40","name":"Magia"},{"id":"41","name":"Samurai"},{"id":"42","name":"Boys love"},{"id":"43","name":"Hentai"}]}""" "https://mangas.in" -> """{"name":"Mangas.pw","base_url":"https://mangas.in","supports_latest":true,"item_url":"https://mangas.in/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"},{"id":"33","name":"Hentai"},{"id":"34","name":"Smut"}]}""" "https://manga.utsukushii-bg.com" -> """{"name":"Utsukushii","base_url":"https://manga.utsukushii-bg.com","supports_latest":true,"item_url":"https://manga.utsukushii-bg.com/manga/","categories":[{"id":"1","name":"Екшън"},{"id":"2","name":"Приключенски"},{"id":"3","name":"Комедия"},{"id":"4","name":"Драма"},{"id":"5","name":"Фентъзи"},{"id":"6","name":"Исторически"},{"id":"7","name":"Ужаси"},{"id":"8","name":"Джосей"},{"id":"9","name":"Бойни изкуства"},{"id":"10","name":"Меха"},{"id":"11","name":"Мистерия"},{"id":"12","name":"Самостоятелна/Пилотна глава"},{"id":"13","name":"Психологически"},{"id":"14","name":"Романтика"},{"id":"15","name":"Училищни"},{"id":"16","name":"Научна фантастика"},{"id":"17","name":"Сейнен"},{"id":"18","name":"Шоджо"},{"id":"19","name":"Реализъм"},{"id":"20","name":"Спорт"},{"id":"21","name":"Свръхестествено"},{"id":"22","name":"Трагедия"},{"id":"23","name":"Йокаи"},{"id":"24","name":"Паралелна вселена"},{"id":"25","name":"Супер сили"},{"id":"26","name":"Пародия"},{"id":"27","name":"Шонен"}]}""" "https://phoenix-scans.pl" -> """{"name":"Phoenix-Scans","base_url":"https://phoenix-scans.pl","supports_latest":true,"item_url":"https://phoenix-scans.pl/manga/","categories":[{"id":"1","name":"Shounen"},{"id":"2","name":"Tragedia"},{"id":"3","name":"Szkolne życie"},{"id":"4","name":"Romans"},{"id":"5","name":"Zagadka"},{"id":"6","name":"Horror"},{"id":"7","name":"Dojrzałe"},{"id":"8","name":"Psychologiczne"},{"id":"9","name":"Przygodowe"},{"id":"10","name":"Akcja"},{"id":"11","name":"Komedia"},{"id":"12","name":"Zboczone"},{"id":"13","name":"Fantasy"},{"id":"14","name":"Harem"},{"id":"15","name":"Historyczne"},{"id":"16","name":"Manhua"},{"id":"17","name":"Manhwa"},{"id":"18","name":"Sztuki walki"},{"id":"19","name":"One shot"},{"id":"20","name":"Sci fi"},{"id":"21","name":"Seinen"},{"id":"22","name":"Shounen ai"},{"id":"23","name":"Spokojne życie"},{"id":"24","name":"Sport"},{"id":"25","name":"Nadprzyrodzone"},{"id":"26","name":"Webtoons"},{"id":"27","name":"Dramat"},{"id":"28","name":"Hentai"},{"id":"29","name":"Mecha"},{"id":"30","name":"Gender Bender"},{"id":"31","name":"Gry"},{"id":"32","name":"Yaoi"}],"tags":[{"id":"aktywne","name":"aktywne"},{"id":"zakonczone","name":"zakończone"},{"id":"porzucone","name":"porzucone"},{"id":"zawieszone","name":"zawieszone"},{"id":"zlicencjonowane","name":"zlicencjonowane"},{"id":"hentai","name":"Hentai"}]}""" - "https://wwv.scan-1.com" -> """{"name":"Scan-1","base_url":"https://wwv.scan-1.com","supports_latest":true,"item_url":"https://wwv.scan-1.com/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" - "https://lelscanvf.com" -> """{"name":"Lelscan-VF","base_url":"https://lelscanvf.com","supports_latest":true,"item_url":"https://lelscanvf.com/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" + "https://lelscanvf.cc" -> """{"name":"Lelscan-VF","base_url":"https://lelscanvf.cc","supports_latest":true,"item_url":"https://lelscanvf.cc/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" "https://animaregia.net" -> """{"name":"AnimaRegia","base_url":"https://animaregia.net","supports_latest":true,"item_url":"http://animaregia.net/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" "https://mangaid.click" -> """{"name":"MangaID","base_url":"https://mangaid.click","supports_latest":true,"item_url":"https://mangaid.click/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"Psychological"},{"id":"18","name":"Romance"},{"id":"19","name":"School Life"},{"id":"20","name":"Sci-fi"},{"id":"21","name":"Seinen"},{"id":"22","name":"Shoujo"},{"id":"23","name":"Shoujo Ai"},{"id":"24","name":"Shounen"},{"id":"25","name":"Shounen Ai"},{"id":"26","name":"Slice of Life"},{"id":"27","name":"Sports"},{"id":"28","name":"Supernatural"},{"id":"29","name":"Tragedy"},{"id":"30","name":"Yaoi"},{"id":"31","name":"Yuri"},{"id":"32","name":"School"},{"id":"33","name":"Isekai"},{"id":"34","name":"Military"}]}""" - "https://jpmangas.cc" -> """{"name":"Jpmangas","base_url":"https://jpmangas.cc","supports_latest":true,"item_url":"https://jpmangas.cc/lecture-en-ligne/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" - "https://www.op-vf.com" -> """{"name":"Op-VF","base_url":"https://www.op-vf.com","supports_latest":true,"item_url":"https://www.op-vf.com/manga/","categories":[]}""" - "https://frscan.ws" -> """{"name":"FR Scan","base_url":"https://frscan.ws","supports_latest":true,"item_url":"https://frscan.ws/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"Vie Scolaire"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Tranche de vie"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedie"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"},{"id":"33","name":"Autre"},{"id":"34","name":"BD Occidentale"},{"id":"35","name":"Webtoon"}]}""" + "https://jpmangas.xyz" -> """{"name":"Jpmangas","base_url":"https://jpmangas.xyz","supports_latest":true,"item_url":"https://jpmangas.xyz/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Adventure"},{"id":"3","name":"Comedy"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historical"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"}]}""" "https://www.hentaishark.com" -> """{"name":"HentaiShark","base_url":"https://www.hentaishark.com","supports_latest":true,"item_url":"https://www.hentaishark.com/manga/","categories":[{"id":"1","name":"Doujinshi"},{"id":"2","name":"Manga"},{"id":"3","name":"Western"},{"id":"4","name":"non-h"},{"id":"5","name":"imageset"},{"id":"6","name":"artistcg"},{"id":"7","name":"misc"}]}""" "https://amascan.com" -> """{"name":"Ama Scans","base_url":"https://amascan.com","supports_latest":true,"item_url":"https://amascan.com/manga/","categories":[{"id":"1","name":"Ação"},{"id":"2","name":"Aventura"},{"id":"3","name":"Comédia"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drama"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasia"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harém"},{"id":"10","name":"Histórico"},{"id":"11","name":"Horror"},{"id":"12","name":"Josei"},{"id":"13","name":"Artes Marciais"},{"id":"14","name":"Adulto"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mistério"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psicológico"},{"id":"19","name":"Romance"},{"id":"20","name":"Vida Escolar"},{"id":"21","name":"Ficcção Científica"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shounen"},{"id":"25","name":"Slice of Life"},{"id":"26","name":"Esportes"},{"id":"27","name":"Sobrenatural"},{"id":"28","name":"Tragédia"},{"id":"29","name":"Hentai"},{"id":"30","name":"Terror"},{"id":"31","name":"LGBTQQICAPF2K+"},{"id":"32","name":"Ação"},{"id":"33","name":"Aventura"},{"id":"34","name":"Comédia"},{"id":"35","name":"Doujinshi"},{"id":"36","name":"Drama"},{"id":"37","name":"Ecchi"},{"id":"38","name":"Fantasia"},{"id":"39","name":"Gender Bender"},{"id":"40","name":"Harém"},{"id":"41","name":"Histórico"},{"id":"42","name":"Horror"},{"id":"43","name":"Josei"},{"id":"44","name":"Artes Marciais"},{"id":"45","name":"Adulto"},{"id":"46","name":"Mecha"},{"id":"47","name":"Mistério"},{"id":"48","name":"One Shot"},{"id":"49","name":"Psicológico"},{"id":"50","name":"Romance"},{"id":"51","name":"Vida Escolar"},{"id":"52","name":"Ficcção Científica"},{"id":"53","name":"Seinen"},{"id":"54","name":"Shoujo"},{"id":"55","name":"Shounen"},{"id":"56","name":"Slice of Life"},{"id":"57","name":"Esportes"},{"id":"58","name":"Sobrenatural"},{"id":"59","name":"Tragédia"},{"id":"60","name":"Hentai"},{"id":"61","name":"Terror"},{"id":"62","name":"LGBTQQICAPF2K+"}]}""" "https://manga-fr.me" -> """{"name":"Manga-FR","base_url":"https://manga-fr.me","supports_latest":true,"item_url":"https://manga-fr.me/lecture-en-ligne/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Aventure"},{"id":"3","name":"Comédie"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drame"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasie"},{"id":"8","name":"Gender Bender"},{"id":"9","name":"Harem"},{"id":"10","name":"Historique"},{"id":"11","name":"Horreur"},{"id":"12","name":"Josei"},{"id":"13","name":"Martial Arts"},{"id":"14","name":"Mature"},{"id":"15","name":"Mecha"},{"id":"16","name":"Mystery"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychological"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Sci-fi"},{"id":"22","name":"Seinen"},{"id":"23","name":"Shoujo"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sports"},{"id":"29","name":"Supernatural"},{"id":"30","name":"Tragédie"},{"id":"31","name":"Yaoi"},{"id":"32","name":"Yuri"},{"id":"33","name":"Fantastique"},{"id":"34","name":"Webtoon"},{"id":"35","name":"Manhwa"},{"id":"36","name":"Amour"},{"id":"37","name":"Combats"},{"id":"38","name":"Amitié"},{"id":"39","name":"Psychologique"},{"id":"40","name":"Magie"}]}""" + "https://manga-scan.co" -> """{"name":"Manga-Scan","base_url":"https://manga-scan.co","supports_latest":true,"item_url":"https://manga-scan.co/manga/","categories":[{"id":"1","name":"Action"},{"id":"2","name":"Aventure"},{"id":"3","name":"Comédie"},{"id":"4","name":"Doujinshi"},{"id":"5","name":"Drame"},{"id":"6","name":"Ecchi"},{"id":"7","name":"Fantasy"},{"id":"8","name":"Webtoon"},{"id":"9","name":"Harem"},{"id":"10","name":"Historique"},{"id":"11","name":"Horreur"},{"id":"12","name":"Thriller"},{"id":"13","name":"Arts Martiaux"},{"id":"14","name":"Mature"},{"id":"15","name":"Tragique"},{"id":"16","name":"Mystère"},{"id":"17","name":"One Shot"},{"id":"18","name":"Psychologique"},{"id":"19","name":"Romance"},{"id":"20","name":"School Life"},{"id":"21","name":"Science-fiction"},{"id":"22","name":"Seinen"},{"id":"23","name":"Erotique"},{"id":"24","name":"Shoujo Ai"},{"id":"25","name":"Shounen"},{"id":"26","name":"Shounen Ai"},{"id":"27","name":"Slice of Life"},{"id":"28","name":"Sport"},{"id":"29","name":"Surnaturel"},{"id":"30","name":"Tragedy"},{"id":"31","name":"Gangster"},{"id":"32","name":"Crime"},{"id":"33","name":"Biographique"},{"id":"34","name":"Fantastique"}]}""" else -> "" } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/readallcomics/ReadAllComicsGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/readallcomics/ReadAllComicsGenerator.kt index d676412ca9..1b22c0bc40 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/readallcomics/ReadAllComicsGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/readallcomics/ReadAllComicsGenerator.kt @@ -12,7 +12,7 @@ class ReadAllComicsGenerator : ThemeSourceGenerator { override val baseVersionCode: Int = 1 override val sources = listOf( - SingleLang("ReadAllComics", "https://readallcomics.com", "en", className = "ReadAllComicsCom"), + SingleLang("ReadAllComics", "https://readallcomics.com", "en", className = "ReadAllComicsCom", overrideVersionCode = 1), SingleLang("ReadAllManga", "https://readallmanga.com", "en"), ) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/sinmh/SinMHGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/sinmh/SinMHGenerator.kt index 133a2782b5..83893c347f 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/sinmh/SinMHGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/sinmh/SinMHGenerator.kt @@ -35,11 +35,11 @@ class SinMHGenerator : ThemeSourceGenerator { ), SingleLang( name = "Qinqin Manhua", - baseUrl = "https://www.acgqd.com", + baseUrl = "https://www.acgud.com", lang = "zh", className = "Qinqin", sourceName = "亲亲漫画", - overrideVersionCode = 1, + overrideVersionCode = 2, ), SingleLang( name = "57Manhua", diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsGenerator.kt index e100a95600..d7a3ddf102 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/webtoons/WebtoonsGenerator.kt @@ -13,7 +13,7 @@ class WebtoonsGenerator : ThemeSourceGenerator { override val baseVersionCode: Int = 2 override val sources = listOf( - MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh-Hant", "de"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 37), + MultiLang("Webtoons.com", "https://www.webtoons.com", listOf("en", "fr", "es", "id", "th", "zh-Hant", "de"), className = "WebtoonsFactory", pkgName = "webtoons", overrideVersionCode = 38), SingleLang("Dongman Manhua", "https://www.dongmanmanhua.cn", "zh"), ) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpcomics/WPComicsGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpcomics/WPComicsGenerator.kt index e7b22f86f4..1e6153e083 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpcomics/WPComicsGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/wpcomics/WPComicsGenerator.kt @@ -12,8 +12,8 @@ class WPComicsGenerator : ThemeSourceGenerator { override val baseVersionCode: Int = 2 override val sources = listOf( - SingleLang("NetTruyen", "https://www.nettruyenmax.com", "vi", overrideVersionCode = 18), - SingleLang("NhatTruyen", "https://nhattruyenmin.com", "vi", overrideVersionCode = 11), + SingleLang("NetTruyen", "https://www.nettruyenus.com", "vi", overrideVersionCode = 19), + SingleLang("NhatTruyen", "https://nhattruyenplus.com", "vi", overrideVersionCode = 12), SingleLang("TruyenChon", "http://truyenchon.com", "vi", overrideVersionCode = 3), SingleLang("XOXO Comics", "https://xoxocomics.net", "en", className = "XoxoComics", overrideVersionCode = 2), ) diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistManga.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistManga.kt index 42334dfa75..94ed9f4d76 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistManga.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistManga.kt @@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.util.asJsoup import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json @@ -16,138 +16,29 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document -import org.jsoup.nodes.Element import uy.kohesive.injekt.injectLazy abstract class ZeistManga( override val name: String, override val baseUrl: String, override val lang: String, -) : ParsedHttpSource() { +) : HttpSource() { override val supportsLatest = false - open val hasFilters = false - protected val json: Json by injectLazy() - protected val intl by lazy { ZeistMangaIntl(lang) } - open val chapterFeedRegex = """clwd\.run\('([^']+)'""".toRegex() - open val scriptSelector = "#clwd > script" + private val json: Json by injectLazy() - open val oldChapterFeedRegex = """([^']+)\?""".toRegex() - open val oldScriptSelector = "#myUL > script" + private val intl by lazy { ZeistMangaIntl(lang) } - open val pageListSelector = "div.check-box div.separator" - - open fun getApiUrl(doc: Document): String { - val script = doc.selectFirst(scriptSelector) - - if (script == null) { - val altScript = doc.selectFirst(oldScriptSelector)!!.attr("src") - val feed = oldChapterFeedRegex - .find(altScript) - ?.groupValues?.get(1) - ?: throw Exception("Failed to find chapter feed") - - return "$baseUrl$feed?alt=json&start-index=2&max-results=999999" - } - - val feed = chapterFeedRegex - .find(script.html()) - ?.groupValues?.get(1) - ?: throw Exception("Failed to find chapter feed") - - return apiUrl("Chapter") - .addPathSegments(feed) - .addQueryParameter("max-results", "999999") // Get all chapters - .build().toString() - } - - override fun chapterListParse(response: Response): List { - val document = response.asJsoup() - - val url = getApiUrl(document) - - val req = GET(url, headers) - val res = client.newCall(req).execute() - - val jsonString = res.body.string() - val result = json.decodeFromString(jsonString) - - return result.feed?.entry?.map { it.toSChapter(baseUrl) } - ?: throw Exception("Failed to parse from chapter API") - } - - override fun imageUrlParse(document: Document): String { - throw UnsupportedOperationException("Not used.") - } - - override fun latestUpdatesParse(response: Response): MangasPage { - throw UnsupportedOperationException("Not used.") - } - - override fun latestUpdatesRequest(page: Int): Request { - throw UnsupportedOperationException("Not used.") - } - - override fun chapterFromElement(element: Element): SChapter { - throw UnsupportedOperationException("Not used.") - } - - override fun chapterListSelector(): String { - throw UnsupportedOperationException("Not used.") - } - - override fun latestUpdatesFromElement(element: Element): SManga { - throw UnsupportedOperationException("Not used.") - } - - override fun latestUpdatesNextPageSelector(): String? { - throw UnsupportedOperationException("Not used.") - } - - override fun latestUpdatesSelector(): String { - throw UnsupportedOperationException("Not used.") - } - - override fun popularMangaFromElement(element: Element): SManga { - throw UnsupportedOperationException("Not used.") - } - - override fun popularMangaNextPageSelector(): String? { - throw UnsupportedOperationException("Not used.") - } - - override fun popularMangaSelector(): String { - throw UnsupportedOperationException("Not used.") - } - - override fun searchMangaFromElement(element: Element): SManga { - throw UnsupportedOperationException("Not used.") - } - - override fun searchMangaNextPageSelector(): String? { - throw UnsupportedOperationException("Not used.") - } - - override fun searchMangaSelector(): String { - throw UnsupportedOperationException("Not used.") - } - - override fun mangaDetailsParse(document: Document): SManga { - val profileManga = document.selectFirst(".grid.gtc-235fr")!! - return SManga.create().apply { - thumbnail_url = profileManga.selectFirst("img")!!.attr("src") - description = profileManga.select("#synopsis").text() - genre = profileManga.select("div.mt-15 > a[rel=tag]") - .joinToString { it.text() } - } - } + override fun popularMangaRequest(page: Int): Request { + val startIndex = maxMangaResults * (page - 1) + 1 + val url = apiUrl() + .addQueryParameter("orderby", "published") + .addQueryParameter("max-results", (maxMangaResults + 1).toString()) + .addQueryParameter("start-index", startIndex.toString()) + .build() - override fun pageListParse(document: Document): List { - val images = document.select(pageListSelector) - return images.select("img[src]").mapIndexed { i, img -> - Page(i, "", img.attr("src")) - } + return GET(url, headers) } override fun popularMangaParse(response: Response): MangasPage { @@ -155,11 +46,12 @@ abstract class ZeistManga( val result = json.decodeFromString(jsonString) val mangas = result.feed?.entry.orEmpty() - .filter { !it.category.orEmpty().any { category -> category.term == "Anime" } } // Skip animes + .filter { it.category.orEmpty().any { category -> category.term == "Series" } } + .filter { !it.category.orEmpty().any { category -> category.term == "Anime" } } .map { it.toSManga(baseUrl) } val mangalist = mangas.toMutableList() - if (mangas.size == maxResults + 1) { + if (mangas.size == maxMangaResults + 1) { mangalist.removeLast() return MangasPage(mangalist, true) } @@ -167,23 +59,13 @@ abstract class ZeistManga( return MangasPage(mangalist, false) } - override fun popularMangaRequest(page: Int): Request { - val startIndex = maxResults * (page - 1) + 1 - val url = apiUrl() - .addQueryParameter("orderby", "published") - .addQueryParameter("max-results", (maxResults + 1).toString()) - .addQueryParameter("start-index", startIndex.toString()) - .build() - - return GET(url, headers) - } - - override fun searchMangaParse(response: Response) = popularMangaParse(response) + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used.") + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not used.") override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val startIndex = maxResults * (page - 1) + 1 + val startIndex = maxMangaResults * (page - 1) + 1 val url = apiUrl() - .addQueryParameter("max-results", (maxResults + 1).toString()) + .addQueryParameter("max-results", (maxMangaResults + 1).toString()) .addQueryParameter("start-index", startIndex.toString()) if (query.isNotBlank()) { @@ -221,28 +103,140 @@ abstract class ZeistManga( return GET(url.build(), headers) } + override fun searchMangaParse(response: Response) = popularMangaParse(response) + + override fun mangaDetailsParse(response: Response): SManga { + val document = response.asJsoup() + val profileManga = document.selectFirst(".grid.gtc-235fr")!! + return SManga.create().apply { + thumbnail_url = profileManga.selectFirst("img")!!.attr("abs:src") + description = profileManga.select("#synopsis").text() + genre = profileManga.select("div.mt-15 > a[rel=tag]") + .joinToString { it.text() } + } + } + + protected open val chapterCategory = "Chapter" + + override fun chapterListParse(response: Response): List { + val document = response.asJsoup() + + val url = getChapterFeedUrl(document) + + val req = GET(url, headers) + val res = client.newCall(req).execute() + + val jsonString = res.body.string() + val result = json.decodeFromString(jsonString) + + return result.feed?.entry?.filter { it.category.orEmpty().any { category -> category.term == chapterCategory } } + ?.map { it.toSChapter(baseUrl) } + ?: throw Exception("Failed to parse from chapter API") + } + + protected open val useNewChapterFeed = false + protected open val useOldChapterFeed = false + + private val chapterFeedRegex = """clwd\.run\('([^']+)'""".toRegex() + private val scriptSelector = "#clwd > script" + + open fun getChapterFeedUrl(doc: Document): String { + if (useNewChapterFeed) return newChapterFeedUrl(doc) + if (useOldChapterFeed) return oldChapterFeedUrl(doc) + + val script = doc.selectFirst(scriptSelector) + ?: return runCatching { oldChapterFeedUrl(doc) } + .getOrElse { newChapterFeedUrl(doc) } + + val feed = chapterFeedRegex + .find(script.html()) + ?.groupValues?.get(1) + ?: throw Exception("Failed to find chapter feed") + + return apiUrl(chapterCategory) + .addPathSegments(feed) + .addQueryParameter("max-results", maxChapterResults.toString()) + .build().toString() + } + + private val oldChapterFeedRegex = """([^']+)\?""".toRegex() + private val oldScriptSelector = "#myUL > script" + + open fun oldChapterFeedUrl(doc: Document): String { + val script = doc.selectFirst(oldScriptSelector)!!.attr("src") + val feed = oldChapterFeedRegex + .find(script) + ?.groupValues?.get(1) + ?: throw Exception("Failed to find chapter feed") + + return "$baseUrl$feed?alt=json&start-index=1&max-results=$maxChapterResults" + } + + private val newChapterFeedRegex = """label\s*=\s*'([^']+)'""".toRegex() + private val newScriptSelector = "#latest > script" + + private fun newChapterFeedUrl(doc: Document): String { + var chapterRegex = chapterFeedRegex + var script = doc.selectFirst(scriptSelector) + + if (script == null) { + script = doc.selectFirst(newScriptSelector)!! + chapterRegex = newChapterFeedRegex + } + + val feed = chapterRegex + .find(script.html()) + ?.groupValues?.get(1) + ?: throw Exception("Failed to find chapter feed") + + val url = apiUrl(feed) + .addQueryParameter("start-index", "1") + .addQueryParameter("max-results", "999999") + .build() + + return url.toString() + } + + open val pageListSelector = "div.check-box div.separator" + + override fun pageListParse(response: Response): List { + val document = response.asJsoup() + val images = document.select(pageListSelector) + return images.select("img[src]").mapIndexed { i, img -> + Page(i, "", img.attr("abs:src")) + } + } + + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not used.") + open fun apiUrl(feed: String = "Series"): HttpUrl.Builder { return "$baseUrl/feeds/posts/default/-/".toHttpUrl().newBuilder() .addPathSegment(feed) .addQueryParameter("alt", "json") } + protected open val hasFilters = false + + protected open val hasStatusFilter = true + protected open val hasTypeFilter = true + protected open val hasLanguageFilter = true + protected open val hasGenreFilter = true + override fun getFilterList(): FilterList { + val filterList = mutableListOf>() + if (!hasFilters) { return FilterList(emptyList()) } - return FilterList( - Filter.Header(intl.filterWarning), - Filter.Separator(), - StatusList(intl.statusFilterTitle, getStatusList()), - TypeList(intl.typeFilterTitle, getTypeList()), - LanguageList(intl.languageFilterTitle, getLanguageList()), - GenreList(intl.genreFilterTitle, getGenreList()), - ) + if (hasStatusFilter) filterList.add(StatusList(intl.statusFilterTitle, getStatusList())) + if (hasTypeFilter) filterList.add(TypeList(intl.typeFilterTitle, getTypeList())) + if (hasLanguageFilter) filterList.add(LanguageList(intl.languageFilterTitle, getLanguageList())) + if (hasGenreFilter) filterList.add(GenreList(intl.genreFilterTitle, getGenreList())) + + return FilterList(filterList) } - // Theme Default Status protected open fun getStatusList(): List = listOf( Status(intl.all, ""), Status(intl.statusOngoing, "Ongoing"), @@ -253,7 +247,6 @@ abstract class ZeistManga( Status(intl.statusCancelled, "Cancelled"), ) - // Theme Default Types protected open fun getTypeList(): List = listOf( Type(intl.all, ""), Type(intl.typeManga, "Manga"), @@ -266,7 +259,6 @@ abstract class ZeistManga( Type(intl.typeDoujinshi, "Doujinshi"), ) - // Theme Default Genres protected open fun getGenreList(): List = listOf( Genre("Action", "Action"), Genre("Adventurer", "Adventurer"), @@ -308,7 +300,6 @@ abstract class ZeistManga( Genre("Yuri", "Yuri"), ) - // Theme Default Languages protected open fun getLanguageList(): List = listOf( Language(intl.all, ""), Language("Indonesian", "Indonesian"), @@ -316,6 +307,7 @@ abstract class ZeistManga( ) companion object { - private const val maxResults = 20 + private const val maxMangaResults = 20 + private const val maxChapterResults = 999999 } } diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistMangaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistMangaGenerator.kt index 575d77ea2c..ef894c6277 100644 --- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistMangaGenerator.kt +++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/zeistmanga/ZeistMangaGenerator.kt @@ -9,7 +9,7 @@ class ZeistMangaGenerator : ThemeSourceGenerator { override val themeClass = "ZeistManga" - override val baseVersionCode: Int = 6 + override val baseVersionCode: Int = 7 override val sources = listOf( SingleLang("Asupan Komik", "https://www.asupankomik.my.id", "id", overrideVersionCode = 1), @@ -18,7 +18,6 @@ class ZeistMangaGenerator : ThemeSourceGenerator { SingleLang("KLManhua", "https://klmanhua.blogspot.com", "id", isNsfw = true), SingleLang("Manga Ai Land", "https://manga-ai-land.blogspot.com", "ar"), SingleLang("Muslos No Sekai", "https://muslosnosekai.blogspot.com", "es"), - SingleLang("Noromax", "https://www.noromax.xyz", "id"), SingleLang("ShiyuraSub", "https://shiyurasub.blogspot.com", "id"), SingleLang("Tooncubus", "https://www.tooncubus.top", "id", isNsfw = true), ) diff --git a/multisrc/src/main/java/generator/ThemeSourceGenerator.kt b/multisrc/src/main/java/generator/ThemeSourceGenerator.kt index ba19d359ae..d768cade5b 100644 --- a/multisrc/src/main/java/generator/ThemeSourceGenerator.kt +++ b/multisrc/src/main/java/generator/ThemeSourceGenerator.kt @@ -108,7 +108,7 @@ interface ThemeSourceGenerator { """ | | - | + | """.trimMargin(), ) } diff --git a/settings.gradle.kts b/settings.gradle.kts index 1a65ef43f7..0e79c488c7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,10 @@ include(":core") -listOf("dataimage", "unpacker", "cryptoaes", "textinterceptor", "synchrony").forEach { - include(":lib-$it") - project(":lib-$it").projectDir = File("lib/$it") +// all the directories under /lib instead of manually adding each to a list +File(rootDir, "lib").eachDir { + val libName = it.name + include(":lib-$libName") + project(":lib-$libName").projectDir = File("lib/$libName") } if (System.getenv("CI") == null || System.getenv("CI_MODULE_GEN") == "true") { diff --git a/src/all/akuma/AndroidManifest.xml b/src/all/akuma/AndroidManifest.xml index 3d6f16b294..329ef70335 100644 --- a/src/all/akuma/AndroidManifest.xml +++ b/src/all/akuma/AndroidManifest.xml @@ -1,6 +1,5 @@ - + - \ No newline at end of file + diff --git a/src/all/batoto/AndroidManifest.xml b/src/all/batoto/AndroidManifest.xml index 5817ba8e3b..c4ca310dd6 100644 --- a/src/all/batoto/AndroidManifest.xml +++ b/src/all/batoto/AndroidManifest.xml @@ -1,7 +1,5 @@ - - + - \ No newline at end of file + diff --git a/src/en/lynxscans/AndroidManifest.xml b/src/all/comicfury/AndroidManifest.xml similarity index 100% rename from src/en/lynxscans/AndroidManifest.xml rename to src/all/comicfury/AndroidManifest.xml diff --git a/src/all/comicfury/build.gradle b/src/all/comicfury/build.gradle new file mode 100644 index 0000000000..daf7c52bd2 --- /dev/null +++ b/src/all/comicfury/build.gradle @@ -0,0 +1,17 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Comic Fury' + pkgNameSuffix = 'all.comicfury' + extClass = '.ComicFuryFactory' + extVersionCode = 1 + isNsfw = true +} + +dependencies { + implementation(project(':lib-textinterceptor')) +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/comicfury/res/mipmap-hdpi/ic_launcher.png b/src/all/comicfury/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..865ccdc5f5 Binary files /dev/null and b/src/all/comicfury/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/comicfury/res/mipmap-mdpi/ic_launcher.png b/src/all/comicfury/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..436075be25 Binary files /dev/null and b/src/all/comicfury/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/comicfury/res/mipmap-xhdpi/ic_launcher.png b/src/all/comicfury/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2e7393ab74 Binary files /dev/null and b/src/all/comicfury/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/comicfury/res/mipmap-xxhdpi/ic_launcher.png b/src/all/comicfury/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..a5c5732167 Binary files /dev/null and b/src/all/comicfury/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/comicfury/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/comicfury/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..1188a64c5c Binary files /dev/null and b/src/all/comicfury/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/comicfury/res/web_hi_res_512.png b/src/all/comicfury/res/web_hi_res_512.png new file mode 100644 index 0000000000..9f721a498c Binary files /dev/null and b/src/all/comicfury/res/web_hi_res_512.png differ diff --git a/src/all/comicfury/src/eu/kanade/tachiyomi/extension/all/comicfury/ComicFury.kt b/src/all/comicfury/src/eu/kanade/tachiyomi/extension/all/comicfury/ComicFury.kt new file mode 100644 index 0000000000..dc7b3b82ab --- /dev/null +++ b/src/all/comicfury/src/eu/kanade/tachiyomi/extension/all/comicfury/ComicFury.kt @@ -0,0 +1,290 @@ +package eu.kanade.tachiyomi.extension.all.comicfury + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.lib.textinterceptor.TextInterceptor +import eu.kanade.tachiyomi.lib.textinterceptor.TextInterceptorHelper +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.text.SimpleDateFormat +import java.util.Locale + +class ComicFury( + override val lang: String, + private val siteLang: String = lang, // override lang string used in MangaSearch + private val extraName: String = "", +) : HttpSource(), ConfigurableSource { + override val baseUrl: String = "https://comicfury.com" + override val name: String = "Comic Fury$extraName" //Used for No Text + override val supportsLatest: Boolean = true + private val dateFormat = SimpleDateFormat("dd MMM yyyy hh:mm aa", Locale.US) + private val dateFormatSlim = SimpleDateFormat("dd MMM yyyy", Locale.US) + + override val client = super.client.newBuilder().addInterceptor(TextInterceptor()).build() + + /** + * Archive is on a separate page from manga info + */ + override fun chapterListRequest(manga: SManga): Request = + GET("$baseUrl/read/${manga.url.substringAfter("?url=")}/archive") + + /** + * Open Archive Url instead of the details page + * Helps with getting past the nfsw pages + */ + override fun getMangaUrl(manga: SManga): String { + return "$baseUrl/read/" + manga.url.substringAfter("?url=") + "/archive" + } + + /** + * There are two different ways chapters are setup + * First Way if (true) + * Manga -> Chapter -> Comic -> Pages + * The Second Way if (false) + * Manga -> Comic -> Pages + * + * Importantly the Chapter And Comic Pages can be easy distinguished + * by the name of the list elements in this case archive-chapter/archive-comic + * + * For Manga that doesn't have "chapters" skip the loop. Including All Sub-Comics of Chapters + * + * Put the chapter name into scanlator so read can know what chapter it is. + * + * Chapter Number is handled as Chapter dot Comic. Ex. Chapter 6, Comic 4: chapter_number = 6.4 + * + */ + override fun chapterListParse(response: Response): List { + val jsp = response.asJsoup() + if (jsp.selectFirst("div.archive-chapter") != null) { + val chapters: MutableList = arrayListOf() + for (chapter in jsp.select("div.archive-chapter").parents().reversed()) { + val name = chapter.text() + chapters.addAll( + client.newCall( + GET("$baseUrl${chapter.attr("href")}"), + ).execute() + .use { chapterListParse(it) } + .mapIndexed { i, it -> + it.apply { + scanlator = name + chapter_number += i + } + }, + ) + } + return chapters + } else { + return jsp.select("div.archive-comic").mapIndexed { i, it -> + SChapter.create().apply { + url = it.parent()!!.attr("href") + name = it.child(0).ownText() + date_upload = it.child(1).ownText().toDate() + chapter_number = "0.$i".toFloat() + } + }.toList().reversed() + } + } + + override fun pageListParse(response: Response): List { + val jsp = response.asJsoup() + val pages: MutableList = arrayListOf() + val comic = jsp.selectFirst("div.is--comic-page") + for (child in comic!!.select("div.is--image-segment div img")) { + pages.add( + Page( + pages.size, + response.request.url.toString(), + child.attr("src"), + ), + ) + } + if (showAuthorsNotesPref()) { + for (child in comic.select("div.is--author-notes div.is--comment-box").withIndex()) { + pages.add( + Page( + pages.size, + response.request.url.toString(), + TextInterceptorHelper.createUrl( + jsp.selectFirst("a.is--comment-author")?.ownText() + ?: "Error No Author For Comment Found", + jsp.selectFirst("div.is--comment-content")?.html() + ?: "Error No Comment Content Found", + ), + ), + ) + } + } + return pages + } + + /** + * Author name joining maybe redundant. + * + * Manga Status is available but not currently implemented. + */ + override fun mangaDetailsParse(response: Response): SManga { + val jsp = response.asJsoup() + val desDiv = jsp.selectFirst("div.description-tags") + return SManga.create().apply { + setUrlWithoutDomain(response.request.url.toString()) + description = desDiv?.parent()?.ownText() + genre = desDiv?.children()?.eachText()?.joinToString(", ") + author = jsp.select("a.authorname").eachText().joinToString(", ") + initialized = true + } + } + + override fun searchMangaParse(response: Response): MangasPage { + val jsp = response.asJsoup() + val list: MutableList = arrayListOf() + for (result in jsp.select("div.webcomic-result")) { + list.add( + SManga.create().apply { + url = result.selectFirst("div.webcomic-result-avatar a")!!.attr("href") + title = result.selectFirst("div.webcomic-result-title")!!.attr("title") + thumbnail_url = result.selectFirst("div.webcomic-result-avatar a img")!!.absUrl("src") + }, + ) + } + return MangasPage(list, (jsp.selectFirst("div.search-next-page") != null)) + } + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val req: HttpUrl.Builder = "$baseUrl/search.php".toHttpUrl().newBuilder() + req.addQueryParameter("page", page.toString()) + req.addQueryParameter("language", siteLang) + filters.forEach { + when (it) { + is TagsFilter -> req.addEncodedQueryParameter( + "tags", + it.state.replace(", ", ","), + ) + is SortFilter -> req.addQueryParameter("sort", it.state.toString()) + is CompletedComicFilter -> req.addQueryParameter( + "completed", + it.state.toInt().toString(), + ) + is LastUpdatedFilter -> req.addQueryParameter( + "lastupdate", + it.state.toString(), + ) + is ViolenceFilter -> req.addQueryParameter("fv", it.state.toString()) + is NudityFilter -> req.addQueryParameter("fn", it.state.toString()) + is StrongLangFilter -> req.addQueryParameter("fl", it.state.toString()) + is SexualFilter -> req.addQueryParameter("fs", it.state.toString()) + else -> {} + } + } + + return Request.Builder().url(req.build()).build() + } + + // START OF AUTHOR NOTES // + private val preferences: SharedPreferences by lazy { + Injekt.get().getSharedPreferences("source_$id", 0x0000) + } + companion object { + private const val SHOW_AUTHORS_NOTES_KEY = "showAuthorsNotes" + } + private fun showAuthorsNotesPref() = + preferences.getBoolean(SHOW_AUTHORS_NOTES_KEY, false) + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + val authorsNotesPref = SwitchPreferenceCompat(screen.context).apply { + key = SHOW_AUTHORS_NOTES_KEY; title = "Show author's notes" + summary = "Enable to see the author's notes at the end of chapters (if they're there)." + setDefaultValue(false) + } + screen.addPreference(authorsNotesPref) + } + // END OF AUTHOR NOTES // + + // START OF FILTERS // + override fun getFilterList(): FilterList = getFilterList(0) + private fun getFilterList(sortIndex: Int): FilterList = FilterList( + TagsFilter(), + Filter.Separator(), + SortFilter(sortIndex), + Filter.Separator(), + LastUpdatedFilter(), + CompletedComicFilter(), + Filter.Separator(), + Filter.Header("Flags"), + ViolenceFilter(), + NudityFilter(), + StrongLangFilter(), + SexualFilter(), + ) + + internal class SortFilter(index: Int) : Filter.Select( + "Sort By", + arrayOf("Relevance", "Popularity", "Last Update"), + index, + ) + internal class CompletedComicFilter : Filter.CheckBox("Comic Completed", false) + internal class LastUpdatedFilter : Filter.Select( + "Last Updated", + arrayOf("All Time", "This Week", "This Month", "This Year", "Completed Only"), + 0, + ) + internal class ViolenceFilter : Filter.Select( + "Violence", + arrayOf("None / Minimal", "Violent Content", "Gore / Graphic"), + 2, + ) + internal class NudityFilter : Filter.Select( + "Frontal Nudity", + arrayOf("None", "Occasional", "Frequent"), + 2, + ) + internal class StrongLangFilter : Filter.Select( + "Strong Language", + arrayOf("None", "Occasional", "Frequent"), + 2, + ) + internal class SexualFilter : Filter.Select( + "Sexual Content", + arrayOf("No Sexual Content", "Sexual Situations", "Strong Sexual Themes"), + 2, + ) + internal class TagsFilter : Filter.Text("Tags") + + // END OF FILTERS // + + override fun popularMangaRequest(page: Int): Request = + searchMangaRequest(page, "", getFilterList(1)) + override fun popularMangaParse(response: Response): MangasPage = searchMangaParse(response) + + override fun latestUpdatesRequest(page: Int): Request = + searchMangaRequest(page, "", getFilterList(2)) + override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response) + + override fun imageUrlParse(response: Response): String = + throw UnsupportedOperationException("Not Used") + + private fun String.toDate(): Long { + val ret = this.replace("st", "") + .replace("nd", "") + .replace("rd", "") + .replace("th", "") + .replace(",", "") + return dateFormat.parse(ret)?.time ?: dateFormatSlim.parse(ret)!!.time + } + + private fun Boolean.toInt(): Int = if (this) { 0 } else { 1 } +} diff --git a/src/all/comicfury/src/eu/kanade/tachiyomi/extension/all/comicfury/ComicFuryFactory.kt b/src/all/comicfury/src/eu/kanade/tachiyomi/extension/all/comicfury/ComicFuryFactory.kt new file mode 100644 index 0000000000..cb18b496d0 --- /dev/null +++ b/src/all/comicfury/src/eu/kanade/tachiyomi/extension/all/comicfury/ComicFuryFactory.kt @@ -0,0 +1,23 @@ +package eu.kanade.tachiyomi.extension.all.comicfury + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class ComicFuryFactory : SourceFactory { + override fun createSources(): List = listOf( + ComicFury("all"), + ComicFury("en"), + ComicFury("es"), + ComicFury("pt-BR", "pt"), + ComicFury("de"), + ComicFury("fr"), + ComicFury("it"), + ComicFury("pl"), + ComicFury("ja"), + ComicFury("zh"), + ComicFury("ru"), + ComicFury("fi"), + ComicFury("other"), + ComicFury("other", "notext", " (No Text)"), + ) +} diff --git a/src/all/comickfun/AndroidManifest.xml b/src/all/comickfun/AndroidManifest.xml index 1411452060..c0e9ad24b1 100644 --- a/src/all/comickfun/AndroidManifest.xml +++ b/src/all/comickfun/AndroidManifest.xml @@ -1,6 +1,5 @@ - + { - it.state.filter { (it as TriState).isIncluded() }.forEach { - addQueryParameter( - "genres", - (it as TriState).value, - ) + it.state.filter { it.isIncluded() }.forEach { + addQueryParameter("genres", it.value) } - it.state.filter { (it as TriState).isExcluded() }.forEach { - addQueryParameter( - "excludes", - (it as TriState).value, - ) + it.state.filter { it.isExcluded() }.forEach { + addQueryParameter("excludes", it.value) } } is DemographicFilter -> { - it.state.filter { (it as CheckBox).state }.forEach { - addQueryParameter( - "demographic", - (it as CheckBox).value, - ) + it.state.filter { it.isIncluded() }.forEach { + addQueryParameter("demographic", it.value) } } is TypeFilter -> { - it.state.filter { (it as CheckBox).state }.forEach { - addQueryParameter( - "country", - (it as CheckBox).value, - ) + it.state.filter { it.state }.forEach { + addQueryParameter("country", it.value) } } is SortFilter -> { addQueryParameter("sort", it.getValue()) } + is StatusFilter -> { + if (it.state > 0) { + addQueryParameter("status", it.getValue()) + } + } is CreatedAtFilter -> { if (it.state > 0) { addQueryParameter("time", it.getValue()) diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt index 51e1742fa3..4a8bf621cd 100644 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt +++ b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunDto.kt @@ -9,30 +9,30 @@ import kotlinx.serialization.Serializable data class SearchManga( val hid: String, val title: String, - val md_covers: List, - val cover_url: String? = null, + @SerialName("md_covers") val mdCovers: List = emptyList(), + @SerialName("cover_url") val cover: String? = null, ) { fun toSManga() = SManga.create().apply { - // appennding # at end as part of migration from slug to hid + // appending # at end as part of migration from slug to hid url = "/comic/$hid#" title = this@SearchManga.title - thumbnail_url = parseCover(cover_url, md_covers) + thumbnail_url = parseCover(cover, mdCovers) } } @Serializable data class Manga( val comic: Comic, - val artists: List = emptyList(), - val authors: List = emptyList(), - val genres: List = emptyList(), + val artists: List = emptyList(), + val authors: List = emptyList(), + val genres: List = emptyList(), ) { fun toSManga() = SManga.create().apply { // appennding # at end as part of migration from slug to hid url = "/comic/${comic.hid}#" title = comic.title - description = comic.desc.beautifyDescription() + description = comic.desc?.beautifyDescription() if (comic.altTitles.isNotEmpty()) { if (description.isNullOrEmpty()) { description = "Alternative Titles:\n" @@ -44,11 +44,12 @@ data class Manga( title.title?.let { "• $it" } }.joinToString("\n") } - status = comic.status.parseStatus(comic.translation_completed) - thumbnail_url = parseCover(comic.cover_url, comic.md_covers) + status = comic.status.parseStatus(comic.translationComplete) + thumbnail_url = parseCover(comic.cover, comic.mdCovers) artist = artists.joinToString { it.name.trim() } author = authors.joinToString { it.name.trim() } - genre = genres.joinToString { it.name.trim() } + genre = (listOfNotNull(comic.origination) + genres) + .joinToString { it.name.trim() } } } @@ -56,13 +57,21 @@ data class Manga( data class Comic( val hid: String, val title: String, - @SerialName("md_titles") val altTitles: List, - val desc: String = "N/A", - val status: Int = 0, - val translation_completed: Boolean = true, - val md_covers: List, - val cover_url: String? = null, -) + val country: String? = null, + @SerialName("md_titles") val altTitles: List = emptyList(), + val desc: String? = null, + val status: Int? = 0, + @SerialName("translation_completed") val translationComplete: Boolean? = true, + @SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(), + @SerialName("cover_url") val cover: String? = null, +) { + val origination = when (country) { + "jp" -> Name("Manga") + "kr" -> Name("Manhwa") + "cn" -> Name("Manhua") + else -> null + } +} @Serializable data class MDcovers( @@ -70,25 +79,12 @@ data class MDcovers( ) @Serializable -data class MDtitles( +data class Title( val title: String?, ) @Serializable -data class Artist( - val name: String, - val slug: String, -) - -@Serializable -data class Author( - val name: String, - val slug: String, -) - -@Serializable -data class Genre( - val slug: String, +data class Name( val name: String, ) @@ -101,18 +97,18 @@ data class ChapterList( @Serializable data class Chapter( val hid: String, - val lang: String, + val lang: String = "", val title: String = "", - val created_at: String = "", + @SerialName("created_at") val createdAt: String = "", val chap: String = "", val vol: String = "", - val group_name: List<String> = emptyList(), + @SerialName("group_name") val groups: List<String> = emptyList(), ) { fun toSChapter(mangaUrl: String) = SChapter.create().apply { url = "$mangaUrl/$hid-chapter-$chap-$lang" name = beautifyChapterName(vol, chap, title) - date_upload = created_at.parseDate() - scanlator = group_name.joinToString().takeUnless { it.isBlank() } ?: "Unknown" + date_upload = createdAt.parseDate() + scanlator = groups.joinToString().takeUnless { it.isBlank() } ?: "Unknown" } } diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunFilters.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunFilters.kt index 321f8173f3..7d8d0a33d8 100644 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunFilters.kt +++ b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunFilters.kt @@ -5,13 +5,14 @@ import eu.kanade.tachiyomi.source.model.FilterList fun getFilters(): FilterList { return FilterList( - Filter.Header(name = "NOTE: Everything below is ignored if using text search"), - CompletedFilter("Completed translation"), + Filter.Header(name = "The filter is ignored when using text search."), GenreFilter("Genre", getGenresList), DemographicFilter("Demographic", getDemographicList), TypeFilter("Type", getTypeList), SortFilter("Sort", getSortsList), - CreatedAtFilter("Created At", getCreatedAtList), + StatusFilter("Status", getStatusList), + CompletedFilter("Completely Scanlated?"), + CreatedAtFilter("Created at", getCreatedAtList), MinimumFilter("Minimum Chapters"), Filter.Header("From Year, ex: 2010"), FromYearFilter("From"), @@ -19,162 +20,173 @@ fun getFilters(): FilterList { ToYearFilter("To"), Filter.Header("Separate tags with commas"), TagFilter("Tags"), - ) } /** Filters **/ -internal class GenreFilter(name: String, genreList: List<TriState>) : Group(name, genreList) +internal class GenreFilter(name: String, genreList: List<Pair<String, String>>) : + Filter.Group<TriFilter>(name, genreList.map { TriFilter(it.first, it.second) }) -internal class TagFilter(name: String) : Text(name) +internal class TagFilter(name: String) : TextFilter(name) -internal class DemographicFilter(name: String, demographicList: List<CheckBox>) : - Group(name, demographicList) +internal class DemographicFilter(name: String, demographicList: List<Pair<String, String>>) : + Filter.Group<TriFilter>(name, demographicList.map { TriFilter(it.first, it.second) }) -internal class TypeFilter(name: String, typeList: List<CheckBox>) : - Group(name, typeList) +internal class TypeFilter(name: String, typeList: List<Pair<String, String>>) : + Filter.Group<CheckBoxFilter>(name, typeList.map { CheckBoxFilter(it.first, it.second) }) -internal class CompletedFilter(name: String) : CheckBox(name) +internal class CompletedFilter(name: String) : CheckBoxFilter(name) -internal class CreatedAtFilter(name: String, createdAtList: Array<Pair<String, String>>) : - Select(name, createdAtList) +internal class CreatedAtFilter(name: String, createdAtList: List<Pair<String, String>>) : + SelectFilter(name, createdAtList) -internal class MinimumFilter(name: String) : Text(name) +internal class MinimumFilter(name: String) : TextFilter(name) -internal class FromYearFilter(name: String) : Text(name) +internal class FromYearFilter(name: String) : TextFilter(name) -internal class ToYearFilter(name: String) : Text(name) +internal class ToYearFilter(name: String) : TextFilter(name) -internal class SortFilter(name: String, sortList: Array<Pair<String, String>>, state: Int = 0) : - Select(name, sortList, state) +internal class SortFilter(name: String, sortList: List<Pair<String, String>>, state: Int = 0) : + SelectFilter(name, sortList, state) -/** Generics **/ -internal open class Group(name: String, values: List<Any>) : - Filter.Group<Any>(name, values) +internal class StatusFilter(name: String, statusList: List<Pair<String, String>>, state: Int = 0) : + SelectFilter(name, statusList, state) -internal open class TriState(name: String, val value: String) : Filter.TriState(name) +/** Generics **/ +internal open class TriFilter(name: String, val value: String) : Filter.TriState(name) -internal open class Text(name: String) : Filter.Text(name) +internal open class TextFilter(name: String) : Filter.Text(name) -internal open class CheckBox(name: String, val value: String = "") : Filter.CheckBox(name) +internal open class CheckBoxFilter(name: String, val value: String = "") : Filter.CheckBox(name) -internal open class Select(name: String, private val vals: Array<Pair<String, String>>, state: Int = 0) : +internal open class SelectFilter(name: String, private val vals: List<Pair<String, String>>, state: Int = 0) : Filter.Select<String>(name, vals.map { it.first }.toTypedArray(), state) { fun getValue() = vals[state].second } /** Filters Data **/ -private val getGenresList: List<TriState> = listOf( - TriState("4-Koma", "4-koma"), - TriState("Action", "action"), - TriState("Adaptation", "adaptation"), - TriState("Adult", "adult"), - TriState("Adventure", "adventure"), - TriState("Aliens", "aliens"), - TriState("Animals", "animals"), - TriState("Anthology", "anthology"), - TriState("Award Winning", "award-winning"), - TriState("Comedy", "comedy"), - TriState("Cooking", "cooking"), - TriState("Crime", "crime"), - TriState("Crossdressing", "crossdressing"), - TriState("Delinquents", "delinquents"), - TriState("Demons", "demons"), - TriState("Doujinshi", "doujinshi"), - TriState("Drama", "drama"), - TriState("Ecchi", "ecchi"), - TriState("Fan Colored", "fan-colored"), - TriState("Fantasy", "fantasy"), - TriState("Full Color", "full-color"), - TriState("Gender Bender", "gender-bender"), - TriState("Genderswap", "genderswap"), - TriState("Ghosts", "ghosts"), - TriState("Gore", "gore"), - TriState("Gyaru", "gyaru"), - TriState("Harem", "harem"), - TriState("Historical", "historical"), - TriState("Horror", "horror"), - TriState("Incest", "incest"), - TriState("Isekai", "isekai"), - TriState("Loli", "loli"), - TriState("Long Strip", "long-strip"), - TriState("Mafia", "mafia"), - TriState("Magic", "magic"), - TriState("Magical Girls", "magical-girls"), - TriState("Martial Arts", "martial-arts"), - TriState("Mature", "mature"), - TriState("Mecha", "mecha"), - TriState("Medical", "medical"), - TriState("Military", "military"), - TriState("Monster Girls", "monster-girls"), - TriState("Monsters", "monsters"), - TriState("Music", "music"), - TriState("Mystery", "mystery"), - TriState("Ninja", "ninja"), - TriState("Office Workers", "office-workers"), - TriState("Official Colored", "official-colored"), - TriState("Oneshot", "oneshot"), - TriState("Philosophical", "philosophical"), - TriState("Police", "police"), - TriState("Post-Apocalyptic", "post-apocalyptic"), - TriState("Psychological", "psychological"), - TriState("Reincarnation", "reincarnation"), - TriState("Reverse Harem", "reverse-harem"), - TriState("Romance", "romance"), - TriState("Samurai", "samurai"), - TriState("School Life", "school-life"), - TriState("Sci-Fi", "sci-fi"), - TriState("Sexual Violence", "sexual-violence"), - TriState("Shota", "shota"), - TriState("Shoujo Ai", "shoujo-ai"), - TriState("Shounen Ai", "shounen-ai"), - TriState("Slice of Life", "slice-of-life"), - TriState("Smut", "smut"), - TriState("Sports", "sports"), - TriState("Superhero", "superhero"), - TriState("Supernatural", "supernatural"), - TriState("Survival", "survival"), - TriState("Thriller", "thriller"), - TriState("Time Travel", "time-travel"), - TriState("Traditional Games", "traditional-games"), - TriState("Tragedy", "tragedy"), - TriState("User Created", "user-created"), - TriState("Vampires", "vampires"), - TriState("Video Games", "video-games"), - TriState("Villainess", "villainess"), - TriState("Virtual Reality", "virtual-reality"), - TriState("Web Comic", "web-comic"), - TriState("Wuxia", "wuxia"), - TriState("Yaoi", "yaoi"), - TriState("Yuri", "yuri"), - TriState("Zombies", "zombies"), +private val getGenresList: List<Pair<String, String>> = listOf( + Pair("4-Koma", "4-koma"), + Pair("Action", "action"), + Pair("Adaptation", "adaptation"), + Pair("Adult", "adult"), + Pair("Adventure", "adventure"), + Pair("Aliens", "aliens"), + Pair("Animals", "animals"), + Pair("Anthology", "anthology"), + Pair("Award Winning", "award-winning"), + Pair("Comedy", "comedy"), + Pair("Cooking", "cooking"), + Pair("Crime", "crime"), + Pair("Crossdressing", "crossdressing"), + Pair("Delinquents", "delinquents"), + Pair("Demons", "demons"), + Pair("Doujinshi", "doujinshi"), + Pair("Drama", "drama"), + Pair("Ecchi", "ecchi"), + Pair("Fan Colored", "fan-colored"), + Pair("Fantasy", "fantasy"), + Pair("Full Color", "full-color"), + Pair("Gender Bender", "gender-bender"), + Pair("Genderswap", "genderswap"), + Pair("Ghosts", "ghosts"), + Pair("Gore", "gore"), + Pair("Gyaru", "gyaru"), + Pair("Harem", "harem"), + Pair("Historical", "historical"), + Pair("Horror", "horror"), + Pair("Incest", "incest"), + Pair("Isekai", "isekai"), + Pair("Loli", "loli"), + Pair("Long Strip", "long-strip"), + Pair("Mafia", "mafia"), + Pair("Magic", "magic"), + Pair("Magical Girls", "magical-girls"), + Pair("Martial Arts", "martial-arts"), + Pair("Mature", "mature"), + Pair("Mecha", "mecha"), + Pair("Medical", "medical"), + Pair("Military", "military"), + Pair("Monster Girls", "monster-girls"), + Pair("Monsters", "monsters"), + Pair("Music", "music"), + Pair("Mystery", "mystery"), + Pair("Ninja", "ninja"), + Pair("Office Workers", "office-workers"), + Pair("Official Colored", "official-colored"), + Pair("Oneshot", "oneshot"), + Pair("Philosophical", "philosophical"), + Pair("Police", "police"), + Pair("Post-Apocalyptic", "post-apocalyptic"), + Pair("Psychological", "psychological"), + Pair("Reincarnation", "reincarnation"), + Pair("Reverse Harem", "reverse-harem"), + Pair("Romance", "romance"), + Pair("Samurai", "samurai"), + Pair("School Life", "school-life"), + Pair("Sci-Fi", "sci-fi"), + Pair("Sexual Violence", "sexual-violence"), + Pair("Shota", "shota"), + Pair("Shoujo Ai", "shoujo-ai"), + Pair("Shounen Ai", "shounen-ai"), + Pair("Slice of Life", "slice-of-life"), + Pair("Smut", "smut"), + Pair("Sports", "sports"), + Pair("Superhero", "superhero"), + Pair("Supernatural", "supernatural"), + Pair("Survival", "survival"), + Pair("Thriller", "thriller"), + Pair("Time Travel", "time-travel"), + Pair("Traditional Games", "traditional-games"), + Pair("Tragedy", "tragedy"), + Pair("User Created", "user-created"), + Pair("Vampires", "vampires"), + Pair("Video Games", "video-games"), + Pair("Villainess", "villainess"), + Pair("Virtual Reality", "virtual-reality"), + Pair("Web Comic", "web-comic"), + Pair("Wuxia", "wuxia"), + Pair("Yaoi", "yaoi"), + Pair("Yuri", "yuri"), + Pair("Zombies", "zombies"), ) -private val getDemographicList: List<CheckBox> = listOf( - CheckBox("Shounen", "1"), - CheckBox("Shoujo", "2"), - CheckBox("Seinen", "3"), - CheckBox("Josei", "4"), +private val getDemographicList: List<Pair<String, String>> = listOf( + Pair("Shounen", "1"), + Pair("Shoujo", "2"), + Pair("Seinen", "3"), + Pair("Josei", "4"), ) -private val getTypeList: List<CheckBox> = listOf( - CheckBox("Manga", "jp"), - CheckBox("Manhwa", "kr"), - CheckBox("Manhua", "cn"), +private val getTypeList: List<Pair<String, String>> = listOf( + Pair("Manga", "jp"), + Pair("Manhwa", "kr"), + Pair("Manhua", "cn"), ) -private val getCreatedAtList: Array<Pair<String, String>> = arrayOf( +private val getCreatedAtList: List<Pair<String, String>> = listOf( Pair("", ""), + Pair("3 days", "3"), + Pair("7 days", "7"), Pair("30 days", "30"), Pair("3 months", "90"), Pair("6 months", "180"), Pair("1 year", "365"), ) -private val getSortsList: Array<Pair<String, String>> = arrayOf( +private val getSortsList: List<Pair<String, String>> = listOf( Pair("Most popular", "follow"), Pair("Most follows", "user_follow_count"), Pair("Most views", "view"), Pair("High rating", "rating"), Pair("Last updated", "uploaded"), + Pair("Newest", "created_at"), +) + +private val getStatusList: List<Pair<String, String>> = listOf( + Pair("All", "0"), + Pair("Ongoing", "1"), + Pair("Completed", "2"), + Pair("Cancelled", "3"), + Pair("Hiatus", "4"), ) diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt index e73fc3f62a..ea8c05946c 100644 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt +++ b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFunHelper.kt @@ -18,11 +18,11 @@ internal fun String.beautifyDescription(): String { .trim() } -internal fun Int.parseStatus(translationComplete: Boolean): Int { +internal fun Int?.parseStatus(translationComplete: Boolean?): Int { return when (this) { 1 -> SManga.ONGOING 2 -> { - if (translationComplete) { + if (translationComplete == true) { SManga.COMPLETED } else { SManga.PUBLISHING_FINISHED @@ -34,11 +34,11 @@ internal fun Int.parseStatus(translationComplete: Boolean): Int { } } -internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>): String { - val b2key = runCatching { mdCovers.first().b2key } - .getOrNull() ?: "" +internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>): String? { + val b2key = mdCovers.firstOrNull()?.b2key + ?: return thumbnailUrl - return "$thumbnailUrl#$b2key" + return thumbnailUrl?.let { "$it#$b2key" } } internal fun thumbnailIntercept(chain: Interceptor.Chain): Response { diff --git a/src/all/comico/AndroidManifest.xml b/src/all/comico/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/comico/AndroidManifest.xml +++ b/src/all/comico/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/commitstrip/AndroidManifest.xml b/src/all/commitstrip/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/commitstrip/AndroidManifest.xml +++ b/src/all/commitstrip/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/cubari/AndroidManifest.xml b/src/all/cubari/AndroidManifest.xml index 3cfd493e82..4eaf291a1f 100644 --- a/src/all/cubari/AndroidManifest.xml +++ b/src/all/cubari/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -72,4 +71,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/all/cubari/build.gradle b/src/all/cubari/build.gradle index b7f6df0db3..b427711587 100644 --- a/src/all/cubari/build.gradle +++ b/src/all/cubari/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Cubari' pkgNameSuffix = "all.cubari" extClass = '.CubariFactory' - extVersionCode = 19 + extVersionCode = 21 } apply from: "$rootDir/common.gradle" diff --git a/src/all/cubari/src/eu/kanade/tachiyomi/extension/all/cubari/Cubari.kt b/src/all/cubari/src/eu/kanade/tachiyomi/extension/all/cubari/Cubari.kt index 2717e4885e..c1103f386d 100644 --- a/src/all/cubari/src/eu/kanade/tachiyomi/extension/all/cubari/Cubari.kt +++ b/src/all/cubari/src/eu/kanade/tachiyomi/extension/all/cubari/Cubari.kt @@ -4,6 +4,7 @@ import android.app.Application import android.os.Build import eu.kanade.tachiyomi.AppInfo import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage @@ -105,7 +106,7 @@ open class Cubari(override val lang: String) : HttpSource() { override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { return client.newCall(chapterListRequest(manga)) - .asObservableSuccess() + .asObservable() .map { response -> chapterListParse(response, manga) } } diff --git a/src/all/danbooru/AndroidManifest.xml b/src/all/danbooru/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/danbooru/AndroidManifest.xml +++ b/src/all/danbooru/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/dragonballmultiverse/AndroidManifest.xml b/src/all/dragonballmultiverse/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/dragonballmultiverse/AndroidManifest.xml +++ b/src/all/dragonballmultiverse/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/ehentai/AndroidManifest.xml b/src/all/ehentai/AndroidManifest.xml index c529fbbd13..c297d5bcbd 100644 --- a/src/all/ehentai/AndroidManifest.xml +++ b/src/all/ehentai/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/all/everiaclub/AndroidManifest.xml b/src/all/everiaclub/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/all/everiaclub/AndroidManifest.xml +++ b/src/all/everiaclub/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/all/everiaclub/build.gradle b/src/all/everiaclub/build.gradle index 9655825281..f631e163a1 100644 --- a/src/all/everiaclub/build.gradle +++ b/src/all/everiaclub/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Everia.club' pkgNameSuffix = 'all.everiaclub' extClass = '.EveriaClub' - extVersionCode = 6 + extVersionCode = 8 isNsfw = true } diff --git a/src/all/everiaclub/src/eu/kanade/tachiyomi/extension/all/everiaclub/EveriaClub.kt b/src/all/everiaclub/src/eu/kanade/tachiyomi/extension/all/everiaclub/EveriaClub.kt index 566c62782c..8fc8e2ba70 100644 --- a/src/all/everiaclub/src/eu/kanade/tachiyomi/extension/all/everiaclub/EveriaClub.kt +++ b/src/all/everiaclub/src/eu/kanade/tachiyomi/extension/all/everiaclub/EveriaClub.kt @@ -41,7 +41,7 @@ class EveriaClub() : ParsedHttpSource() { return GET("$baseUrl/page/$page/") } - override fun latestUpdatesSelector() = ".posts-wrapper > article" + override fun latestUpdatesSelector() = "#blog-entries > article" // Popular override fun popularMangaFromElement(element: Element): SManga { @@ -81,7 +81,7 @@ class EveriaClub() : ParsedHttpSource() { manga.title = document.select(".entry-title").text() manga.description = document.select(".entry-title").text() val genres = mutableListOf<String>() - document.select(".nv-tags-list > a").forEach { + document.select(".post-tags > a").forEach { genres.add(it.text()) } manga.genre = genres.joinToString(", ") @@ -104,7 +104,7 @@ class EveriaClub() : ParsedHttpSource() { override fun pageListParse(document: Document): List<Page> { val pages = mutableListOf<Page>() document.select("noscript").remove() - document.select("article img").forEachIndexed { i, it -> + document.select(".entry-content img").forEachIndexed { i, it -> val itUrl = it.imgSrc pages.add(Page(i, itUrl, itUrl)) } diff --git a/src/all/freleinbooks/AndroidManifest.xml b/src/all/freleinbooks/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/all/freleinbooks/AndroidManifest.xml +++ b/src/all/freleinbooks/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/all/hennojin/AndroidManifest.xml b/src/all/hennojin/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/hennojin/AndroidManifest.xml +++ b/src/all/hennojin/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/holonometria/AndroidManifest.xml b/src/all/holonometria/AndroidManifest.xml new file mode 100644 index 0000000000..cc947c5679 --- /dev/null +++ b/src/all/holonometria/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest /> diff --git a/src/all/holonometria/build.gradle b/src/all/holonometria/build.gradle new file mode 100644 index 0000000000..fe9ec48fa7 --- /dev/null +++ b/src/all/holonometria/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +ext { + extName = 'HOLONOMETRIA' + pkgNameSuffix = 'all.holonometria' + extClass = '.HolonometriaFactory' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/holonometria/res/mipmap-hdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..bef1d2b85e Binary files /dev/null and b/src/all/holonometria/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-mdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..caaa59fadf Binary files /dev/null and b/src/all/holonometria/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-xhdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..fc8894dbd5 Binary files /dev/null and b/src/all/holonometria/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-xxhdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..5f6f59ea8b Binary files /dev/null and b/src/all/holonometria/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/holonometria/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..f0f185ceac Binary files /dev/null and b/src/all/holonometria/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/holonometria/res/web_hi_res_512.png b/src/all/holonometria/res/web_hi_res_512.png new file mode 100644 index 0000000000..b3bd72e7b9 Binary files /dev/null and b/src/all/holonometria/res/web_hi_res_512.png differ diff --git a/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/Holonometria.kt b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/Holonometria.kt new file mode 100644 index 0000000000..4c90583285 --- /dev/null +++ b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/Holonometria.kt @@ -0,0 +1,150 @@ +package eu.kanade.tachiyomi.extension.all.holonometria + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.concurrent.TimeUnit + +class Holonometria( + override val lang: String, + private val langPath: String = "$lang/", +) : ParsedHttpSource() { + + override val name = "HOLONOMETRIA" + + override val baseUrl = "https://alt.hololive.tv" + + override val supportsLatest = false + + override val client = network.client.newBuilder() + .readTimeout(60, TimeUnit.SECONDS) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + + override fun popularMangaRequest(page: Int) = + GET("$baseUrl/holonometria/$langPath", headers) + + override fun popularMangaSelector() = "#Story article:has(a[href*=/manga/])" + override fun popularMangaNextPageSelector() = null + + override fun popularMangaFromElement(element: Element) = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + title = element.select(".ttl").text() + thumbnail_url = element.selectFirst("img")?.attr("abs:src") + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = + GET("$baseUrl/holonometria/$langPath#${query.trim()}", headers) + + override fun searchMangaParse(response: Response): MangasPage { + val document = response.asJsoup() + val search = response.request.url.fragment!! + + val entries = document.select(searchMangaSelector()) + .map(::searchMangaFromElement) + .filter { it.title.contains(search, true) } + + return MangasPage(entries, false) + } + + override fun searchMangaSelector() = popularMangaSelector() + override fun searchMangaNextPageSelector() = popularMangaNextPageSelector() + override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.select(".md-ttl__pages").text() + thumbnail_url = document.select(".mangainfo img").attr("abs:src") + description = document.select(".mangainfo aside").text() + val info = document.select(".mangainfo footer").html().split("<br>") + author = info.firstOrNull { desc -> manga.any { desc.contains(it, true) } } + ?.substringAfter(":") + ?.substringAfter(":") + ?.trim() + ?.replace("&", "&") + artist = info.firstOrNull { desc -> script.any { desc.contains(it, true) } } + ?.substringAfter(":") + ?.substringAfter(":") + ?.trim() + ?.replace("&", "&") + } + + override fun chapterListRequest(manga: SManga) = + paginatedChapterListRequest(manga.url, 1) + + private fun paginatedChapterListRequest(mangaUrl: String, page: Int) = + GET("$baseUrl$mangaUrl".removeSuffix("/") + if (page == 1) "/" else "/page/$page/", headers) + + override fun chapterListParse(response: Response): List<SChapter> { + val document = response.asJsoup() + val mangaUrl = response.request.url.toString() + .substringAfter(baseUrl) + .substringBefore("page/") + + val chapters = document.select(chapterListSelector()) + .map(::chapterFromElement) + .toMutableList() + + val lastPage = document.select(".pagenation-list a").last() + ?.text()?.toIntOrNull() ?: return chapters + + for (page in 2..lastPage) { + val request = paginatedChapterListRequest(mangaUrl, page) + val newDocument = client.newCall(request).execute().asJsoup() + + val moreChapters = newDocument.select(chapterListSelector()) + .map(::chapterFromElement) + + chapters.addAll(moreChapters) + } + + return chapters + } + + override fun chapterListSelector() = "#Archive article" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + name = element.select(".ttl").text() + date_upload = element.selectFirst(".data--date")?.text().parseDate() + scanlator = element.selectFirst(".data--category")?.text() + } + + private fun String?.parseDate(): Long { + return runCatching { + dateFormat.parse(this!!)!!.time + }.getOrDefault(0L) + } + + override fun pageListParse(document: Document): List<Page> { + return document.select("#js-mangaviewer img").mapIndexed { idx, img -> + Page(idx, "", img.attr("abs:src")) + } + } + + companion object { + private val manga = listOf("manga", "gambar", "漫画") + private val script = listOf("script", "naskah", "脚本") + + private val dateFormat by lazy { + SimpleDateFormat("yy.MM.dd", Locale.ENGLISH) + } + } + + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used") + override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException("Not used") + override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException("Not used") + override fun latestUpdatesSelector() = throw UnsupportedOperationException("Not used") + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used") +} diff --git a/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/HolonometriaFactory.kt b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/HolonometriaFactory.kt new file mode 100644 index 0000000000..08d64ab980 --- /dev/null +++ b/src/all/holonometria/src/eu/kanade/tachiyomi/extension/all/holonometria/HolonometriaFactory.kt @@ -0,0 +1,11 @@ +package eu.kanade.tachiyomi.extension.all.holonometria + +import eu.kanade.tachiyomi.source.SourceFactory + +class HolonometriaFactory : SourceFactory { + override fun createSources() = listOf( + Holonometria("ja", ""), + Holonometria("en"), + Holonometria("id"), + ) +} diff --git a/src/all/imhentai/AndroidManifest.xml b/src/all/imhentai/AndroidManifest.xml index c0545790ee..a8e83cde1c 100644 --- a/src/all/imhentai/AndroidManifest.xml +++ b/src/all/imhentai/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -21,4 +20,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/all/imhentai/build.gradle b/src/all/imhentai/build.gradle index 75553cbc4c..a534d852c2 100644 --- a/src/all/imhentai/build.gradle +++ b/src/all/imhentai/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'IMHentai' pkgNameSuffix = 'all.imhentai' extClass = '.IMHentaiFactory' - extVersionCode = 11 + extVersionCode = 12 isNsfw = true } diff --git a/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentai.kt b/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentai.kt index b7d26c217b..dfde195ca6 100644 --- a/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentai.kt +++ b/src/all/imhentai/src/eu/kanade/tachiyomi/extension/all/imhentai/IMHentai.kt @@ -216,7 +216,8 @@ class IMHentai(override val lang: String, private val imhLang: String) : ParsedH in 527144..632481 -> "m4.imhentai.xxx" in 632482..816010 -> "m5.imhentai.xxx" in 816011..970098 -> "m6.imhentai.xxx" - else -> "m7.imhentai.xxx" + in 970099..1121113 -> "m7.imhentai.xxx" + else -> "m8.imhentai.xxx" } val images = json.parseToJsonElement( diff --git a/src/all/izneo/AndroidManifest.xml b/src/all/izneo/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/izneo/AndroidManifest.xml +++ b/src/all/izneo/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/junmeitu/AndroidManifest.xml b/src/all/junmeitu/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/all/junmeitu/AndroidManifest.xml +++ b/src/all/junmeitu/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/all/kavita/AndroidManifest.xml b/src/all/kavita/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/kavita/AndroidManifest.xml +++ b/src/all/kavita/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/komga/AndroidManifest.xml b/src/all/komga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/komga/AndroidManifest.xml +++ b/src/all/komga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/komga/build.gradle b/src/all/komga/build.gradle index f04faa5c72..c603e66997 100644 --- a/src/all/komga/build.gradle +++ b/src/all/komga/build.gradle @@ -6,11 +6,7 @@ ext { extName = 'Komga' pkgNameSuffix = 'all.komga' extClass = '.KomgaFactory' - extVersionCode = 47 -} - -dependencies { - compileOnly libs.bundles.reactivex + extVersionCode = 50 } apply from: "$rootDir/common.gradle" diff --git a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt index 3bfe5742fd..4f0adb59fe 100644 --- a/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt +++ b/src/all/komga/src/eu/kanade/tachiyomi/extension/all/komga/Komga.kt @@ -20,7 +20,8 @@ import eu.kanade.tachiyomi.extension.all.komga.dto.PageWrapperDto import eu.kanade.tachiyomi.extension.all.komga.dto.ReadListDto import eu.kanade.tachiyomi.extension.all.komga.dto.SeriesDto import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.asObservable +import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.model.Filter @@ -182,7 +183,7 @@ open class Komga(private val suffix: String = "") : ConfigurableSource, Unmetere override fun fetchMangaDetails(manga: SManga): Observable<SManga> { return client.newCall(GET(manga.url, headers)) - .asObservableSuccess() + .asObservable() .map { response -> mangaDetailsParse(response).apply { initialized = true } } diff --git a/src/all/lanraragi/AndroidManifest.xml b/src/all/lanraragi/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/lanraragi/AndroidManifest.xml +++ b/src/all/lanraragi/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/lanraragi/build.gradle b/src/all/lanraragi/build.gradle index 6cf8118a94..0bd070762a 100644 --- a/src/all/lanraragi/build.gradle +++ b/src/all/lanraragi/build.gradle @@ -6,11 +6,7 @@ ext { extName = 'LANraragi' pkgNameSuffix = 'all.lanraragi' extClass = '.LANraragiFactory' - extVersionCode = 13 -} - -dependencies { - compileOnly libs.bundles.reactivex + extVersionCode = 14 } apply from: "$rootDir/common.gradle" diff --git a/src/all/leagueoflegends/AndroidManifest.xml b/src/all/leagueoflegends/AndroidManifest.xml index 95481c3e28..8072ee00db 100644 --- a/src/all/leagueoflegends/AndroidManifest.xml +++ b/src/all/leagueoflegends/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension"/> +<manifest /> diff --git a/src/all/littlegarden/AndroidManifest.xml b/src/all/littlegarden/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/littlegarden/AndroidManifest.xml +++ b/src/all/littlegarden/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/luscious/AndroidManifest.xml b/src/all/luscious/AndroidManifest.xml index 7f8a6d5d3c..8072ee00db 100644 --- a/src/all/luscious/AndroidManifest.xml +++ b/src/all/luscious/AndroidManifest.xml @@ -1,4 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" -package="eu.kanade.tachiyomi.extension"> -</manifest> \ No newline at end of file +<manifest /> diff --git a/src/all/mangadex/AndroidManifest.xml b/src/all/mangadex/AndroidManifest.xml index 3fb6d85193..4faeb6138d 100644 --- a/src/all/mangadex/AndroidManifest.xml +++ b/src/all/mangadex/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/all/mangadex/assets/i18n/messages_en.properties b/src/all/mangadex/assets/i18n/messages_en.properties new file mode 100644 index 0000000000..7d27c05e69 --- /dev/null +++ b/src/all/mangadex/assets/i18n/messages_en.properties @@ -0,0 +1,146 @@ +alternative_titles=Alternative titles: +alternative_titles_in_description=Alternative titles in description +alternative_titles_in_description_summary=Include a manga's alternative titles at the end of its description +block_group_by_uuid=Block groups by UUID +block_group_by_uuid_summary=Chapters from blocked groups will not show up in Latest or Manga feed. Enter as a Comma-separated list of group UUIDs +block_uploader_by_uuid=Block uploader by UUID +block_uploader_by_uuid_summary=Chapters from blocked uploaders will not show up in Latest or Manga feed. Enter as a Comma-separated list of uploader UUIDs +content=Content +content_gore=Gore +content_rating=Content rating +content_rating_erotica=Erotica +content_rating_genre=Content rating: %s +content_rating_pornographic=Pornographic +content_rating_safe=Safe +content_rating_suggestive=Suggestive +content_sexual_violence=Sexual violence +cover_quality=Cover quality +cover_quality_low=Low +cover_quality_medium=Medium +cover_quality_original=Original +data_saver=Data saver +data_saver_summary=Enables smaller, more compressed images +excluded_tags_mode=Excluded tags mode +filter_original_languages=Filter original languages +filter_original_languages_summary=Only show content that was originally published in the selected languages in both latest and browse +format=Format +format_adaptation=Adaptation +format_anthology=Anthology +format_award_winning=Award Winning +format_doujinshi=Doujinshi +format_fan_colored=Fan Colored +format_full_color=Full Color +format_long_strip=Long Strip +format_official_colored=Official Colored +format_oneshot=Oneshot +format_user_created=User Created +format_web_comic=Web Comic +format_yonkoma=4-Koma +genre=Genre +genre_action=Action +genre_adventure=Adventure +genre_boys_love=Boy's Love +genre_comedy=Comedy +genre_crime=Crime +genre_drama=Drama +genre_fantasy=Fantasy +genre_girls_love=Girl's Love +genre_historical=Historical +genre_horror=Horror +genre_isekai=Isekai +genre_magical_girls=Magical Girls +genre_mecha=Mecha +genre_medical=Medical +genre_mystery=Mystery +genre_philosophical=Philosophical +genre_romance=Romance +genre_sci_fi=Sci-Fi +genre_slice_of_life=Slice of Life +genre_sports=Sports +genre_superhero=Superhero +genre_thriller=Thriller +genre_tragedy=Tragedy +genre_wuxia=Wuxia +has_available_chapters=Has available chapters +included_tags_mode=Included tags mode +invalid_author_id=Not a valid author ID +invalid_manga_id=Not a valid manga ID +invalid_group_id=Not a valid group ID +invalid_uuids=The text contains invalid UUIDs +migrate_warning=Migrate this entry from MangaDex to MangaDex to update it +mode_and=And +mode_or=Or +no_group=No Group +no_series_in_list=No series in the list +original_language=Original language +original_language_filter_chinese=%s (Manhua) +original_language_filter_japanese=%s (Manga) +original_language_filter_korean=%s (Manhwa) +publication_demographic=Publication demographic +publication_demographic_josei=Josei +publication_demographic_none=None +publication_demographic_seinen=Seinen +publication_demographic_shoujo=Shoujo +publication_demographic_shounen=Shounen +sort=Sort +sort_alphabetic=Alphabetic +sort_chapter_uploaded_at=Chapter uploaded at +sort_content_created_at=Content created at +sort_content_info_updated_at=Content info updated at +sort_number_of_follows=Number of follows +sort_rating=Rating +sort_relevance=Relevance +sort_year=Year +standard_content_rating=Default content rating +standard_content_rating_summary=Show content with the selected ratings by default +standard_https_port=Use HTTPS port 443 only +standard_https_port_summary=Enable to only request image servers that use port 443. This allows users with stricter firewall restrictions to access MangaDex images +status=Status +status_cancelled=Cancelled +status_completed=Completed +status_hiatus=Hiatus +status_ongoing=Ongoing +tags_mode=Tags mode +theme=Theme +theme_aliens=Aliens +theme_animals=Animals +theme_cooking=Cooking +theme_crossdressing=Crossdressing +theme_delinquents=Delinquents +theme_demons=Demons +theme_gender_swap=Genderswap +theme_ghosts=Ghosts +theme_gyaru=Gyaru +theme_harem=Harem +theme_incest=Incest +theme_loli=Loli +theme_mafia=Mafia +theme_magic=Magic +theme_martial_arts=Martial Arts +theme_military=Military +theme_monster_girls=Monster Girls +theme_monsters=Monsters +theme_music=Music +theme_ninja=Ninja +theme_office_workers=Office Workers +theme_police=Police +theme_post_apocalyptic=Post-Apocalyptic +theme_psychological=Psychological +theme_reincarnation=Reincarnation +theme_reverse_harem=Reverse Harem +theme_samurai=Samurai +theme_school_life=School Life +theme_shota=Shota +theme_supernatural=Supernatural +theme_survival=Survival +theme_time_travel=Time Travel +theme_traditional_games=Traditional Games +theme_vampires=Vampires +theme_video_games=Video Games +theme_villainess=Vilania +theme_virtual_reality=Virtual Reality +theme_zombies=Zombies +try_using_first_volume_cover=Attempt to use the first volume cover as cover +try_using_first_volume_cover_summary=May need to manually refresh entries already in library. Otherwise, clear database to have new covers to show up. +unable_to_process_chapter_request=Unable to process Chapter request. HTTP code: %d +uploaded_by=Uploaded by %s diff --git a/src/all/mangadex/assets/i18n/messages_es.properties b/src/all/mangadex/assets/i18n/messages_es.properties new file mode 100644 index 0000000000..fc1dc27ab3 --- /dev/null +++ b/src/all/mangadex/assets/i18n/messages_es.properties @@ -0,0 +1,108 @@ +block_group_by_uuid=Bloquear grupos por UUID +block_group_by_uuid_summary=Los capítulos de los grupos bloqueados no aparecerán en Recientes o en el Feed de mangas. Introduce una coma para separar la lista de UUIDs +block_uploader_by_uuid=Bloquear uploader por UUID +block_uploader_by_uuid_summary=Los capítulos de los uploaders bloqueados no aparecerán en Recientes o en el Feed de mangas. Introduce una coma para separar la lista de UUIDs +content=Contenido +content_rating=Clasificación de contenido +content_rating_erotica=Erótico +content_rating_genre=Clasificación: %s +content_rating_pornographic=Pornográfico +content_rating_safe=Seguro +content_rating_suggestive=Sugestivo +content_sexual_violence=Violencia sexual +cover_quality=Calidad de la portada +cover_quality_low=Bajo +cover_quality_medium=Medio +data_saver=Ahorro de datos +data_saver_summary=Utiliza imágenes más pequeñas y más comprimidas +excluded_tags_mode=Modo de etiquetas excluidas +filter_original_languages=Filtrar por lenguajes +filter_original_languages_summary=Muestra solo el contenido publicado en los idiomas seleccionados en recientes y en la búsqueda +format=Formato +format_adaptation=Adaptación +format_anthology=Antología +format_award_winning=Ganador de premio +format_fan_colored=Coloreado por fans +format_full_color=Todo a color +format_long_strip=Tira larga +format_official_colored=Coloreo oficial +format_user_created=Creado por usuario +genre=Genero +genre_action=Acción +genre_adventure=Aventura +genre_comedy=Comedia +genre_crime=Crimen +genre_fantasy=Fantasia +genre_historical=Histórico +genre_magical_girls=Chicas mágicas +genre_medical=Medico +genre_mystery=Misterio +genre_philosophical=Filosófico +genre_sci_fi=Ciencia ficción +genre_slice_of_life=Recuentos de la vida +genre_sports=Deportes +genre_superhero=Superhéroes +genre_tragedy=Tragedia +has_available_chapters=Tiene capítulos disponibles +included_tags_mode=Modo de etiquetas incluidas +invalid_author_id=ID de autor inválida +invalid_group_id=ID de grupo inválida +migrate_warning=Migre la entrada MangaDex a MangaDex para actualizarla +mode_and=Y +mode_or=O +no_group=Sin grupo +no_series_in_list=No hay series en la lista +original_language=Lenguaje original +publication_demographic=Demografía +publication_demographic_none=Ninguna +sort=Ordenar +sort_alphabetic=Alfabeticamente +sort_chapter_uploaded_at=Capítulo subido en +sort_content_created_at=Contenido creado en +sort_content_info_updated_at=Información del contenido actualizada en +sort_number_of_follows=Número de seguidores +sort_rating=Calificación +sort_relevance=Relevancia +sort_year=Año +standard_content_rating=Clasificación de contenido por defecto +standard_content_rating_summary=Muestra el contenido con la clasificación de contenido seleccionada por defecto +standard_https_port=Utilizar el puerto 443 de HTTPS +standard_https_port_summary=Habilite esta opción solicitar las imágenes a los servidores que usan el puerto 443. Esto permite a los usuarios con restricciones estrictas de firewall acceder a las imagenes en MangaDex +status=Estado +status_cancelled=Cancelado +status_completed=Completado +status_hiatus=Pausado +status_ongoing=Publicandose +tags_mode=Modo de etiquetas +theme=Tema +theme_aliens=Alienígenas +theme_animals=Animales +theme_cooking=Cocina +theme_crossdressing=Travestismo +theme_delinquents=Delincuentes +theme_demons=Demonios +theme_gender_swap=Cambio de sexo +theme_ghosts=Fantasmas +theme_incest=Incesto +theme_magic=Magia +theme_martial_arts=Artes marciales +theme_military=Militar +theme_monster_girls=Chicas monstruo +theme_monsters=Monstruos +theme_music=Musica +theme_office_workers=Oficinistas +theme_police=Policial +theme_post_apocalyptic=Post-apocalíptico +theme_psychological=Psicológico +theme_reincarnation=Reencarnación +theme_reverse_harem=Harem inverso +theme_school_life=Vida escolar +theme_supernatural=Sobrenatural +theme_survival=Supervivencia +theme_time_travel=Viaje en el tiempo +theme_traditional_games=Juegos tradicionales +theme_vampires=Vampiros +theme_villainess=Villana +theme_virtual_reality=Realidad virtual +unable_to_process_chapter_request=No se ha podido procesar la solicitud del capítulo. Código HTTP: %d +uploaded_by=Subido por %s \ No newline at end of file diff --git a/src/all/mangadex/assets/i18n/messages_pt_br.properties b/src/all/mangadex/assets/i18n/messages_pt_br.properties new file mode 100644 index 0000000000..ecd4ca7be2 --- /dev/null +++ b/src/all/mangadex/assets/i18n/messages_pt_br.properties @@ -0,0 +1,119 @@ +alternative_titles=Títulos alternativos: +alternative_titles_in_description=Títulos alternativos na descrição +alternative_titles_in_description_summary=Inclui os títulos alternativos das séries no final de cada descrição +block_group_by_uuid=Bloquear grupos por UUID +block_group_by_uuid_summary=Capítulos de grupos bloqueados não irão aparecer no feed de Recentes ou Mangás. Digite uma lista de UUIDs dos grupos separados por vírgulas +block_uploader_by_uuid=Bloquear uploaders por UUID +block_uploader_by_uuid_summary=Capítulos de usuários bloqueados não irão aparecer no feed de Recentes ou Mangás. Digite uma lista de UUIDs dos usuários separados por vírgulas +content=Conteúdo +content_rating=Classificação de conteúdo +content_rating_erotica=Erótico +content_rating_genre=Classificação: %s +content_rating_pornographic=Pornográfico +content_rating_safe=Seguro +content_rating_suggestive=Sugestivo +content_sexual_violence=Violência sexual +cover_quality=Qualidade da capa +cover_quality_low=Baixa +cover_quality_medium=Média +data_saver=Economia de dados +data_saver_summary=Utiliza imagens menores e mais compactadas +excluded_tags_mode=Modo de exclusão de tags +filter_original_languages=Filtrar os idiomas originais +filter_original_languages_summary=Mostra somente conteúdos que foram publicados originalmente nos idiomas selecionados nas seções de recentes e navegar +format=Formato +format_adaptation=Adaptação +format_anthology=Antologia +format_award_winning=Premiado +format_fan_colored=Colorizado por fãs +format_full_color=Colorido +format_long_strip=Vertical +format_official_colored=Colorizado oficialmente +format_user_created=Criado por usuários +genre=Gênero +genre_action=Ação +genre_adventure=Aventura +genre_comedy=Comédia +genre_crime=Crime +genre_fantasy=Fantasia +genre_historical=Histórico +genre_magical_girls=Garotas mágicas +genre_medical=Médico +genre_mystery=Mistério +genre_philosophical=Filosófico +genre_sci_fi=Ficção científica +genre_slice_of_life=Cotidiano +genre_sports=Esportes +genre_superhero=Super-heroi +genre_tragedy=Tragédia +has_available_chapters=Há capítulos disponíveis +included_tags_mode=Modo de inclusão de tags +invalid_author_id=ID do autor inválido +invalid_manga_id=ID do mangá inválido +invalid_group_id=ID do grupo inválido +invalid_uuids=O texto contém UUIDs inválidos +migrate_warning=Migre esta entrada do MangaDex para o MangaDex para atualizar +mode_and=E +mode_or=Ou +no_group=Sem grupo +no_series_in_list=Sem séries na lista +original_language=Idioma original +original_language_filter_japanese=%s (Mangá) +publication_demographic=Demografia da publicação +publication_demographic_none=Nenhuma +sort=Ordenar +sort_alphabetic=Alfabeticamente +sort_chapter_uploaded_at=Upload do capítulo +sort_content_created_at=Criação do conteúdo +sort_content_info_updated_at=Atualização das informações +sort_number_of_follows=Número de seguidores +sort_rating=Nota +sort_relevance=Relevância +sort_year=Ano de lançamento +standard_content_rating=Classificação de conteúdo padrão +standard_content_rating_summary=Mostra os conteúdos com as classificações selecionadas por padrão +standard_https_port=Utilizar somente a porta 443 do HTTPS +standard_https_port_summary=Ative para fazer requisições em somente servidores de imagem que usem a porta 443. Isso permite com que usuários com regras mais restritas de firewall possam acessar as imagens do MangaDex. +status=Estado +status_cancelled=Cancelado +status_completed=Completo +status_hiatus=Hiato +status_ongoing=Em andamento +tags_mode=Modo das tags +theme=Tema +theme_aliens=Alienígenas +theme_animals=Animais +theme_cooking=Culinária +theme_delinquents=Delinquentes +theme_demons=Demônios +theme_gender_swap=Troca de gêneros +theme_ghosts=Fantasmas +theme_harem=Harém +theme_incest=Incesto +theme_mafia=Máfia +theme_magic=Magia +theme_martial_arts=Artes marciais +theme_military=Militar +theme_monster_girls=Garotas monstro +theme_monsters=Monstros +theme_music=Musical +theme_office_workers=Funcionários de escritório +theme_police=Policial +theme_post_apocalyptic=Pós-apocalíptico +theme_psychological=Psicológico +theme_reincarnation=Reencarnação +theme_reverse_harem=Harém reverso +theme_school_life=Vida escolar +theme_supernatural=Sobrenatural +theme_survival=Sobrevivência +theme_time_travel=Viagem no tempo +theme_traditional_games=Jogos tradicionais +theme_vampires=Vampiros +theme_video_games=Videojuegos +theme_villainess=Villainess +theme_virtual_reality=Realidade virtual +theme_zombies=Zumbis +try_using_first_volume_cover=Tentar usar a capa do primeiro volume como capa +try_using_first_volume_cover_summary=Pode ser necessário atualizar os itens já adicionados na biblioteca. Alternativamente, limpe o banco de dados para as novas capas aparecerem. +unable_to_process_chapter_request=Não foi possível processar a requisição do capítulo. Código HTTP: %d +uploaded_by=Enviado por %s \ No newline at end of file diff --git a/src/all/mangadex/assets/i18n/messages_ru.properties b/src/all/mangadex/assets/i18n/messages_ru.properties new file mode 100644 index 0000000000..fbc49b49f0 --- /dev/null +++ b/src/all/mangadex/assets/i18n/messages_ru.properties @@ -0,0 +1,138 @@ +block_group_by_uuid=Заблокировать группы по UUID +block_group_by_uuid_summary=Главы от заблокированных групп не будут отображаться в последних обновлениях и в списке глав тайтла. Введите через запятую список UUID групп. +block_uploader_by_uuid=Заблокировать загрузчика по UUID +block_uploader_by_uuid_summary=Главы от заблокированных загрузчиков не будут отображаться в последних обновлениях и в списке глав тайтла. Введите через запятую список UUID загрузчиков. +content=Неприемлемый контент +content_gore=Жестокость +content_rating=Рейтинг контента +content_rating_erotica=Эротический +content_rating_genre=Рейтинг контента: %s +content_rating_pornographic=Порнографический +content_rating_safe=Безопасный +content_rating_suggestive=Намекающий +content_sexual_violence=Сексуальное насилие +cover_quality=Качество обложки +cover_quality_low=Низкое +cover_quality_medium=Среднее +cover_quality_original=Оригинальное +data_saver=Экономия трафика +data_saver_summary=Использует меньшие по размеру, сжатые изображения +excluded_tags_mode=Исключая +filter_original_languages=Фильтр по языку оригинала +filter_original_languages_summary=Показывать тайтлы которые изначально были выпущены только в выбранных языках в последних обновлениях и при поиске +format=Формат +format_adaptation=Адаптация +format_anthology=Антология +format_award_winning=Отмеченный наградами +format_doujinshi=Додзинси +format_fan_colored=Раскрашенная фанатами +format_full_color=В цвете +format_long_strip=Веб +format_official_colored=Официально раскрашенная +format_oneshot=Сингл +format_user_created=Созданная пользователями +format_web_comic=Веб-комикс +format_yonkoma=Ёнкома +genre=Жанр +genre_action=Боевик +genre_adventure=Приключения +genre_boys_love=BL +genre_comedy=Комедия +genre_crime=Криминал +genre_drama=Драма +genre_fantasy=Фэнтези +genre_girls_love=GL +genre_historical=История +genre_horror=Ужасы +genre_isekai=Исекай +genre_magical_girls=Махо-сёдзё +genre_mecha=Меха +genre_medical=Медицина +genre_mystery=Мистика +genre_philosophical=Философия +genre_romance=Романтика +genre_sci_fi=Научная фантастика +genre_slice_of_life=Повседневность +genre_sports=Спорт +genre_superhero=Супергерои +genre_thriller=Триллер +genre_tragedy=Трагедия +genre_wuxia=Культивация +has_available_chapters=Есть главы +included_tags_mode=Включая +invalid_author_id=Недействительный ID автора +invalid_group_id=Недействительный ID группы +mode_and=И +mode_or=Или +no_group=Нет группы +no_series_in_list=Лист пуст +original_language=Язык оригинала +original_language_filter_chinese=%s (Манхуа) +original_language_filter_japanese=%s (Манга) +original_language_filter_korean=%s (Манхва) +publication_demographic=Целевая аудитория +publication_demographic_josei=Дзёсэй +publication_demographic_none=Нет +publication_demographic_seinen=Сэйнэн +publication_demographic_shoujo=Сёдзё +publication_demographic_shounen=Сёнэн +sort=Сортировать по +sort_alphabetic=Алфавиту +sort_chapter_uploaded_at=Загруженной главе +sort_content_created_at=По дате создания +sort_content_info_updated_at=По дате обновления +sort_number_of_follows=Количеству фолловеров +sort_rating=Популярности +sort_relevance=Лучшему соответствию +sort_year=Год +standard_content_rating=Рейтинг контента по умолчанию +standard_content_rating_summary=Показывать контент с выбранным рейтингом по умолчанию +standard_https_port=Использовать только HTTPS порт 443 +standard_https_port_summary=Запрашивает изображения только с серверов которые используют порт 443. Это позволяет пользователям со строгими правилами брандмауэра загружать изображения с MangaDex. +status=Статус +status_cancelled=Отменён +status_completed=Завершён +status_hiatus=Приостановлен +status_ongoing=Онгоинг +tags_mode=Режим поиска +theme=Теги +theme_aliens=Инопланетяне +theme_animals=Животные +theme_cooking=Животные +theme_crossdressing=Кроссдрессинг +theme_delinquents=Хулиганы +theme_demons=Демоны +theme_gender_swap=Смена гендера +theme_ghosts=Призраки +theme_gyaru=Гяру +theme_harem=Гарем +theme_incest=Инцест +theme_loli=Лоли +theme_mafia=Мафия +theme_magic=Магия +theme_martial_arts=Боевые исскуства +theme_military=Военные +theme_monster_girls=Монстродевушки +theme_monsters=Монстры +theme_music=Музыка +theme_ninja=Ниндзя +theme_office_workers=Офисные работники +theme_police=Полиция +theme_post_apocalyptic=Постапокалиптика +theme_psychological=Психология +theme_reincarnation=Реинкарнация +theme_reverse_harem=Обратный гарем +theme_samurai=Самураи +theme_school_life=Школа +theme_shota=Шота +theme_supernatural=Сверхъестественное +theme_survival=Выживание +theme_time_travel=Путешествие во времени +theme_traditional_games=Путешествие во времени +theme_vampires=Вампиры +theme_video_games=Видеоигры +theme_villainess=Злодейка +theme_virtual_reality=Виртуальная реальность +theme_zombies=Зомби +unable_to_process_chapter_request=Не удалось обработать ссылку на главу. Ошибка: %d +uploaded_by=Загрузил %s \ No newline at end of file diff --git a/src/all/mangadex/build.gradle b/src/all/mangadex/build.gradle index af4c1d2e27..a3a74a415b 100644 --- a/src/all/mangadex/build.gradle +++ b/src/all/mangadex/build.gradle @@ -6,8 +6,12 @@ ext { extName = 'MangaDex' pkgNameSuffix = 'all.mangadex' extClass = '.MangaDexFactory' - extVersionCode = 183 + extVersionCode = 188 isNsfw = true } +dependencies { + implementation(project(":lib-i18n")) +} + apply from: "$rootDir/common.gradle" diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt index b7f8eaa11a..69f79d63bc 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MDConstants.kt @@ -1,8 +1,10 @@ package eu.kanade.tachiyomi.extension.all.mangadex +import eu.kanade.tachiyomi.lib.i18n.Intl import java.text.SimpleDateFormat import java.util.Locale import java.util.TimeZone +import kotlin.time.Duration.Companion.minutes object MDConstants { @@ -31,7 +33,7 @@ object MDConstants { const val atHomePostUrl = "https://api.mangadex.network/report" val whitespaceRegex = "\\s".toRegex() - const val mdAtHomeTokenLifespan = 5 * 60 * 1000 + val mdAtHomeTokenLifespan = 5.minutes.inWholeMilliseconds val dateFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss+SSS", Locale.US) .apply { timeZone = TimeZone.getTimeZone("UTC") } @@ -49,8 +51,8 @@ object MDConstants { return "${coverQualityPref}_$dexLang" } - fun getCoverQualityPreferenceEntries(intl: MangaDexIntl) = - arrayOf(intl.coverQualityOriginal, intl.coverQualityMedium, intl.coverQualityLow) + fun getCoverQualityPreferenceEntries(intl: Intl) = + arrayOf(intl["cover_quality_original"], intl["cover_quality_medium"], intl["cover_quality_low"]) fun getCoverQualityPreferenceEntryValues() = arrayOf("", ".512.jpg", ".256.jpg") @@ -74,6 +76,12 @@ object MDConstants { const val contentRatingPrefValErotica = "erotica" const val contentRatingPrefValPornographic = "pornographic" val contentRatingPrefDefaults = setOf(contentRatingPrefValSafe, contentRatingPrefValSuggestive) + val allContentRatings = setOf( + contentRatingPrefValSafe, + contentRatingPrefValSuggestive, + contentRatingPrefValErotica, + contentRatingPrefValPornographic, + ) fun getContentRatingPrefKey(dexLang: String): String { return "${contentRatingPref}_$dexLang" diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt index 2bb9a1495e..f3f7aadbf3 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDex.kt @@ -34,7 +34,6 @@ import okhttp3.CacheControl import okhttp3.Headers import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Request import okhttp3.Response import rx.Observable @@ -42,9 +41,8 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.Date -abstract class MangaDex(final override val lang: String, private val dexLang: String) : - ConfigurableSource, - HttpSource() { +abstract class MangaDex(final override val lang: String, private val dexLang: String = lang) : + ConfigurableSource, HttpSource() { override val name = MangaDexIntl.MANGADEX_NAME @@ -82,12 +80,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St .addQueryParameter("includes[]", MDConstants.coverArt) .addQueryParameter("contentRating[]", preferences.contentRating) .addQueryParameter("originalLanguage[]", preferences.originalLanguages) + .build() - return GET( - url = url.build().toString(), - headers = headers, - cache = CacheControl.FORCE_NETWORK, - ) + return GET(url, headers, CacheControl.FORCE_NETWORK) } override fun popularMangaParse(response: Response): MangasPage { @@ -96,30 +91,50 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St } val mangaListDto = response.parseAs<MangaListDto>() - val hasMoreResults = mangaListDto.limit + mangaListDto.offset < mangaListDto.total val coverSuffix = preferences.coverQuality val firstVolumeCovers = fetchFirstVolumeCovers(mangaListDto.data).orEmpty() val mangaList = mangaListDto.data.map { mangaDataDto -> - val fileName = firstVolumeCovers[mangaDataDto.id] ?: mangaDataDto.relationships - .filterIsInstance<CoverArtDto>() - .firstOrNull() - ?.attributes?.fileName + val fileName = firstVolumeCovers.getOrElse(mangaDataDto.id) { + mangaDataDto.relationships + .firstInstanceOrNull<CoverArtDto>() + ?.attributes?.fileName + } helper.createBasicManga(mangaDataDto, fileName, coverSuffix, dexLang) } - return MangasPage(mangaList, hasMoreResults) + return MangasPage(mangaList, mangaListDto.hasNextPage) } // Latest manga section + override fun latestUpdatesRequest(page: Int): Request { + val url = MDConstants.apiChapterUrl.toHttpUrl().newBuilder() + .addQueryParameter("offset", helper.getLatestChapterOffset(page)) + .addQueryParameter("limit", MDConstants.latestChapterLimit.toString()) + .addQueryParameter("translatedLanguage[]", dexLang) + .addQueryParameter("order[publishAt]", "desc") + .addQueryParameter("includeFutureUpdates", "0") + .addQueryParameter("originalLanguage[]", preferences.originalLanguages) + .addQueryParameter("contentRating[]", preferences.contentRating) + .addQueryParameter( + "excludedGroups[]", + MDConstants.defaultBlockedGroups + preferences.blockedGroups, + ) + .addQueryParameter("excludedUploaders[]", preferences.blockedUploaders) + .addQueryParameter("includeFuturePublishAt", "0") + .addQueryParameter("includeEmptyPages", "0") + .build() + + return GET(url, headers, CacheControl.FORCE_NETWORK) + } + /** * The API endpoint can't sort by date yet, so not implemented. */ override fun latestUpdatesParse(response: Response): MangasPage { val chapterListDto = response.parseAs<ChapterListDto>() - val hasMoreResults = chapterListDto.limit + chapterListDto.offset < chapterListDto.total val mangaIds = chapterListDto.data .flatMap { it.relationships } @@ -128,13 +143,14 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St .distinct() .toSet() - val mangaUrl = MDConstants.apiMangaUrl.toHttpUrlOrNull()!!.newBuilder() + val mangaApiUrl = MDConstants.apiMangaUrl.toHttpUrl().newBuilder() .addQueryParameter("includes[]", MDConstants.coverArt) .addQueryParameter("limit", mangaIds.size.toString()) .addQueryParameter("contentRating[]", preferences.contentRating) .addQueryParameter("ids[]", mangaIds) + .build() - val mangaRequest = GET(mangaUrl.build().toString(), headers, CacheControl.FORCE_NETWORK) + val mangaRequest = GET(mangaApiUrl, headers, CacheControl.FORCE_NETWORK) val mangaResponse = client.newCall(mangaRequest).execute() val mangaListDto = mangaResponse.parseAs<MangaListDto>() val firstVolumeCovers = fetchFirstVolumeCovers(mangaListDto.data).orEmpty() @@ -144,34 +160,15 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val coverSuffix = preferences.coverQuality val mangaList = mangaIds.mapNotNull { mangaDtoMap[it] }.map { mangaDataDto -> - val fileName = firstVolumeCovers[mangaDataDto.id] ?: mangaDataDto.relationships - .filterIsInstance<CoverArtDto>() - .firstOrNull() - ?.attributes?.fileName + val fileName = firstVolumeCovers.getOrElse(mangaDataDto.id) { + mangaDataDto.relationships + .firstInstanceOrNull<CoverArtDto>() + ?.attributes?.fileName + } helper.createBasicManga(mangaDataDto, fileName, coverSuffix, dexLang) } - return MangasPage(mangaList, hasMoreResults) - } - - override fun latestUpdatesRequest(page: Int): Request { - val url = MDConstants.apiChapterUrl.toHttpUrlOrNull()!!.newBuilder() - .addQueryParameter("offset", helper.getLatestChapterOffset(page)) - .addQueryParameter("limit", MDConstants.latestChapterLimit.toString()) - .addQueryParameter("translatedLanguage[]", dexLang) - .addQueryParameter("order[publishAt]", "desc") - .addQueryParameter("includeFutureUpdates", "0") - .addQueryParameter("originalLanguage[]", preferences.originalLanguages) - .addQueryParameter("contentRating[]", preferences.contentRating) - .addQueryParameter( - "excludedGroups[]", - MDConstants.defaultBlockedGroups + preferences.blockedGroups, - ) - .addQueryParameter("excludedUploaders[]", preferences.blockedUploaders) - .addQueryParameter("includeFuturePublishAt", "0") - .addQueryParameter("includeEmptyPages", "0") - - return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK) + return MangasPage(mangaList, chapterListDto.hasNextPage) } // Search manga section @@ -207,7 +204,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St ), ) .asObservableSuccess() - .map { searchMangaListParse(it, page) } + .map { searchMangaListParse(it, page, filters) } else -> super.fetchSearchManga(page, query.trim(), filters) } @@ -218,38 +215,42 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St .asObservable() .map { response -> if (response.isSuccessful.not()) { - throw Exception(helper.intl.unableToProcessChapterRequest(response.code)) + throw Exception(helper.intl.format("unable_to_process_chapter_request", response.code)) } response.parseAs<ChapterDto>().data!!.relationships - .filterIsInstance<MangaDataDto>() - .firstOrNull()!!.id + .firstInstanceOrNull<MangaDataDto>()!!.id } } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + if (query.startsWith(MDConstants.prefixIdSearch)) { + val mangaId = query.removePrefix(MDConstants.prefixIdSearch) + + if (!helper.containsUuid(mangaId)) { + throw Exception(helper.intl["invalid_manga_id"]) + } + + val url = MDConstants.apiMangaUrl.toHttpUrl().newBuilder() + .addQueryParameter("ids[]", query.removePrefix(MDConstants.prefixIdSearch)) + .addQueryParameter("includes[]", MDConstants.coverArt) + .addQueryParameter("contentRating[]", MDConstants.allContentRatings) + .build() + + return GET(url, headers, CacheControl.FORCE_NETWORK) + } + val tempUrl = MDConstants.apiMangaUrl.toHttpUrl().newBuilder() .addQueryParameter("limit", MDConstants.mangaLimit.toString()) .addQueryParameter("offset", helper.getMangaListOffset(page)) .addQueryParameter("includes[]", MDConstants.coverArt) when { - query.startsWith(MDConstants.prefixIdSearch) -> { - val url = MDConstants.apiMangaUrl.toHttpUrlOrNull()!!.newBuilder() - .addQueryParameter("ids[]", query.removePrefix(MDConstants.prefixIdSearch)) - .addQueryParameter("includes[]", MDConstants.coverArt) - .addQueryParameter("contentRating[]", "safe") - .addQueryParameter("contentRating[]", "suggestive") - .addQueryParameter("contentRating[]", "erotica") - .addQueryParameter("contentRating[]", "pornographic") - - return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK) - } - query.startsWith(MDConstants.prefixGrpSearch) -> { val groupId = query.removePrefix(MDConstants.prefixGrpSearch) + if (!helper.containsUuid(groupId)) { - throw Exception(helper.intl.invalidGroupId) + throw Exception(helper.intl["invalid_group_id"]) } tempUrl.addQueryParameter("group", groupId) @@ -257,8 +258,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St query.startsWith(MDConstants.prefixAuthSearch) -> { val authorId = query.removePrefix(MDConstants.prefixAuthSearch) + if (!helper.containsUuid(authorId)) { - throw Exception(helper.intl.invalidAuthorId) + throw Exception(helper.intl["invalid_author_id"]) } tempUrl.addQueryParameter("authorOrArtist", authorId) @@ -288,18 +290,18 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St return GET("${MDConstants.apiListUrl}/$list", headers, CacheControl.FORCE_NETWORK) } - private fun searchMangaListParse(response: Response, page: Int): MangasPage { + private fun searchMangaListParse(response: Response, page: Int, filters: FilterList): MangasPage { val listDto = response.parseAs<ListDto>() val listDtoFiltered = listDto.data!!.relationships.filterIsInstance<MangaDataDto>() val amount = listDtoFiltered.count() if (amount < 1) { - throw Exception(helper.intl.noSeriesInList) + throw Exception(helper.intl["no_series_in_list"]) } val minIndex = (page - 1) * MDConstants.mangaLimit - val url = MDConstants.apiMangaUrl.toHttpUrl().newBuilder() + val tempUrl = MDConstants.apiMangaUrl.toHttpUrl().newBuilder() .addQueryParameter("limit", MDConstants.mangaLimit.toString()) .addQueryParameter("offset", "0") .addQueryParameter("includes[]", MDConstants.coverArt) @@ -309,13 +311,20 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St .map(MangaDataDto::id) .toSet() - url.addQueryParameter("ids[]", ids) + tempUrl.addQueryParameter("ids[]", ids) + + val finalUrl = helper.mdFilters.addFiltersToUrl( + url = tempUrl, + filters = filters.ifEmpty { getFilterList() }, + dexLang = dexLang, + ) - val mangaRequest = GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK) + val mangaRequest = GET(finalUrl, headers, CacheControl.FORCE_NETWORK) val mangaResponse = client.newCall(mangaRequest).execute() val mangaList = searchMangaListParse(mangaResponse) - val hasNextPage = amount.toFloat() / MDConstants.mangaLimit - (page.toFloat() - 1) > 1 + val hasNextPage = amount.toFloat() / MDConstants.mangaLimit - (page.toFloat() - 1) > 1 && + ids.size == MDConstants.mangaLimit return MangasPage(mangaList, hasNextPage) } @@ -334,10 +343,11 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val coverSuffix = preferences.coverQuality val mangaList = mangaListDto.data.map { mangaDataDto -> - val fileName = firstVolumeCovers[mangaDataDto.id] ?: mangaDataDto.relationships - .filterIsInstance<CoverArtDto>() - .firstOrNull() - ?.attributes?.fileName + val fileName = firstVolumeCovers.getOrElse(mangaDataDto.id) { + mangaDataDto.relationships + .firstInstanceOrNull<CoverArtDto>() + ?.attributes?.fileName + } helper.createBasicManga(mangaDataDto, fileName, coverSuffix, dexLang) } @@ -345,7 +355,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St } private fun searchMangaUploaderRequest(page: Int, uploader: String): Request { - val url = MDConstants.apiChapterUrl.toHttpUrlOrNull()!!.newBuilder() + val url = MDConstants.apiChapterUrl.toHttpUrl().newBuilder() .addQueryParameter("offset", helper.getLatestChapterOffset(page)) .addQueryParameter("limit", MDConstants.latestChapterLimit.toString()) .addQueryParameter("translatedLanguage[]", dexLang) @@ -361,18 +371,15 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St MDConstants.defaultBlockedGroups + preferences.blockedGroups, ) .addQueryParameter("excludedUploaders[]", preferences.blockedUploaders) + .build() - return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK) + return GET(url, headers, CacheControl.FORCE_NETWORK) } // Manga Details section override fun getMangaUrl(manga: SManga): String { - // TODO: Remove once redirect for /manga is fixed. - val title = manga.title - val url = "${baseUrl}${manga.url.replace("manga", "title")}" - - return "$url/" + helper.titleToSlug(title) + return baseUrl + manga.url + "/" + helper.titleToSlug(manga.title) } /** @@ -382,15 +389,16 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St */ override fun mangaDetailsRequest(manga: SManga): Request { if (!helper.containsUuid(manga.url.trim())) { - throw Exception(helper.intl.migrateWarning) + throw Exception(helper.intl["migrate_warning"]) } val url = (MDConstants.apiUrl + manga.url).toHttpUrl().newBuilder() .addQueryParameter("includes[]", MDConstants.coverArt) .addQueryParameter("includes[]", MDConstants.author) .addQueryParameter("includes[]", MDConstants.artist) + .build() - return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK) + return GET(url, headers, CacheControl.FORCE_NETWORK) } override fun mangaDetailsParse(response: Response): SManga { @@ -442,9 +450,9 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St return null } - val mangaMap = mangaList.associate { it.id to it.attributes!! } - .filterValues { !it.originalLanguage.isNullOrEmpty() } - val locales = mangaList.mapNotNull { it.attributes!!.originalLanguage }.distinct() + val safeMangaList = mangaList.filterNot { it.attributes?.originalLanguage.isNullOrEmpty() } + val mangaMap = safeMangaList.associate { it.id to it.attributes!! } + val locales = safeMangaList.mapNotNull { it.attributes!!.originalLanguage }.distinct() val limit = (mangaMap.size * locales.size).coerceAtMost(100) val apiUrl = "${MDConstants.apiUrl}/cover".toHttpUrl().newBuilder() @@ -453,7 +461,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St .addQueryParameter("locales[]", locales.toSet()) .addQueryParameter("limit", limit.toString()) .addQueryParameter("offset", "0") - .toString() + .build() val result = runCatching { client.newCall(GET(apiUrl, headers)).execute().parseAs<CoverArtListDto>().data @@ -462,10 +470,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val covers = result.getOrNull() ?: return null return covers - .groupBy { - it.relationships.filterIsInstance<MangaDataDto>() - .firstOrNull()!!.id - } + .groupBy { it.relationships.firstInstanceOrNull<MangaDataDto>()!!.id } .mapValues { it.value.find { c -> c.attributes?.locale == mangaMap[it.key]?.originalLanguage } } @@ -482,7 +487,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St */ override fun chapterListRequest(manga: SManga): Request { if (!helper.containsUuid(manga.url)) { - throw Exception(helper.intl.migrateWarning) + throw Exception(helper.intl["migrate_warning"]) } return paginatedChapterListRequest(helper.getUUIDFromUrl(manga.url), 0) @@ -493,14 +498,12 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St */ private fun paginatedChapterListRequest(mangaId: String, offset: Int): Request { val url = helper.getChapterEndpoint(mangaId, offset, dexLang).toHttpUrl().newBuilder() - .addQueryParameter("contentRating[]", "safe") - .addQueryParameter("contentRating[]", "suggestive") - .addQueryParameter("contentRating[]", "erotica") - .addQueryParameter("contentRating[]", "pornographic") + .addQueryParameter("contentRating[]", MDConstants.allContentRatings) .addQueryParameter("excludedGroups[]", preferences.blockedGroups) .addQueryParameter("excludedUploaders[]", preferences.blockedUploaders) + .build() - return GET(url.build().toString(), headers, CacheControl.FORCE_NETWORK) + return GET(url, headers, CacheControl.FORCE_NETWORK) } override fun chapterListParse(response: Response): List<SChapter> { @@ -516,21 +519,20 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St .substringBefore("/feed") .substringAfter("${MDConstants.apiMangaUrl}/") - val limit = chapterListResponse.limit - var offset = chapterListResponse.offset - - var hasMoreResults = (limit + offset) < chapterListResponse.total + var hasNextPage = chapterListResponse.hasNextPage // Max results that can be returned is 500 so need to make more API - // calls if limit + offset > total chapters - while (hasMoreResults) { - offset += limit + // calls if the chapter list response has a next page. + while (hasNextPage) { + offset += chapterListResponse.limit + val newRequest = paginatedChapterListRequest(mangaId, offset) val newResponse = client.newCall(newRequest).execute() val newChapterList = newResponse.parseAs<ChapterListDto>() chapterListResults.addAll(newChapterList.data) - hasMoreResults = (limit + offset) < newChapterList.total + + hasNextPage = newChapterList.hasNextPage } return chapterListResults @@ -542,7 +544,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St override fun pageListRequest(chapter: SChapter): Request { if (!helper.containsUuid(chapter.url)) { - throw Exception(helper.intl.migrateWarning) + throw Exception(helper.intl["migrate_warning"]) } val chapterId = chapter.url.substringAfter("/chapter/") @@ -586,7 +588,7 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St override fun setupPreferenceScreen(screen: PreferenceScreen) { val coverQualityPref = ListPreference(screen.context).apply { key = MDConstants.getCoverQualityPreferenceKey(dexLang) - title = helper.intl.coverQuality + title = helper.intl["cover_quality"] entries = MDConstants.getCoverQualityPreferenceEntries(helper.intl) entryValues = MDConstants.getCoverQualityPreferenceEntryValues() setDefaultValue(MDConstants.getCoverQualityPreferenceDefaultValue()) @@ -605,8 +607,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val tryUsingFirstVolumeCoverPref = SwitchPreferenceCompat(screen.context).apply { key = MDConstants.getTryUsingFirstVolumeCoverPrefKey(dexLang) - title = helper.intl.tryUsingFirstVolumeCover - summary = helper.intl.tryUsingFirstVolumeCoverSummary + title = helper.intl["try_using_first_volume_cover"] + summary = helper.intl["try_using_first_volume_cover_summary"] setDefaultValue(MDConstants.tryUsingFirstVolumeCoverDefault) setOnPreferenceChangeListener { _, newValue -> @@ -620,8 +622,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val dataSaverPref = SwitchPreferenceCompat(screen.context).apply { key = MDConstants.getDataSaverPreferenceKey(dexLang) - title = helper.intl.dataSaver - summary = helper.intl.dataSaverSummary + title = helper.intl["data_saver"] + summary = helper.intl["data_saver_summary"] setDefaultValue(false) setOnPreferenceChangeListener { _, newValue -> @@ -635,8 +637,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val standardHttpsPortPref = SwitchPreferenceCompat(screen.context).apply { key = MDConstants.getStandardHttpsPreferenceKey(dexLang) - title = helper.intl.standardHttpsPort - summary = helper.intl.standardHttpsPortSummary + title = helper.intl["standard_https_port"] + summary = helper.intl["standard_https_port_summary"] setDefaultValue(false) setOnPreferenceChangeListener { _, newValue -> @@ -650,13 +652,13 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val contentRatingPref = MultiSelectListPreference(screen.context).apply { key = MDConstants.getContentRatingPrefKey(dexLang) - title = helper.intl.standardContentRating - summary = helper.intl.standardContentRatingSummary + title = helper.intl["standard_content_rating"] + summary = helper.intl["standard_content_rating_summary"] entries = arrayOf( - helper.intl.contentRatingSafe, - helper.intl.contentRatingSuggestive, - helper.intl.contentRatingErotica, - helper.intl.contentRatingPornographic, + helper.intl["content_rating_safe"], + helper.intl["content_rating_suggestive"], + helper.intl["content_rating_erotica"], + helper.intl["content_rating_pornographic"], ) entryValues = arrayOf( MDConstants.contentRatingPrefValSafe, @@ -677,8 +679,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val originalLanguagePref = MultiSelectListPreference(screen.context).apply { key = MDConstants.getOriginalLanguagePrefKey(dexLang) - title = helper.intl.filterOriginalLanguages - summary = helper.intl.filterOriginalLanguagesSummary + title = helper.intl["filter_original_languages"] + summary = helper.intl["filter_original_languages_summary"] entries = arrayOf( helper.intl.languageDisplayName(MangaDexIntl.JAPANESE), helper.intl.languageDisplayName(MangaDexIntl.CHINESE), @@ -702,8 +704,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val blockedGroupsPref = EditTextPreference(screen.context).apply { key = MDConstants.getBlockedGroupsPrefKey(dexLang) - title = helper.intl.blockGroupByUuid - summary = helper.intl.blockGroupByUuidSummary + title = helper.intl["block_group_by_uuid"] + summary = helper.intl["block_group_by_uuid_summary"] setOnBindEditTextListener(helper::setupEditTextUuidValidator) @@ -716,8 +718,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val blockedUploaderPref = EditTextPreference(screen.context).apply { key = MDConstants.getBlockedUploaderPrefKey(dexLang) - title = helper.intl.blockUploaderByUuid - summary = helper.intl.blockUploaderByUuidSummary + title = helper.intl["block_uploader_by_uuid"] + summary = helper.intl["block_uploader_by_uuid_summary"] setOnBindEditTextListener(helper::setupEditTextUuidValidator) @@ -730,8 +732,8 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St val altTitlesInDescPref = SwitchPreferenceCompat(screen.context).apply { key = MDConstants.getAltTitlesInDescPrefKey(dexLang) - title = helper.intl.altTitlesInDesc - summary = helper.intl.altTitlesInDescSummary + title = helper.intl["alternative_titles_in_description"] + summary = helper.intl["alternative_titles_in_description_summary"] setDefaultValue(false) setOnPreferenceChangeListener { _, newValue -> @@ -757,14 +759,17 @@ abstract class MangaDex(final override val lang: String, private val dexLang: St override fun getFilterList(): FilterList = helper.mdFilters.getMDFilterList(preferences, dexLang, helper.intl) - private fun HttpUrl.Builder.addQueryParameter(name: String, value: Set<String>?): HttpUrl.Builder { - return apply { value?.forEach { addQueryParameter(name, it) } } + private fun HttpUrl.Builder.addQueryParameter(name: String, value: Set<String>?) = apply { + value?.forEach { addQueryParameter(name, it) } } private inline fun <reified T> Response.parseAs(): T = use { helper.json.decodeFromString(body.string()) } + private inline fun <reified T> List<*>.firstInstanceOrNull(): T? = + firstOrNull { it is T } as? T? + private val SharedPreferences.contentRating get() = getStringSet( MDConstants.getContentRatingPrefKey(dexLang), diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFactory.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFactory.kt index cd5ab1a458..10a39bc6ca 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFactory.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFactory.kt @@ -6,19 +6,25 @@ import eu.kanade.tachiyomi.source.SourceFactory class MangaDexFactory : SourceFactory { override fun createSources(): List<Source> = listOf( MangaDexEnglish(), + MangaDexAlbanian(), MangaDexArabic(), + MangaDexAzerbaijani(), MangaDexBengali(), MangaDexBulgarian(), MangaDexBurmese(), MangaDexCatalan(), MangaDexChineseSimplified(), MangaDexChineseTraditional(), + MangaDexCroatian(), MangaDexCzech(), MangaDexDanish(), MangaDexDutch(), + MangaDexEsperanto(), + MangaDexEstonian(), MangaDexFilipino(), MangaDexFinnish(), MangaDexFrench(), + MangaDexGeorgian(), MangaDexGerman(), MangaDexGreek(), MangaDexHebrew(), @@ -41,11 +47,13 @@ class MangaDexFactory : SourceFactory { MangaDexPortuguesePortugal(), MangaDexRomanian(), MangaDexRussian(), - MangaDexSerboCroatian(), + MangaDexSerbian(), + MangaDexSlovak(), MangaDexSpanishLatinAmerica(), MangaDexSpanishSpain(), MangaDexSwedish(), MangaDexTamil(), + MangaDexTelugu(), MangaDexThai(), MangaDexTurkish(), MangaDexUkrainian(), @@ -53,48 +61,56 @@ class MangaDexFactory : SourceFactory { ) } -class MangaDexArabic : MangaDex("ar", "ar") -class MangaDexBengali : MangaDex("bn", "bn") -class MangaDexBulgarian : MangaDex("bg", "bg") -class MangaDexBurmese : MangaDex("my", "my") -class MangaDexCatalan : MangaDex("ca", "ca") +class MangaDexAlbanian : MangaDex("sq") +class MangaDexArabic : MangaDex("ar") +class MangaDexAzerbaijani : MangaDex("az") +class MangaDexBengali : MangaDex("bn") +class MangaDexBulgarian : MangaDex("bg") +class MangaDexBurmese : MangaDex("my") +class MangaDexCatalan : MangaDex("ca") class MangaDexChineseSimplified : MangaDex("zh-Hans", "zh") class MangaDexChineseTraditional : MangaDex("zh-Hant", "zh-hk") -class MangaDexCzech : MangaDex("cs", "cs") -class MangaDexDanish : MangaDex("da", "da") -class MangaDexDutch : MangaDex("nl", "nl") -class MangaDexEnglish : MangaDex("en", "en") +class MangaDexCroatian : MangaDex("hr") +class MangaDexCzech : MangaDex("cs") +class MangaDexDanish : MangaDex("da") +class MangaDexDutch : MangaDex("nl") +class MangaDexEnglish : MangaDex("en") +class MangaDexEsperanto : MangaDex("eo") +class MangaDexEstonian : MangaDex("et") class MangaDexFilipino : MangaDex("fil", "tl") -class MangaDexFinnish : MangaDex("fi", "fi") -class MangaDexFrench : MangaDex("fr", "fr") -class MangaDexGerman : MangaDex("de", "de") -class MangaDexGreek : MangaDex("el", "el") -class MangaDexHebrew : MangaDex("he", "he") -class MangaDexHindi : MangaDex("hi", "hi") -class MangaDexHungarian : MangaDex("hu", "hu") -class MangaDexIndonesian : MangaDex("id", "id") -class MangaDexItalian : MangaDex("it", "it") -class MangaDexJapanese : MangaDex("ja", "ja") -class MangaDexKazakh : MangaDex("kk", "kk") -class MangaDexKorean : MangaDex("ko", "ko") -class MangaDexLatin : MangaDex("la", "la") -class MangaDexLithuanian : MangaDex("lt", "lt") -class MangaDexMalay : MangaDex("ms", "ms") -class MangaDexMongolian : MangaDex("mn", "mn") -class MangaDexNepali : MangaDex("ne", "ne") -class MangaDexNorwegian : MangaDex("no", "no") -class MangaDexPersian : MangaDex("fa", "fa") -class MangaDexPolish : MangaDex("pl", "pl") +class MangaDexFinnish : MangaDex("fi") +class MangaDexFrench : MangaDex("fr") +class MangaDexGeorgian : MangaDex("ka") +class MangaDexGerman : MangaDex("de") +class MangaDexGreek : MangaDex("el") +class MangaDexHebrew : MangaDex("he") +class MangaDexHindi : MangaDex("hi") +class MangaDexHungarian : MangaDex("hu") +class MangaDexIndonesian : MangaDex("id") +class MangaDexItalian : MangaDex("it") +class MangaDexJapanese : MangaDex("ja") +class MangaDexKazakh : MangaDex("kk") +class MangaDexKorean : MangaDex("ko") +class MangaDexLatin : MangaDex("la") +class MangaDexLithuanian : MangaDex("lt") +class MangaDexMalay : MangaDex("ms") +class MangaDexMongolian : MangaDex("mn") +class MangaDexNepali : MangaDex("ne") +class MangaDexNorwegian : MangaDex("no") +class MangaDexPersian : MangaDex("fa") +class MangaDexPolish : MangaDex("pl") class MangaDexPortugueseBrazil : MangaDex("pt-BR", "pt-br") -class MangaDexPortuguesePortugal : MangaDex("pt", "pt") -class MangaDexRomanian : MangaDex("ro", "ro") -class MangaDexRussian : MangaDex("ru", "ru") -class MangaDexSerboCroatian : MangaDex("sh", "sh") +class MangaDexPortuguesePortugal : MangaDex("pt") +class MangaDexRomanian : MangaDex("ro") +class MangaDexRussian : MangaDex("ru") +class MangaDexSerbian : MangaDex("sr") +class MangaDexSlovak : MangaDex("sk") class MangaDexSpanishLatinAmerica : MangaDex("es-419", "es-la") -class MangaDexSpanishSpain : MangaDex("es", "es") -class MangaDexSwedish : MangaDex("sv", "sv") -class MangaDexTamil : MangaDex("ta", "ta") -class MangaDexThai : MangaDex("th", "th") -class MangaDexTurkish : MangaDex("tr", "tr") -class MangaDexUkrainian : MangaDex("uk", "uk") -class MangaDexVietnamese : MangaDex("vi", "vi") +class MangaDexSpanishSpain : MangaDex("es") +class MangaDexSwedish : MangaDex("sv") +class MangaDexTamil : MangaDex("ta") +class MangaDexTelugu : MangaDex("te") +class MangaDexThai : MangaDex("th") +class MangaDexTurkish : MangaDex("tr") +class MangaDexUkrainian : MangaDex("uk") +class MangaDexVietnamese : MangaDex("vi") diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFilters.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFilters.kt index d6e8f49590..05b452a01b 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFilters.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexFilters.kt @@ -4,6 +4,7 @@ import android.content.SharedPreferences import eu.kanade.tachiyomi.extension.all.mangadex.dto.ContentRatingDto import eu.kanade.tachiyomi.extension.all.mangadex.dto.PublicationDemographicDto import eu.kanade.tachiyomi.extension.all.mangadex.dto.StatusDto +import eu.kanade.tachiyomi.lib.i18n.Intl import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import okhttp3.HttpUrl @@ -13,7 +14,7 @@ class MangaDexFilters { internal fun getMDFilterList( preferences: SharedPreferences, dexLang: String, - intl: MangaDexIntl, + intl: Intl, ): FilterList = FilterList( HasAvailableChaptersFilter(intl), OriginalLanguageList(intl, getOriginalLanguage(preferences, dexLang, intl)), @@ -22,18 +23,18 @@ class MangaDexFilters { StatusList(intl, getStatus(intl)), SortFilter(intl, getSortables(intl)), TagsFilter(intl, getTagFilters(intl)), - TagList(intl.content, getContents(intl)), - TagList(intl.format, getFormats(intl)), - TagList(intl.genre, getGenres(intl)), - TagList(intl.theme, getThemes(intl)), + TagList(intl["content"], getContents(intl)), + TagList(intl["format"], getFormats(intl)), + TagList(intl["genre"], getGenres(intl)), + TagList(intl["theme"], getThemes(intl)), ) private interface UrlQueryFilter { fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) } - private class HasAvailableChaptersFilter(intl: MangaDexIntl) : - Filter.CheckBox(intl.hasAvailableChapters), + private class HasAvailableChaptersFilter(intl: Intl) : + Filter.CheckBox(intl["has_available_chapters"]), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { @@ -44,14 +45,18 @@ class MangaDexFilters { } } - private class OriginalLanguage(name: String, val isoCode: String) : Filter.CheckBox(name) - private class OriginalLanguageList(intl: MangaDexIntl, originalLanguage: List<OriginalLanguage>) : - Filter.Group<OriginalLanguage>(intl.originalLanguage, originalLanguage), + private class OriginalLanguage( + name: String, + val isoCode: String, + state: Boolean = false, + ) : Filter.CheckBox(name, state) + private class OriginalLanguageList(intl: Intl, originalLanguage: List<OriginalLanguage>) : + Filter.Group<OriginalLanguage>(intl["original_language"], originalLanguage), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { - state.forEach { lang -> - if (lang.state) { + state.filter(OriginalLanguage::state) + .forEach { lang -> // dex has zh and zh-hk for chinese manhua if (lang.isoCode == MDConstants.originalLanguagePrefValChinese) { url.addQueryParameter( @@ -62,14 +67,13 @@ class MangaDexFilters { url.addQueryParameter("originalLanguage[]", lang.isoCode) } - } } } private fun getOriginalLanguage( preferences: SharedPreferences, dexLang: String, - intl: MangaDexIntl, + intl: Intl, ): List<OriginalLanguage> { val originalLanguages = preferences.getStringSet( MDConstants.getOriginalLanguagePrefKey(dexLang), @@ -77,33 +81,48 @@ class MangaDexFilters { )!! return listOf( - OriginalLanguage(intl.originalLanguageFilterJapanese, MDConstants.originalLanguagePrefValJapanese) - .apply { state = MDConstants.originalLanguagePrefValJapanese in originalLanguages }, - OriginalLanguage(intl.originalLanguageFilterChinese, MDConstants.originalLanguagePrefValChinese) - .apply { state = MDConstants.originalLanguagePrefValChinese in originalLanguages }, - OriginalLanguage(intl.originalLanguageFilterKorean, MDConstants.originalLanguagePrefValKorean) - .apply { state = MDConstants.originalLanguagePrefValKorean in originalLanguages }, + OriginalLanguage( + name = intl.format( + "original_language_filter_japanese", + intl.languageDisplayName(MangaDexIntl.JAPANESE), + ), + isoCode = MDConstants.originalLanguagePrefValJapanese, + state = MDConstants.originalLanguagePrefValJapanese in originalLanguages, + ), + OriginalLanguage( + name = intl.format( + "original_language_filter_chinese", + intl.languageDisplayName(MangaDexIntl.CHINESE), + ), + isoCode = MDConstants.originalLanguagePrefValChinese, + state = MDConstants.originalLanguagePrefValChinese in originalLanguages, + ), + OriginalLanguage( + name = intl.format( + "original_language_filter_korean", + intl.languageDisplayName(MangaDexIntl.KOREAN), + ), + isoCode = MDConstants.originalLanguagePrefValKorean, + state = MDConstants.originalLanguagePrefValKorean in originalLanguages, + ), ) } private class ContentRating(name: String, val value: String) : Filter.CheckBox(name) - private class ContentRatingList(intl: MangaDexIntl, contentRating: List<ContentRating>) : - Filter.Group<ContentRating>(intl.contentRating, contentRating), + private class ContentRatingList(intl: Intl, contentRating: List<ContentRating>) : + Filter.Group<ContentRating>(intl["content_rating"], contentRating), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { - state.forEach { rating -> - if (rating.state) { - url.addQueryParameter("contentRating[]", rating.value) - } - } + state.filter(ContentRating::state) + .forEach { url.addQueryParameter("contentRating[]", it.value) } } } private fun getContentRating( preferences: SharedPreferences, dexLang: String, - intl: MangaDexIntl, + intl: Intl, ): List<ContentRating> { val contentRatings = preferences.getStringSet( MDConstants.getContentRatingPrefKey(dexLang), @@ -111,82 +130,76 @@ class MangaDexFilters { ) return listOf( - ContentRating(intl.contentRatingSafe, ContentRatingDto.SAFE.value).apply { + ContentRating(intl["content_rating_safe"], ContentRatingDto.SAFE.value).apply { state = contentRatings?.contains(MDConstants.contentRatingPrefValSafe) ?: true }, - ContentRating(intl.contentRatingSuggestive, ContentRatingDto.SUGGESTIVE.value).apply { + ContentRating(intl["content_rating_suggestive"], ContentRatingDto.SUGGESTIVE.value).apply { state = contentRatings?.contains(MDConstants.contentRatingPrefValSuggestive) ?: true }, - ContentRating(intl.contentRatingErotica, ContentRatingDto.EROTICA.value).apply { + ContentRating(intl["content_rating_erotica"], ContentRatingDto.EROTICA.value).apply { state = contentRatings?.contains(MDConstants.contentRatingPrefValErotica) ?: false }, - ContentRating(intl.contentRatingPornographic, ContentRatingDto.PORNOGRAPHIC.value).apply { + ContentRating(intl["content_rating_pornographic"], ContentRatingDto.PORNOGRAPHIC.value).apply { state = contentRatings?.contains(MDConstants.contentRatingPrefValPornographic) ?: false }, ) } private class Demographic(name: String, val value: String) : Filter.CheckBox(name) - private class DemographicList(intl: MangaDexIntl, demographics: List<Demographic>) : - Filter.Group<Demographic>(intl.publicationDemographic, demographics), + private class DemographicList(intl: Intl, demographics: List<Demographic>) : + Filter.Group<Demographic>(intl["publication_demographic"], demographics), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { - state.forEach { demographic -> - if (demographic.state) { - url.addQueryParameter("publicationDemographic[]", demographic.value) - } - } + state.filter(Demographic::state) + .forEach { url.addQueryParameter("publicationDemographic[]", it.value) } } } - private fun getDemographics(intl: MangaDexIntl) = listOf( - Demographic(intl.publicationDemographicNone, PublicationDemographicDto.NONE.value), - Demographic(intl.publicationDemographicShounen, PublicationDemographicDto.SHOUNEN.value), - Demographic(intl.publicationDemographicShoujo, PublicationDemographicDto.SHOUJO.value), - Demographic(intl.publicationDemographicSeinen, PublicationDemographicDto.SEINEN.value), - Demographic(intl.publicationDemographicJosei, PublicationDemographicDto.JOSEI.value), + private fun getDemographics(intl: Intl) = listOf( + Demographic(intl["publication_demographic_none"], PublicationDemographicDto.NONE.value), + Demographic(intl["publication_demographic_shounen"], PublicationDemographicDto.SHOUNEN.value), + Demographic(intl["publication_demographic_shoujo"], PublicationDemographicDto.SHOUJO.value), + Demographic(intl["publication_demographic_seinen"], PublicationDemographicDto.SEINEN.value), + Demographic(intl["publication_demographic_josei"], PublicationDemographicDto.JOSEI.value), ) private class Status(name: String, val value: String) : Filter.CheckBox(name) - private class StatusList(intl: MangaDexIntl, status: List<Status>) : - Filter.Group<Status>(intl.status, status), + private class StatusList(intl: Intl, status: List<Status>) : + Filter.Group<Status>(intl["status"], status), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { - state.forEach { status -> - if (status.state) { - url.addQueryParameter("status[]", status.value) - } - } + state.filter(Status::state) + .forEach { url.addQueryParameter("status[]", it.value) } } } - private fun getStatus(intl: MangaDexIntl) = listOf( - Status(intl.statusOngoing, StatusDto.ONGOING.value), - Status(intl.statusCompleted, StatusDto.COMPLETED.value), - Status(intl.statusHiatus, StatusDto.HIATUS.value), - Status(intl.statusCancelled, StatusDto.CANCELLED.value), + private fun getStatus(intl: Intl) = listOf( + Status(intl["status_ongoing"], StatusDto.ONGOING.value), + Status(intl["status_completed"], StatusDto.COMPLETED.value), + Status(intl["status_hiatus"], StatusDto.HIATUS.value), + Status(intl["status_cancelled"], StatusDto.CANCELLED.value), ) data class Sortable(val title: String, val value: String) { override fun toString(): String = title } - private fun getSortables(intl: MangaDexIntl) = arrayOf( - Sortable(intl.sortAlphabetic, "title"), - Sortable(intl.sortChapterUploadedAt, "latestUploadedChapter"), - Sortable(intl.sortNumberOfFollows, "followedCount"), - Sortable(intl.sortContentCreatedAt, "createdAt"), - Sortable(intl.sortContentInfoUpdatedAt, "updatedAt"), - Sortable(intl.sortRelevance, "relevance"), - Sortable(intl.sortYear, "year"), - Sortable(intl.sortRating, "rating"), + private fun getSortables(intl: Intl) = arrayOf( + Sortable(intl["sort_alphabetic"], "title"), + Sortable(intl["sort_chapter_uploaded_at"], "latestUploadedChapter"), + Sortable(intl["sort_number_of_follows"], "followedCount"), + Sortable(intl["sort_content_created_at"], "createdAt"), + Sortable(intl["sort_content_info_updated_at"], "updatedAt"), + Sortable(intl["sort_relevance"], "relevance"), + Sortable(intl["sort_year"], "year"), + Sortable(intl["sort_rating"], "rating"), ) - class SortFilter(intl: MangaDexIntl, private val sortables: Array<Sortable>) : + class SortFilter(intl: Intl, private val sortables: Array<Sortable>) : Filter.Sort( - intl.sort, + intl["sort"], sortables.map(Sortable::title).toTypedArray(), Selection(5, false), ), @@ -195,10 +208,7 @@ class MangaDexFilters { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { if (state != null) { val query = sortables[state!!.index].value - val value = when (state!!.ascending) { - true -> "asc" - false -> "desc" - } + val value = if (state!!.ascending) "asc" else "desc" url.addQueryParameter("order[$query]", value) } @@ -222,112 +232,112 @@ class MangaDexFilters { } } - private fun getContents(intl: MangaDexIntl): List<Tag> { + private fun getContents(intl: Intl): List<Tag> { val tags = listOf( - Tag("b29d6a3d-1569-4e7a-8caf-7557bc92cd5d", intl.contentGore), - Tag("97893a4c-12af-4dac-b6be-0dffb353568e", intl.contentSexualViolence), + Tag("b29d6a3d-1569-4e7a-8caf-7557bc92cd5d", intl["content_gore"]), + Tag("97893a4c-12af-4dac-b6be-0dffb353568e", intl["content_sexual_violence"]), ) return tags.sortIfTranslated(intl) } - private fun getFormats(intl: MangaDexIntl): List<Tag> { + private fun getFormats(intl: Intl): List<Tag> { val tags = listOf( - Tag("b11fda93-8f1d-4bef-b2ed-8803d3733170", intl.formatFourKoma), - Tag("f4122d1c-3b44-44d0-9936-ff7502c39ad3", intl.formatAdaptation), - Tag("51d83883-4103-437c-b4b1-731cb73d786c", intl.formatAnthology), - Tag("0a39b5a1-b235-4886-a747-1d05d216532d", intl.formatAwardWinning), - Tag("b13b2a48-c720-44a9-9c77-39c9979373fb", intl.formatDoujinshi), - Tag("7b2ce280-79ef-4c09-9b58-12b7c23a9b78", intl.formatFanColored), - Tag("f5ba408b-0e7a-484d-8d49-4e9125ac96de", intl.formatFullColor), - Tag("3e2b8dae-350e-4ab8-a8ce-016e844b9f0d", intl.formatLongStrip), - Tag("320831a8-4026-470b-94f6-8353740e6f04", intl.formatOfficialColored), - Tag("0234a31e-a729-4e28-9d6a-3f87c4966b9e", intl.formatOneshot), - Tag("891cf039-b895-47f0-9229-bef4c96eccd4", intl.formatUserCreated), - Tag("e197df38-d0e7-43b5-9b09-2842d0c326dd", intl.formatWebComic), + Tag("b11fda93-8f1d-4bef-b2ed-8803d3733170", intl["format_yonkoma"]), + Tag("f4122d1c-3b44-44d0-9936-ff7502c39ad3", intl["format_adaptation"]), + Tag("51d83883-4103-437c-b4b1-731cb73d786c", intl["format_anthology"]), + Tag("0a39b5a1-b235-4886-a747-1d05d216532d", intl["format_award_winning"]), + Tag("b13b2a48-c720-44a9-9c77-39c9979373fb", intl["format_doujinshi"]), + Tag("7b2ce280-79ef-4c09-9b58-12b7c23a9b78", intl["format_fan_colored"]), + Tag("f5ba408b-0e7a-484d-8d49-4e9125ac96de", intl["format_full_color"]), + Tag("3e2b8dae-350e-4ab8-a8ce-016e844b9f0d", intl["format_long_strip"]), + Tag("320831a8-4026-470b-94f6-8353740e6f04", intl["format_official_colored"]), + Tag("0234a31e-a729-4e28-9d6a-3f87c4966b9e", intl["format_oneshot"]), + Tag("891cf039-b895-47f0-9229-bef4c96eccd4", intl["format_user_created"]), + Tag("e197df38-d0e7-43b5-9b09-2842d0c326dd", intl["format_web_comic"]), ) return tags.sortIfTranslated(intl) } - private fun getGenres(intl: MangaDexIntl): List<Tag> { + private fun getGenres(intl: Intl): List<Tag> { val tags = listOf( - Tag("391b0423-d847-456f-aff0-8b0cfc03066b", intl.genreAction), - Tag("87cc87cd-a395-47af-b27a-93258283bbc6", intl.genreAdventure), - Tag("5920b825-4181-4a17-beeb-9918b0ff7a30", intl.genreBoysLove), - Tag("4d32cc48-9f00-4cca-9b5a-a839f0764984", intl.genreComedy), - Tag("5ca48985-9a9d-4bd8-be29-80dc0303db72", intl.genreCrime), - Tag("b9af3a63-f058-46de-a9a0-e0c13906197a", intl.genreDrama), - Tag("cdc58593-87dd-415e-bbc0-2ec27bf404cc", intl.genreFantasy), - Tag("a3c67850-4684-404e-9b7f-c69850ee5da6", intl.genreGirlsLove), - Tag("33771934-028e-4cb3-8744-691e866a923e", intl.genreHistorical), - Tag("cdad7e68-1419-41dd-bdce-27753074a640", intl.genreHorror), - Tag("ace04997-f6bd-436e-b261-779182193d3d", intl.genreIsekai), - Tag("81c836c9-914a-4eca-981a-560dad663e73", intl.genreMagicalGirls), - Tag("50880a9d-5440-4732-9afb-8f457127e836", intl.genreMecha), - Tag("c8cbe35b-1b2b-4a3f-9c37-db84c4514856", intl.genreMedical), - Tag("ee968100-4191-4968-93d3-f82d72be7e46", intl.genreMystery), - Tag("b1e97889-25b4-4258-b28b-cd7f4d28ea9b", intl.genrePhilosophical), - Tag("423e2eae-a7a2-4a8b-ac03-a8351462d71d", intl.genreRomance), - Tag("256c8bd9-4904-4360-bf4f-508a76d67183", intl.genreSciFi), - Tag("e5301a23-ebd9-49dd-a0cb-2add944c7fe9", intl.genreSliceOfLife), - Tag("69964a64-2f90-4d33-beeb-f3ed2875eb4c", intl.genreSports), - Tag("7064a261-a137-4d3a-8848-2d385de3a99c", intl.genreSuperhero), - Tag("07251805-a27e-4d59-b488-f0bfbec15168", intl.genreThriller), - Tag("f8f62932-27da-4fe4-8ee1-6779a8c5edba", intl.genreTragedy), - Tag("acc803a4-c95a-4c22-86fc-eb6b582d82a2", intl.genreWuxia), + Tag("391b0423-d847-456f-aff0-8b0cfc03066b", intl["genre_action"]), + Tag("87cc87cd-a395-47af-b27a-93258283bbc6", intl["genre_adventure"]), + Tag("5920b825-4181-4a17-beeb-9918b0ff7a30", intl["genre_boys_love"]), + Tag("4d32cc48-9f00-4cca-9b5a-a839f0764984", intl["genre_comedy"]), + Tag("5ca48985-9a9d-4bd8-be29-80dc0303db72", intl["genre_crime"]), + Tag("b9af3a63-f058-46de-a9a0-e0c13906197a", intl["genre_drama"]), + Tag("cdc58593-87dd-415e-bbc0-2ec27bf404cc", intl["genre_fantasy"]), + Tag("a3c67850-4684-404e-9b7f-c69850ee5da6", intl["genre_girls_love"]), + Tag("33771934-028e-4cb3-8744-691e866a923e", intl["genre_historical"]), + Tag("cdad7e68-1419-41dd-bdce-27753074a640", intl["genre_horror"]), + Tag("ace04997-f6bd-436e-b261-779182193d3d", intl["genre_isekai"]), + Tag("81c836c9-914a-4eca-981a-560dad663e73", intl["genre_magical_girls"]), + Tag("50880a9d-5440-4732-9afb-8f457127e836", intl["genre_mecha"]), + Tag("c8cbe35b-1b2b-4a3f-9c37-db84c4514856", intl["genre_medical"]), + Tag("ee968100-4191-4968-93d3-f82d72be7e46", intl["genre_mystery"]), + Tag("b1e97889-25b4-4258-b28b-cd7f4d28ea9b", intl["genre_philosophical"]), + Tag("423e2eae-a7a2-4a8b-ac03-a8351462d71d", intl["genre_romance"]), + Tag("256c8bd9-4904-4360-bf4f-508a76d67183", intl["genre_sci_fi"]), + Tag("e5301a23-ebd9-49dd-a0cb-2add944c7fe9", intl["genre_slice_of_life"]), + Tag("69964a64-2f90-4d33-beeb-f3ed2875eb4c", intl["genre_sports"]), + Tag("7064a261-a137-4d3a-8848-2d385de3a99c", intl["genre_superhero"]), + Tag("07251805-a27e-4d59-b488-f0bfbec15168", intl["genre_thriller"]), + Tag("f8f62932-27da-4fe4-8ee1-6779a8c5edba", intl["genre_tragedy"]), + Tag("acc803a4-c95a-4c22-86fc-eb6b582d82a2", intl["genre_wuxia"]), ) return tags.sortIfTranslated(intl) } - private fun getThemes(intl: MangaDexIntl): List<Tag> { + private fun getThemes(intl: Intl): List<Tag> { val tags = listOf( - Tag("e64f6742-c834-471d-8d72-dd51fc02b835", intl.themeAliens), - Tag("3de8c75d-8ee3-48ff-98ee-e20a65c86451", intl.themeAnimals), - Tag("ea2bc92d-1c26-4930-9b7c-d5c0dc1b6869", intl.themeCooking), - Tag("9ab53f92-3eed-4e9b-903a-917c86035ee3", intl.themeCrossdressing), - Tag("da2d50ca-3018-4cc0-ac7a-6b7d472a29ea", intl.themeDelinquents), - Tag("39730448-9a5f-48a2-85b0-a70db87b1233", intl.themeDemons), - Tag("2bd2e8d0-f146-434a-9b51-fc9ff2c5fe6a", intl.themeGenderSwap), - Tag("3bb26d85-09d5-4d2e-880c-c34b974339e9", intl.themeGhosts), - Tag("fad12b5e-68ba-460e-b933-9ae8318f5b65", intl.themeGyaru), - Tag("aafb99c1-7f60-43fa-b75f-fc9502ce29c7", intl.themeHarem), - Tag("5bd0e105-4481-44ca-b6e7-7544da56b1a3", intl.themeIncest), - Tag("2d1f5d56-a1e5-4d0d-a961-2193588b08ec", intl.themeLoli), - Tag("85daba54-a71c-4554-8a28-9901a8b0afad", intl.themeMafia), - Tag("a1f53773-c69a-4ce5-8cab-fffcd90b1565", intl.themeMagic), - Tag("799c202e-7daa-44eb-9cf7-8a3c0441531e", intl.themeMartialArts), - Tag("ac72833b-c4e9-4878-b9db-6c8a4a99444a", intl.themeMilitary), - Tag("dd1f77c5-dea9-4e2b-97ae-224af09caf99", intl.themeMonsterGirls), - Tag("36fd93ea-e8b8-445e-b836-358f02b3d33d", intl.themeMonsters), - Tag("f42fbf9e-188a-447b-9fdc-f19dc1e4d685", intl.themeMusic), - Tag("489dd859-9b61-4c37-af75-5b18e88daafc", intl.themeNinja), - Tag("92d6d951-ca5e-429c-ac78-451071cbf064", intl.themeOfficeWorkers), - Tag("df33b754-73a3-4c54-80e6-1a74a8058539", intl.themePolice), - Tag("9467335a-1b83-4497-9231-765337a00b96", intl.themePostApocalyptic), - Tag("3b60b75c-a2d7-4860-ab56-05f391bb889c", intl.themePsychological), - Tag("0bc90acb-ccc1-44ca-a34a-b9f3a73259d0", intl.themeReincarnation), - Tag("65761a2a-415e-47f3-bef2-a9dababba7a6", intl.themeReverseHarem), - Tag("81183756-1453-4c81-aa9e-f6e1b63be016", intl.themeSamurai), - Tag("caaa44eb-cd40-4177-b930-79d3ef2afe87", intl.themeSchoolLife), - Tag("ddefd648-5140-4e5f-ba18-4eca4071d19b", intl.themeShota), - Tag("eabc5b4c-6aff-42f3-b657-3e90cbd00b75", intl.themeSupernatural), - Tag("5fff9cde-849c-4d78-aab0-0d52b2ee1d25", intl.themeSurvival), - Tag("292e862b-2d17-4062-90a2-0356caa4ae27", intl.themeTimeTravel), - Tag("31932a7e-5b8e-49a6-9f12-2afa39dc544c", intl.themeTraditionalGames), - Tag("d7d1730f-6eb0-4ba6-9437-602cac38664c", intl.themeVampires), - Tag("9438db5a-7e2a-4ac0-b39e-e0d95a34b8a8", intl.themeVideoGames), - Tag("d14322ac-4d6f-4e9b-afd9-629d5f4d8a41", intl.themeVillainess), - Tag("8c86611e-fab7-4986-9dec-d1a2f44acdd5", intl.themeVirtualReality), - Tag("631ef465-9aba-4afb-b0fc-ea10efe274a8", intl.themeZombies), + Tag("e64f6742-c834-471d-8d72-dd51fc02b835", intl["theme_aliens"]), + Tag("3de8c75d-8ee3-48ff-98ee-e20a65c86451", intl["theme_animals"]), + Tag("ea2bc92d-1c26-4930-9b7c-d5c0dc1b6869", intl["theme_cooking"]), + Tag("9ab53f92-3eed-4e9b-903a-917c86035ee3", intl["theme_crossdressing"]), + Tag("da2d50ca-3018-4cc0-ac7a-6b7d472a29ea", intl["theme_delinquents"]), + Tag("39730448-9a5f-48a2-85b0-a70db87b1233", intl["theme_demons"]), + Tag("2bd2e8d0-f146-434a-9b51-fc9ff2c5fe6a", intl["theme_gender_swap"]), + Tag("3bb26d85-09d5-4d2e-880c-c34b974339e9", intl["theme_ghosts"]), + Tag("fad12b5e-68ba-460e-b933-9ae8318f5b65", intl["theme_gyaru"]), + Tag("aafb99c1-7f60-43fa-b75f-fc9502ce29c7", intl["theme_harem"]), + Tag("5bd0e105-4481-44ca-b6e7-7544da56b1a3", intl["theme_incest"]), + Tag("2d1f5d56-a1e5-4d0d-a961-2193588b08ec", intl["theme_loli"]), + Tag("85daba54-a71c-4554-8a28-9901a8b0afad", intl["theme_mafia"]), + Tag("a1f53773-c69a-4ce5-8cab-fffcd90b1565", intl["theme_magic"]), + Tag("799c202e-7daa-44eb-9cf7-8a3c0441531e", intl["theme_martial_arts"]), + Tag("ac72833b-c4e9-4878-b9db-6c8a4a99444a", intl["theme_military"]), + Tag("dd1f77c5-dea9-4e2b-97ae-224af09caf99", intl["theme_monster_girls"]), + Tag("36fd93ea-e8b8-445e-b836-358f02b3d33d", intl["theme_monsters"]), + Tag("f42fbf9e-188a-447b-9fdc-f19dc1e4d685", intl["theme_music"]), + Tag("489dd859-9b61-4c37-af75-5b18e88daafc", intl["theme_ninja"]), + Tag("92d6d951-ca5e-429c-ac78-451071cbf064", intl["theme_office_workers"]), + Tag("df33b754-73a3-4c54-80e6-1a74a8058539", intl["theme_police"]), + Tag("9467335a-1b83-4497-9231-765337a00b96", intl["theme_post_apocalyptic"]), + Tag("3b60b75c-a2d7-4860-ab56-05f391bb889c", intl["theme_psychological"]), + Tag("0bc90acb-ccc1-44ca-a34a-b9f3a73259d0", intl["theme_reincarnation"]), + Tag("65761a2a-415e-47f3-bef2-a9dababba7a6", intl["theme_reverse_harem"]), + Tag("81183756-1453-4c81-aa9e-f6e1b63be016", intl["theme_samurai"]), + Tag("caaa44eb-cd40-4177-b930-79d3ef2afe87", intl["theme_school_life"]), + Tag("ddefd648-5140-4e5f-ba18-4eca4071d19b", intl["theme_shota"]), + Tag("eabc5b4c-6aff-42f3-b657-3e90cbd00b75", intl["theme_supernatural"]), + Tag("5fff9cde-849c-4d78-aab0-0d52b2ee1d25", intl["theme_survival"]), + Tag("292e862b-2d17-4062-90a2-0356caa4ae27", intl["theme_time_travel"]), + Tag("31932a7e-5b8e-49a6-9f12-2afa39dc544c", intl["theme_traditional_games"]), + Tag("d7d1730f-6eb0-4ba6-9437-602cac38664c", intl["theme_vampires"]), + Tag("9438db5a-7e2a-4ac0-b39e-e0d95a34b8a8", intl["theme_video_games"]), + Tag("d14322ac-4d6f-4e9b-afd9-629d5f4d8a41", intl["theme_villainess"]), + Tag("8c86611e-fab7-4986-9dec-d1a2f44acdd5", intl["theme_virtual_reality"]), + Tag("631ef465-9aba-4afb-b0fc-ea10efe274a8", intl["theme_zombies"]), ) return tags.sortIfTranslated(intl) } // to get all tags from dex https://api.mangadex.org/manga/tag - internal fun getTags(intl: MangaDexIntl): List<Tag> { + internal fun getTags(intl: Intl): List<Tag> { return getContents(intl) + getFormats(intl) + getGenres(intl) + getThemes(intl) } @@ -335,13 +345,13 @@ class MangaDexFilters { override fun toString(): String = title } - private fun getTagModes(intl: MangaDexIntl) = arrayOf( - TagMode(intl.modeAnd, "AND"), - TagMode(intl.modeOr, "OR"), + private fun getTagModes(intl: Intl) = arrayOf( + TagMode(intl["mode_and"], "AND"), + TagMode(intl["mode_or"], "OR"), ) - private class TagInclusionMode(intl: MangaDexIntl, modes: Array<TagMode>) : - Filter.Select<TagMode>(intl.includedTagsMode, modes, 0), + private class TagInclusionMode(intl: Intl, modes: Array<TagMode>) : + Filter.Select<TagMode>(intl["included_tags_mode"], modes, 0), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { @@ -349,8 +359,8 @@ class MangaDexFilters { } } - private class TagExclusionMode(intl: MangaDexIntl, modes: Array<TagMode>) : - Filter.Select<TagMode>(intl.excludedTagsMode, modes, 1), + private class TagExclusionMode(intl: Intl, modes: Array<TagMode>) : + Filter.Select<TagMode>(intl["excluded_tags_mode"], modes, 1), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { @@ -358,8 +368,8 @@ class MangaDexFilters { } } - private class TagsFilter(intl: MangaDexIntl, innerFilters: FilterList) : - Filter.Group<Filter<*>>(intl.tags, innerFilters), + private class TagsFilter(intl: Intl, innerFilters: FilterList) : + Filter.Group<Filter<*>>(intl["tags_mode"], innerFilters), UrlQueryFilter { override fun addQueryParameter(url: HttpUrl.Builder, dexLang: String) { @@ -368,20 +378,20 @@ class MangaDexFilters { } } - private fun getTagFilters(intl: MangaDexIntl): FilterList = FilterList( + private fun getTagFilters(intl: Intl): FilterList = FilterList( TagInclusionMode(intl, getTagModes(intl)), TagExclusionMode(intl, getTagModes(intl)), ) - internal fun addFiltersToUrl(url: HttpUrl.Builder, filters: FilterList, dexLang: String): String { + internal fun addFiltersToUrl(url: HttpUrl.Builder, filters: FilterList, dexLang: String): HttpUrl { filters.filterIsInstance<UrlQueryFilter>() .forEach { filter -> filter.addQueryParameter(url, dexLang) } - return url.toString() + return url.build() } - private fun List<Tag>.sortIfTranslated(intl: MangaDexIntl): List<Tag> = apply { - if (intl.availableLang == MangaDexIntl.ENGLISH) { + private fun List<Tag>.sortIfTranslated(intl: Intl): List<Tag> = apply { + if (intl.chosenLanguage == MangaDexIntl.ENGLISH) { return this } diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexHelper.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexHelper.kt index 60234e8aea..4ee3a28cd8 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexHelper.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexHelper.kt @@ -29,6 +29,7 @@ import eu.kanade.tachiyomi.extension.all.mangadex.dto.TagDto import eu.kanade.tachiyomi.extension.all.mangadex.dto.UnknownEntity import eu.kanade.tachiyomi.extension.all.mangadex.dto.UserAttributes import eu.kanade.tachiyomi.extension.all.mangadex.dto.UserDto +import eu.kanade.tachiyomi.lib.i18n.Intl import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter @@ -85,7 +86,19 @@ class MangaDexHelper(lang: String) { } } - val intl = MangaDexIntl(lang) + val intl = Intl( + language = lang, + baseLanguage = MangaDexIntl.ENGLISH, + availableLanguages = MangaDexIntl.AVAILABLE_LANGS, + classLoader = this::class.java.classLoader!!, + createMessageFileName = { lang -> + when (lang) { + MangaDexIntl.SPANISH_LATAM -> Intl.createDefaultMessageFileName(MangaDexIntl.SPANISH) + MangaDexIntl.PORTUGUESE -> Intl.createDefaultMessageFileName(MangaDexIntl.BRAZILIAN_PORTUGUESE) + else -> Intl.createDefaultMessageFileName(lang) + } + }, + ) /** * Gets the UUID from the url @@ -199,20 +212,18 @@ class MangaDexHelper(lang: String) { * Check the token map to see if the MD@Home host is still valid. */ fun getValidImageUrlForPage(page: Page, headers: Headers, client: OkHttpClient): Request { - val data = page.url.split(",") + val (host, tokenRequestUrl, time) = page.url.split(",") val mdAtHomeServerUrl = - when (Date().time - data[2].toLong() > MDConstants.mdAtHomeTokenLifespan) { - false -> data[0] + when (Date().time - time.toLong() > MDConstants.mdAtHomeTokenLifespan) { + false -> host true -> { - val tokenRequestUrl = data[1] val tokenLifespan = Date().time - (tokenTracker[tokenRequestUrl] ?: 0) - val cacheControl = - if (tokenLifespan > MDConstants.mdAtHomeTokenLifespan) { - CacheControl.FORCE_NETWORK - } else { - USE_CACHE - } + val cacheControl = if (tokenLifespan > MDConstants.mdAtHomeTokenLifespan) { + CacheControl.FORCE_NETWORK + } else { + USE_CACHE + } getMdAtHomeUrl(tokenRequestUrl, client, headers, cacheControl) } } @@ -265,22 +276,20 @@ class MangaDexHelper(lang: String) { coverFileName: String?, coverSuffix: String?, lang: String, - ): SManga { - return SManga.create().apply { - url = "/manga/${mangaDataDto.id}" - val titleMap = mangaDataDto.attributes!!.title - val dirtyTitle = - titleMap.values.firstOrNull() // use literally anything from title as first resort - ?: mangaDataDto.attributes.altTitles - .find { (it[lang] ?: it["en"]) !== null } - ?.values?.singleOrNull() // find something else from alt titles - title = (dirtyTitle ?: "").removeEntitiesAndMarkdown() - - coverFileName?.let { - thumbnail_url = when (!coverSuffix.isNullOrEmpty()) { - true -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName$coverSuffix" - else -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName" - } + ): SManga = SManga.create().apply { + url = "/manga/${mangaDataDto.id}" + val titleMap = mangaDataDto.attributes!!.title + val dirtyTitle = + titleMap.values.firstOrNull() // use literally anything from title as first resort + ?: mangaDataDto.attributes.altTitles + .find { (it[lang] ?: it["en"]) !== null } + ?.values?.singleOrNull() // find something else from alt titles + title = dirtyTitle?.removeEntitiesAndMarkdown().orEmpty() + + coverFileName?.let { + thumbnail_url = when (!coverSuffix.isNullOrEmpty()) { + true -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName$coverSuffix" + else -> "${MDConstants.cdnUrl}/covers/${mangaDataDto.id}/$coverFileName" } } } @@ -302,10 +311,11 @@ class MangaDexHelper(lang: String) { val dexLocale = Locale.forLanguageTag(lang) val nonGenres = listOfNotNull( - attr.publicationDemographic?.let { intl.publicationDemographic(it) }, + attr.publicationDemographic + ?.let { intl["publication_demographic_${it.name.lowercase()}"] }, attr.contentRating .takeIf { it != ContentRatingDto.SAFE } - ?.let { intl.contentRatingGenre(it) }, + ?.let { intl.format("content_rating_genre", intl["content_rating_${it.name.lowercase()}"]) }, attr.originalLanguage ?.let { Locale.forLanguageTag(it) } ?.getDisplayName(dexLocale) @@ -335,10 +345,12 @@ class MangaDexHelper(lang: String) { val genreList = MDConstants.tagGroupsOrder.flatMap { genresMap[it].orEmpty() } + nonGenres - var desc = (attr.description[lang] ?: attr.description["en"])?.removeEntitiesAndMarkdown() ?: "" + var desc = (attr.description[lang] ?: attr.description["en"]) + ?.removeEntitiesAndMarkdown() + .orEmpty() if (altTitlesInDesc) { - val romanizedOriginalLang = MDConstants.romanizedLangCodes[attr.originalLanguage] ?: "" + val romanizedOriginalLang = MDConstants.romanizedLangCodes[attr.originalLanguage].orEmpty() val altTitles = attr.altTitles .filter { it.containsKey(lang) || it.containsKey(romanizedOriginalLang) } .mapNotNull { it.values.singleOrNull() } @@ -346,19 +358,19 @@ class MangaDexHelper(lang: String) { if (altTitles.isNotEmpty()) { val altTitlesDesc = altTitles - .joinToString("\n", "${intl.altTitleText}\n") { "• $it" } - desc += (if (desc.isNullOrBlank()) "" else "\n\n") + altTitlesDesc.removeEntitiesAndMarkdown() + .joinToString("\n", "${intl["alternative_titles"]}\n") { "• $it" } + desc += (if (desc.isBlank()) "" else "\n\n") + altTitlesDesc.removeEntitiesAndMarkdown() } } return createBasicManga(mangaDataDto, coverFileName, coverSuffix, lang).apply { description = desc - author = authors.joinToString(", ") - artist = artists.joinToString(", ") + author = authors.joinToString() + artist = artists.joinToString() status = getPublicationStatus(attr, chapters) genre = genreList .filter(String::isNotEmpty) - .joinToString(", ") + .joinToString() } } @@ -378,9 +390,9 @@ class MangaDexHelper(lang: String) { val users = chapterDataDto.relationships .filterIsInstance<UserDto>() .mapNotNull { it.attributes?.username } - if (users.isNotEmpty()) intl.uploadedBy(users) else "" + if (users.isNotEmpty()) intl.format("uploaded_by", users.joinToString(" & ")) else "" } - .ifEmpty { intl.noGroup } // "No Group" as final resort + .ifEmpty { intl["no_group"] } // "No Group" as final resort val chapterName = mutableListOf<String>() // Build chapter name @@ -447,13 +459,9 @@ class MangaDexHelper(lang: String) { editText.addTextChangedListener( object : TextWatcher { - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { - // Do nothing. - } + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - // Do nothing. - } + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit override fun afterTextChanged(editable: Editable?) { requireNotNull(editable) @@ -465,7 +473,7 @@ class MangaDexHelper(lang: String) { .map(String::trim) .all(::isUuid) - editText.error = if (!isValid) intl.invalidUuids else null + editText.error = if (!isValid) intl["invalid_uuids"] else null editText.rootView.findViewById<Button>(android.R.id.button1) ?.isEnabled = editText.error == null } diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexIntl.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexIntl.kt index 4a91a3b686..7abb152aea 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexIntl.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangaDexIntl.kt @@ -1,1049 +1,24 @@ package eu.kanade.tachiyomi.extension.all.mangadex -import eu.kanade.tachiyomi.extension.all.mangadex.dto.ContentRatingDto -import eu.kanade.tachiyomi.extension.all.mangadex.dto.PublicationDemographicDto -import java.text.Collator -import java.util.Locale - -class MangaDexIntl(lang: String) { - - val availableLang: String = if (lang in AVAILABLE_LANGS) lang else ENGLISH - - private val locale: Locale = Locale.forLanguageTag(availableLang) - - val collator: Collator = Collator.getInstance(locale) - - val invalidUuids: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "O texto contém UUIDs inválidos" - else -> "The text contains invalid UUIDs" - } - - val invalidGroupId: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "ID do grupo inválido" - SPANISH_LATAM, SPANISH -> "ID de grupo inválida" - RUSSIAN -> "Недействительный ID группы" - else -> "Not a valid group ID" - } - - val invalidAuthorId: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "ID do autor inválido" - SPANISH_LATAM, SPANISH -> "ID de autor inválida" - RUSSIAN -> "Недействительный ID автора" - else -> "Not a valid author ID" - } - - val noSeriesInList: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Sem séries na lista" - SPANISH_LATAM, SPANISH -> "No hay series en la lista" - RUSSIAN -> "Лист пуст" - else -> "No series in the list" - } - - val migrateWarning: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Migre esta entrada do $MANGADEX_NAME para o $MANGADEX_NAME para atualizar" - SPANISH_LATAM, SPANISH -> - "Migre la entrada $MANGADEX_NAME a $MANGADEX_NAME para actualizarla" - else -> "Migrate this entry from $MANGADEX_NAME to $MANGADEX_NAME to update it" - } - - val coverQuality: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Qualidade da capa" - SPANISH_LATAM, SPANISH -> "Calidad de la portada" - RUSSIAN -> "Качество обложки" - else -> "Cover quality" - } - - val coverQualityOriginal: String = when (availableLang) { - RUSSIAN -> "Оригинальное" - else -> "Original" - } - - val coverQualityMedium: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Média" - SPANISH_LATAM, SPANISH -> "Medio" - RUSSIAN -> "Среднее" - else -> "Medium" - } - - val coverQualityLow: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Baixa" - SPANISH_LATAM, SPANISH -> "Bajo" - RUSSIAN -> "Низкое" - else -> "Low" - } - - val dataSaver: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Economia de dados" - SPANISH_LATAM, SPANISH -> "Ahorro de datos" - RUSSIAN -> "Экономия трафика" - else -> "Data saver" - } - - val dataSaverSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Utiliza imagens menores e mais compactadas" - SPANISH_LATAM, SPANISH -> "Utiliza imágenes más pequeñas y más comprimidas" - RUSSIAN -> "Использует меньшие по размеру, сжатые изображения" - else -> "Enables smaller, more compressed images" - } - - val standardHttpsPort: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Utilizar somente a porta 443 do HTTPS" - SPANISH_LATAM, SPANISH -> "Utilizar el puerto 443 de HTTPS" - RUSSIAN -> "Использовать только HTTPS порт 443" - else -> "Use HTTPS port 443 only" - } - - val standardHttpsPortSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Ative para fazer requisições em somente servidores de imagem que usem a porta 443. " + - "Isso permite com que usuários com regras mais restritas de firewall possam acessar " + - "as imagens do $MANGADEX_NAME." - SPANISH_LATAM, SPANISH -> - "Habilite esta opción solicitar las imágenes a los servidores que usan el puerto 443. " + - "Esto permite a los usuarios con restricciones estrictas de firewall acceder " + - "a las imagenes en $MANGADEX_NAME" - RUSSIAN -> - "Запрашивает изображения только с серверов которые используют порт 443. " + - "Это позволяет пользователям со строгими правилами брандмауэра загружать " + - "изображения с $MANGADEX_NAME." - else -> - "Enable to only request image servers that use port 443. This allows users with " + - "stricter firewall restrictions to access $MANGADEX_NAME images" - } - - val contentRating: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Classificação de conteúdo" - SPANISH_LATAM, SPANISH -> "Clasificación de contenido" - RUSSIAN -> "Рейтинг контента" - else -> "Content rating" - } - - val standardContentRating: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Classificação de conteúdo padrão" - SPANISH_LATAM, SPANISH -> "Clasificación de contenido por defecto" - RUSSIAN -> "Рейтинг контента по умолчанию" - else -> "Default content rating" - } - - val standardContentRatingSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Mostra os conteúdos com as classificações selecionadas por padrão" - SPANISH_LATAM, SPANISH -> - "Muestra el contenido con la clasificación de contenido seleccionada por defecto" - RUSSIAN -> - "Показывать контент с выбранным рейтингом по умолчанию" - else -> "Show content with the selected ratings by default" - } - - val contentRatingSafe: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Seguro" - SPANISH_LATAM, SPANISH -> "Seguro" - RUSSIAN -> "Безопасный" - else -> "Safe" - } - - val contentRatingSuggestive: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Sugestivo" - SPANISH_LATAM, SPANISH -> "Sugestivo" - RUSSIAN -> "Намекающий" - else -> "Suggestive" - } - - val contentRatingErotica: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Erótico" - SPANISH_LATAM, SPANISH -> "Erótico" - RUSSIAN -> "Эротический" - else -> "Erotica" - } - - val contentRatingPornographic: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Pornográfico" - SPANISH_LATAM, SPANISH -> "Pornográfico" - RUSSIAN -> "Порнографический" - else -> "Pornographic" - } - - private val contentRatingMap: Map<ContentRatingDto, String> = mapOf( - ContentRatingDto.SAFE to contentRatingSafe, - ContentRatingDto.SUGGESTIVE to contentRatingSuggestive, - ContentRatingDto.EROTICA to contentRatingErotica, - ContentRatingDto.PORNOGRAPHIC to contentRatingPornographic, +object MangaDexIntl { + const val BRAZILIAN_PORTUGUESE = "pt-BR" + const val CHINESE = "zh" + const val ENGLISH = "en" + const val JAPANESE = "ja" + const val KOREAN = "ko" + const val PORTUGUESE = "pt" + const val SPANISH_LATAM = "es-419" + const val SPANISH = "es" + const val RUSSIAN = "ru" + + val AVAILABLE_LANGS = setOf( + ENGLISH, + BRAZILIAN_PORTUGUESE, + PORTUGUESE, + SPANISH, + SPANISH_LATAM, + RUSSIAN, ) - fun contentRatingGenre(contentRating: ContentRatingDto): String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Classificação: ${contentRatingMap[contentRating]}" - SPANISH_LATAM, SPANISH -> "Clasificación: ${contentRatingMap[contentRating]}" - RUSSIAN -> "Рейтинг контента: ${contentRatingMap[contentRating]}" - else -> "${this.contentRating}: ${contentRatingMap[contentRating]}" - } - - val originalLanguage: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Idioma original" - SPANISH_LATAM, SPANISH -> "Lenguaje original" - RUSSIAN -> "Язык оригинала" - else -> "Original language" - } - - val originalLanguageFilterJapanese: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "${languageDisplayName(JAPANESE)} (Mangá)" - RUSSIAN -> "${languageDisplayName(JAPANESE)} (Манга)" - else -> "${languageDisplayName(JAPANESE)} (Manga)" - } - - val originalLanguageFilterChinese: String = when (availableLang) { - RUSSIAN -> "${languageDisplayName(CHINESE)} (Манхуа)" - else -> "${languageDisplayName(CHINESE)} (Manhua)" - } - - val originalLanguageFilterKorean: String = when (availableLang) { - RUSSIAN -> "${languageDisplayName(KOREAN)} (Манхва)" - else -> "${languageDisplayName(KOREAN)} (Manhwa)" - } - - val filterOriginalLanguages: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Filtrar os idiomas originais" - SPANISH_LATAM, SPANISH -> "Filtrar por lenguajes" - RUSSIAN -> "Фильтр по языку оригинала" - else -> "Filter original languages" - } - - val filterOriginalLanguagesSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Mostra somente conteúdos que foram publicados originalmente nos idiomas " + - "selecionados nas seções de recentes e navegar" - SPANISH_LATAM, SPANISH -> - "Muestra solo el contenido publicado en los idiomas " + - "seleccionados en recientes y en la búsqueda" - RUSSIAN -> - "Показывать тайтлы которые изначально были выпущены только в выбранных" + - " языках в последних обновлениях и при поиске" - else -> - "Only show content that was originally published in the selected languages in " + - "both latest and browse" - } - - val blockGroupByUuid: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Bloquear grupos por UUID" - SPANISH_LATAM, SPANISH -> "Bloquear grupos por UUID" - RUSSIAN -> "Заблокировать группы по UUID" - else -> "Block groups by UUID" - } - - val blockGroupByUuidSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Capítulos de grupos bloqueados não irão aparecer no feed de Recentes ou Mangás. " + - "Digite uma lista de UUIDs dos grupos separados por vírgulas" - SPANISH_LATAM, SPANISH -> - "Los capítulos de los grupos bloqueados no aparecerán en Recientes o en el Feed" + - " de mangas. Introduce una coma para separar la lista de UUIDs" - RUSSIAN -> - "Главы от заблокированных групп не будут отображаться в последних обновлениях" + - " и в списке глав тайтла. Введите через запятую список UUID групп." - else -> - "Chapters from blocked groups will not show up in Latest or Manga feed. " + - "Enter as a Comma-separated list of group UUIDs" - } - - val blockUploaderByUuid: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Bloquear uploaders por UUID" - SPANISH_LATAM, SPANISH -> "Bloquear uploader por UUID" - RUSSIAN -> "Заблокировать загрузчика по UUID" - else -> "Block uploader by UUID" - } - - val blockUploaderByUuidSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Capítulos de usuários bloqueados não irão aparecer no feed de Recentes ou Mangás. " + - "Digite uma lista de UUIDs dos usuários separados por vírgulas" - SPANISH_LATAM, SPANISH -> - "Los capítulos de los uploaders bloqueados no aparecerán en Recientes o en el Feed" + - " de mangas. Introduce una coma para separar la lista de UUIDs" - RUSSIAN -> - "Главы от заблокированных загрузчиков не будут отображаться в последних обновлениях" + - " и в списке глав тайтла. Введите через запятую список UUID загрузчиков." - else -> - "Chapters from blocked uploaders will not show up in Latest or Manga feed. " + - "Enter as a Comma-separated list of uploader UUIDs" - } - - val altTitlesInDesc: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Títulos alternativos na descrição" - else -> "Alternative titles in description" - } - - val altTitlesInDescSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Inclui os títulos alternativos das séries no final de cada descrição" - else -> "Include a manga's alternative titles at the end of its description" - } - - val tryUsingFirstVolumeCover: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Tentar usar a capa do primeiro volume como capa" - else -> "Attempt to use the first volume cover as cover" - } - - val tryUsingFirstVolumeCoverSummary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Pode ser necessário atualizar os itens já adicionados na biblioteca. " + - "Alternativamente, limpe o banco de dados para as novas capas aparecerem." - else -> - "May need to manually refresh entries already in library. " + - "Otherwise, clear database to have new covers to show up." - } - - val publicationDemographic: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Demografia da publicação" - SPANISH_LATAM, SPANISH -> "Demografía" - RUSSIAN -> "Целевая аудитория" - else -> "Publication demographic" - } - - val publicationDemographicNone: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Nenhuma" - SPANISH_LATAM, SPANISH -> "Ninguna" - RUSSIAN -> "Нет" - else -> "None" - } - - val publicationDemographicShounen: String = when (availableLang) { - RUSSIAN -> "Сёнэн" - else -> "Shounen" - } - - val publicationDemographicShoujo: String = when (availableLang) { - RUSSIAN -> "Сёдзё" - else -> "Shoujo" - } - - val publicationDemographicSeinen: String = when (availableLang) { - RUSSIAN -> "Сэйнэн" - else -> "Seinen" - } - - val publicationDemographicJosei: String = when (availableLang) { - RUSSIAN -> "Дзёсэй" - else -> "Josei" - } - - fun publicationDemographic(demographic: PublicationDemographicDto): String = when (demographic) { - PublicationDemographicDto.NONE -> publicationDemographicNone - PublicationDemographicDto.SHOUNEN -> publicationDemographicShounen - PublicationDemographicDto.SHOUJO -> publicationDemographicShoujo - PublicationDemographicDto.SEINEN -> publicationDemographicSeinen - PublicationDemographicDto.JOSEI -> publicationDemographicJosei - } - - val status: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Estado" - SPANISH_LATAM, SPANISH -> "Estado" - RUSSIAN -> "Статус" - else -> "Status" - } - - val statusOngoing: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Em andamento" - SPANISH_LATAM, SPANISH -> "Publicandose" - RUSSIAN -> "Онгоинг" - else -> "Ongoing" - } - - val statusCompleted: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Completo" - SPANISH_LATAM, SPANISH -> "Completado" - RUSSIAN -> "Завершён" - else -> "Completed" - } - - val statusHiatus: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Hiato" - SPANISH_LATAM, SPANISH -> "Pausado" - RUSSIAN -> "Приостановлен" - else -> "Hiatus" - } - - val statusCancelled: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Cancelado" - SPANISH_LATAM, SPANISH -> "Cancelado" - RUSSIAN -> "Отменён" - else -> "Cancelled" - } - - val content: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Conteúdo" - SPANISH_LATAM, SPANISH -> "Contenido" - RUSSIAN -> "Неприемлемый контент" - else -> "Content" - } - - val contentGore: String = when (availableLang) { - RUSSIAN -> "Жестокость" - else -> "Gore" - } - - val contentSexualViolence: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Violência sexual" - SPANISH_LATAM, SPANISH -> "Violencia Sexual" - RUSSIAN -> "Сексуальное насилие" - else -> "Sexual Violence" - } - - val format: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Formato" - SPANISH_LATAM, SPANISH -> "Formato" - RUSSIAN -> "Формат" - else -> "Format" - } - - val formatAdaptation: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Adaptação" - SPANISH_LATAM, SPANISH -> "Adaptación" - RUSSIAN -> "Адаптация" - else -> "Adaptation" - } - - val formatAnthology: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Antologia" - SPANISH_LATAM, SPANISH -> "Antología" - RUSSIAN -> "Антология" - else -> "Anthology" - } - - val formatAwardWinning: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Premiado" - SPANISH_LATAM, SPANISH -> "Ganador de premio" - RUSSIAN -> "Отмеченный наградами" - else -> "Award Winning" - } - - val formatDoujinshi: String = when (availableLang) { - RUSSIAN -> "Додзинси" - else -> "Doujinshi" - } - - val formatFanColored: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Colorizado por fãs" - SPANISH_LATAM, SPANISH -> "Coloreado por fans" - RUSSIAN -> "Раскрашенная фанатами" - else -> "Fan Colored" - } - - val formatFourKoma: String = when (availableLang) { - RUSSIAN -> "Ёнкома" - else -> "4-Koma" - } - - val formatFullColor: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Colorido" - SPANISH_LATAM, SPANISH -> "Todo a color" - RUSSIAN -> "В цвете" - else -> "Full Color" - } - - val formatLongStrip: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Vertical" - SPANISH_LATAM, SPANISH -> "Tira larga" - RUSSIAN -> "Веб" - else -> "Long Strip" - } - - val formatOfficialColored: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Colorizado oficialmente" - SPANISH_LATAM, SPANISH -> "Coloreo oficial" - RUSSIAN -> "Официально раскрашенная" - else -> "Official Colored" - } - - val formatOneshot: String = when (availableLang) { - RUSSIAN -> "Сингл" - else -> "Oneshot" - } - - val formatUserCreated: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Criado por usuários" - SPANISH_LATAM, SPANISH -> "Creado por usuario" - RUSSIAN -> "Созданная пользователями" - else -> "User Created" - } - - val formatWebComic: String = when (availableLang) { - RUSSIAN -> "Веб-комикс" - else -> "Web Comic" - } - - val genre: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Gênero" - SPANISH_LATAM, SPANISH -> "Genero" - RUSSIAN -> "Жанр" - else -> "Genre" - } - - val genreAction: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Ação" - SPANISH_LATAM, SPANISH -> "Acción" - RUSSIAN -> "Боевик" - else -> "Action" - } - - val genreAdventure: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Aventura" - SPANISH_LATAM, SPANISH -> "Aventura" - RUSSIAN -> "Приключения" - else -> "Adventure" - } - - val genreBoysLove: String = when (availableLang) { - RUSSIAN -> "BL" - else -> "Boy's Love" - } - - val genreComedy: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Comédia" - SPANISH_LATAM, SPANISH -> "Comedia" - RUSSIAN -> "Комедия" - else -> "Comedy" - } - - val genreCrime: String = when (availableLang) { - SPANISH_LATAM, SPANISH -> "Crimen" - RUSSIAN -> "Криминал" - else -> "Crime" - } - - val genreDrama: String = when (availableLang) { - RUSSIAN -> "Драма" - else -> "Drama" - } - - val genreFantasy: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Fantasia" - SPANISH_LATAM, SPANISH -> "Fantasia" - RUSSIAN -> "Фэнтези" - else -> "Fantasy" - } - - val genreGirlsLove: String = when (availableLang) { - RUSSIAN -> "GL" - else -> "Girl's Love" - } - - val genreHistorical: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Histórico" - SPANISH_LATAM, SPANISH -> "Histórico" - RUSSIAN -> "История" - else -> "Historical" - } - - val genreHorror: String = when (availableLang) { - RUSSIAN -> "Ужасы" - else -> "Horror" - } - - val genreIsekai: String = when (availableLang) { - RUSSIAN -> "Исекай" - else -> "Isekai" - } - - val genreMagicalGirls: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Garotas mágicas" - SPANISH_LATAM, SPANISH -> "Chicas mágicas" - RUSSIAN -> "Махо-сёдзё" - else -> "Magical Girls" - } - - val genreMecha: String = when (availableLang) { - RUSSIAN -> "Меха" - else -> "Mecha" - } - - val genreMedical: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Médico" - SPANISH_LATAM, SPANISH -> "Medico" - RUSSIAN -> "Медицина" - else -> "Medical" - } - - val genreMystery: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Mistério" - SPANISH_LATAM, SPANISH -> "Misterio" - RUSSIAN -> "Мистика" - else -> "Mystery" - } - - val genrePhilosophical: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Filosófico" - SPANISH_LATAM, SPANISH -> "Filosófico" - RUSSIAN -> "Философия" - else -> "Philosophical" - } - - val genreRomance: String = when (availableLang) { - RUSSIAN -> "Романтика" - else -> "Romance" - } - - val genreSciFi: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Ficção científica" - SPANISH_LATAM, SPANISH -> "Ciencia ficción" - RUSSIAN -> "Научная фантастика" - else -> "Sci-Fi" - } - - val genreSliceOfLife: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Cotidiano" - SPANISH_LATAM, SPANISH -> "Recuentos de la vida" - RUSSIAN -> "Повседневность" - else -> "Slice of Life" - } - - val genreSports: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Esportes" - SPANISH_LATAM, SPANISH -> "Deportes" - RUSSIAN -> "Спорт" - else -> "Sports" - } - - val genreSuperhero: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Super-heroi" - SPANISH_LATAM, SPANISH -> "Superhéroes" - RUSSIAN -> "Супергерои" - else -> "Superhero" - } - - val genreThriller: String = when (availableLang) { - RUSSIAN -> "Триллер" - else -> "Thriller" - } - - val genreTragedy: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Tragédia" - SPANISH_LATAM, SPANISH -> "Tragedia" - RUSSIAN -> "Трагедия" - else -> "Tragedy" - } - - val genreWuxia: String = when (availableLang) { - RUSSIAN -> "Культивация" - else -> "Wuxia" - } - - val theme: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Tema" - SPANISH_LATAM, SPANISH -> "Tema" - RUSSIAN -> "Теги" - else -> "Theme" - } - - val themeAliens: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Alienígenas" - SPANISH_LATAM, SPANISH -> "Alienígenas" - RUSSIAN -> "Инопланетяне" - else -> "Aliens" - } - - val themeAnimals: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Animais" - SPANISH_LATAM, SPANISH -> "Animales" - RUSSIAN -> "Животные" - else -> "Animals" - } - - val themeCooking: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Culinária" - SPANISH_LATAM, SPANISH -> "Cocina" - RUSSIAN -> "Кулинария" - else -> "Cooking" - } - - val themeCrossdressing: String = when (availableLang) { - SPANISH_LATAM, SPANISH -> "Travestismo" - RUSSIAN -> "Кроссдрессинг" - else -> "Crossdressing" - } - - val themeDelinquents: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Delinquentes" - SPANISH_LATAM, SPANISH -> "Delincuentes" - RUSSIAN -> "Хулиганы" - else -> "Delinquents" - } - - val themeDemons: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Demônios" - SPANISH_LATAM, SPANISH -> "Demonios" - RUSSIAN -> "Демоны" - else -> "Demons" - } - - val themeGenderSwap: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Troca de gêneros" - SPANISH_LATAM, SPANISH -> "Cambio de sexo" - RUSSIAN -> "Смена гендера" - else -> "Genderswap" - } - - val themeGhosts: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Fantasmas" - SPANISH_LATAM, SPANISH -> "Fantasmas" - RUSSIAN -> "Призраки" - else -> "Ghosts" - } - - val themeGyaru: String = when (availableLang) { - RUSSIAN -> "Гяру" - else -> "Gyaru" - } - - val themeHarem: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Harém" - RUSSIAN -> "Гарем" - else -> "Harem" - } - - val themeIncest: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Incesto" - SPANISH_LATAM, SPANISH -> "Incesto" - RUSSIAN -> "Инцест" - else -> "Incest" - } - - val themeLoli: String = when (availableLang) { - RUSSIAN -> "Лоли" - else -> "Loli" - } - - val themeMafia: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Máfia" - RUSSIAN -> "Мафия" - else -> "Mafia" - } - - val themeMagic: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Magia" - SPANISH_LATAM, SPANISH -> "Magia" - RUSSIAN -> "Магия" - else -> "Magic" - } - - val themeMartialArts: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Artes marciais" - SPANISH_LATAM, SPANISH -> "Artes marciales" - RUSSIAN -> "Боевые исскуства" - else -> "Martial Arts" - } - - val themeMilitary: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Militar" - SPANISH_LATAM, SPANISH -> "Militar" - RUSSIAN -> "Военные" - else -> "Military" - } - - val themeMonsterGirls: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Garotas monstro" - SPANISH_LATAM, SPANISH -> "Chicas monstruo" - RUSSIAN -> "Монстродевушки" - else -> "Monster Girls" - } - - val themeMonsters: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Monstros" - SPANISH_LATAM, SPANISH -> "Monstruos" - RUSSIAN -> "Монстры" - else -> "Monsters" - } - - val themeMusic: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Musical" - SPANISH_LATAM, SPANISH -> "Musica" - RUSSIAN -> "Музыка" - else -> "Music" - } - - val themeNinja: String = when (availableLang) { - RUSSIAN -> "Ниндзя" - else -> "Ninja" - } - - val themeOfficeWorkers: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Funcionários de escritório" - SPANISH_LATAM, SPANISH -> "Oficinistas" - RUSSIAN -> "Офисные работники" - else -> "Office Workers" - } - - val themePolice: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Policial" - SPANISH_LATAM, SPANISH -> "Policial" - RUSSIAN -> "Полиция" - else -> "Police" - } - - val themePostApocalyptic: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Pós-apocalíptico" - SPANISH_LATAM, SPANISH -> "Post-Apocalíptico" - RUSSIAN -> "Постапокалиптика" - else -> "Post-Apocalyptic" - } - - val themePsychological: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Psicológico" - SPANISH_LATAM, SPANISH -> "Psicológico" - RUSSIAN -> "Психология" - else -> "Psychological" - } - - val themeReincarnation: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Reencarnação" - SPANISH_LATAM, SPANISH -> "Reencarnación" - RUSSIAN -> "Реинкарнация" - else -> "Reincarnation" - } - - val themeReverseHarem: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Harém reverso" - SPANISH_LATAM, SPANISH -> "Harem Inverso" - RUSSIAN -> "Обратный гарем" - else -> "Reverse Harem" - } - - val themeSamurai: String = when (availableLang) { - RUSSIAN -> "Самураи" - else -> "Samurai" - } - - val themeSchoolLife: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Vida escolar" - SPANISH_LATAM, SPANISH -> "Vida escolar" - RUSSIAN -> "Школа" - else -> "School Life" - } - - val themeShota: String = when (availableLang) { - RUSSIAN -> "Шота" - else -> "Shota" - } - - val themeSupernatural: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Sobrenatural" - SPANISH_LATAM, SPANISH -> "Sobrenatural" - RUSSIAN -> "Сверхъестественное" - else -> "Supernatural" - } - - val themeSurvival: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Sobrevivência" - SPANISH_LATAM, SPANISH -> "Supervivencia" - RUSSIAN -> "Выживание" - else -> "Survival" - } - - val themeTimeTravel: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Viagem no tempo" - SPANISH_LATAM, SPANISH -> "Viaje en el tiempo" - RUSSIAN -> "Путешествие во времени" - else -> "Time Travel" - } - - val themeTraditionalGames: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Jogos tradicionais" - SPANISH_LATAM, SPANISH -> "Juegos tradicionales" - RUSSIAN -> "Традиционные игры" - else -> "Traditional Games" - } - - val themeVampires: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Vampiros" - SPANISH_LATAM, SPANISH -> "Vampiros" - RUSSIAN -> "Вампиры" - else -> "Vampires" - } - - val themeVideoGames: String = when (availableLang) { - SPANISH_LATAM, SPANISH -> "Videojuegos" - RUSSIAN -> "Видеоигры" - else -> "Video Games" - } - - val themeVillainess: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Vilania" - SPANISH_LATAM, SPANISH -> "Villana" - RUSSIAN -> "Злодейка" - else -> "Villainess" - } - - val themeVirtualReality: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Realidade virtual" - SPANISH_LATAM, SPANISH -> "Realidad Virtual" - RUSSIAN -> "Виртуальная реальность" - else -> "Virtual Reality" - } - - val themeZombies: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Zumbis" - RUSSIAN -> "Зомби" - else -> "Zombies" - } - - val tags: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Modo das tags" - SPANISH_LATAM, SPANISH -> "Modo de etiquetas" - RUSSIAN -> "Режим поиска" - else -> "Tags mode" - } - - val includedTagsMode: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Modo de inclusão de tags" - SPANISH_LATAM, SPANISH -> "Modo de etiquetas incluidas" - RUSSIAN -> "Включая" - else -> "Included tags mode" - } - - val excludedTagsMode: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Modo de exclusão de tags" - SPANISH_LATAM, SPANISH -> "Modo de etiquetas excluidas" - RUSSIAN -> "Исключая" - else -> "Excluded tags mode" - } - - val modeAnd: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "E" - SPANISH_LATAM, SPANISH -> "Y" - RUSSIAN -> "И" - else -> "And" - } - - val modeOr: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Ou" - SPANISH_LATAM, SPANISH -> "O" - RUSSIAN -> "Или" - else -> "Or" - } - - val sort: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Ordenar" - SPANISH_LATAM, SPANISH -> "Ordenar" - RUSSIAN -> "Сортировать по" - else -> "Sort" - } - - val sortAlphabetic: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Alfabeticamente" - SPANISH_LATAM, SPANISH -> "Alfabeticamente" - RUSSIAN -> "Алфавиту" - else -> "Alphabetic" - } - - val sortChapterUploadedAt: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Upload do capítulo" - SPANISH_LATAM, SPANISH -> "Capítulo subido en" - RUSSIAN -> "Загруженной главе" - else -> "Chapter uploaded at" - } - - val sortNumberOfFollows: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Número de seguidores" - SPANISH_LATAM, SPANISH -> "Número de seguidores" - RUSSIAN -> "Количеству фолловеров" - else -> "Number of follows" - } - - val sortContentCreatedAt: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Criação do conteúdo" - SPANISH_LATAM, SPANISH -> "Contenido creado en" - RUSSIAN -> "По дате создания" - else -> "Content created at" - } - - val sortContentInfoUpdatedAt: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Atualização das informações" - SPANISH_LATAM, SPANISH -> "Información del contenido actualizada en" - RUSSIAN -> "По дате обновления" - else -> "Content info updated at" - } - - val sortRelevance: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Relevância" - SPANISH_LATAM, SPANISH -> "Relevancia" - RUSSIAN -> "Лучшему соответствию" - else -> "Relevance" - } - - val sortYear: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Ano de lançamento" - SPANISH_LATAM, SPANISH -> "Año" - RUSSIAN -> "Год" - else -> "Year" - } - - val sortRating: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Nota" - SPANISH_LATAM, SPANISH -> "Calificación" - RUSSIAN -> "Популярности" - else -> "Rating" - } - - val hasAvailableChapters: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Há capítulos disponíveis" - SPANISH_LATAM, SPANISH -> "Tiene capítulos disponibles" - RUSSIAN -> "Есть главы" - else -> "Has available chapters" - } - - fun languageDisplayName(localeCode: String): String = - Locale.forLanguageTag(localeCode) - .getDisplayName(locale) - .replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() } - - fun unableToProcessChapterRequest(code: Int): String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> - "Não foi possível processar a requisição do capítulo. Código HTTP: $code" - SPANISH_LATAM, SPANISH -> - "No se ha podido procesar la solicitud del capítulo. Código HTTP: $code" - RUSSIAN -> - "Не удалось обработать ссылку на главу. Ошибка: $code" - else -> "Unable to process Chapter request. HTTP code: $code" - } - - fun uploadedBy(users: List<String>): String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Enviado por ${users.joinToString(" & ")}" - SPANISH_LATAM, SPANISH -> "Subido por: ${users.joinToString(" & ") }" - RUSSIAN -> "Загрузил ${users.joinToString(" & ")}" - else -> "Uploaded by ${users.joinToString(" & ")}" - } - - val noGroup: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Sem grupo" - SPANISH_LATAM, SPANISH -> "Sin grupo" - RUSSIAN -> "Нет группы" - else -> "No Group" - } - - val altTitleText: String = when (availableLang) { - BRAZILIAN_PORTUGUESE, PORTUGUESE -> "Títulos alternativos:" - else -> "Alternative titles:" - } - - companion object { - const val BRAZILIAN_PORTUGUESE = "pt-BR" - const val CHINESE = "zh" - const val ENGLISH = "en" - const val JAPANESE = "ja" - const val KOREAN = "ko" - const val PORTUGUESE = "pt" - const val SPANISH_LATAM = "es-419" - const val SPANISH = "es" - const val RUSSIAN = "ru" - - val AVAILABLE_LANGS = arrayOf( - ENGLISH, - BRAZILIAN_PORTUGUESE, - PORTUGUESE, - SPANISH_LATAM, - SPANISH, - RUSSIAN, - ) - - const val MANGADEX_NAME = "MangaDex" - } + const val MANGADEX_NAME = "MangaDex" } diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangadexUrlActivity.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangadexUrlActivity.kt index fc473bdf6c..2d0256a870 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangadexUrlActivity.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MangadexUrlActivity.kt @@ -22,17 +22,17 @@ class MangadexUrlActivity : Activity() { super.onCreate(savedInstanceState) val pathSegments = intent?.data?.pathSegments if (pathSegments != null && pathSegments.size > 1) { - val titleid = pathSegments[1] + val titleId = pathSegments[1] val mainIntent = Intent().apply { action = "eu.kanade.tachiyomi.SEARCH" with(pathSegments[0]) { when { - equals("chapter") -> putExtra("query", "${MDConstants.prefixChSearch}$titleid") - equals("group") -> putExtra("query", "${MDConstants.prefixGrpSearch}$titleid") - equals("user") -> putExtra("query", "${MDConstants.prefixUsrSearch}$titleid") - equals("author") -> putExtra("query", "${MDConstants.prefixAuthSearch}$titleid") - equals("list") -> putExtra("query", "${MDConstants.prefixListSearch}$titleid") - else -> putExtra("query", "${MDConstants.prefixIdSearch}$titleid") + equals("chapter") -> putExtra("query", MDConstants.prefixChSearch + titleId) + equals("group") -> putExtra("query", MDConstants.prefixGrpSearch + titleId) + equals("user") -> putExtra("query", MDConstants.prefixUsrSearch + titleId) + equals("author") -> putExtra("query", MDConstants.prefixAuthSearch + titleId) + equals("list") -> putExtra("query", MDConstants.prefixListSearch + titleId) + else -> putExtra("query", MDConstants.prefixIdSearch + titleId) } } putExtra("filter", packageName) @@ -44,7 +44,7 @@ class MangadexUrlActivity : Activity() { Log.e("MangadexUrlActivity", e.toString()) } } else { - Log.e("MangadexUrlActivity", "could not parse uri from intent $intent") + Log.e("MangadexUrlActivity", "Could not parse URI from intent $intent") } finish() diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt index c717874203..48c79056b9 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/MdAtHomeReportInterceptor.kt @@ -2,22 +2,23 @@ package eu.kanade.tachiyomi.extension.all.mangadex import android.util.Log import eu.kanade.tachiyomi.extension.all.mangadex.dto.ImageReportDto +import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import okhttp3.Call import okhttp3.Callback import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import uy.kohesive.injekt.injectLazy -import java.io.IOException /** - * Interceptor to post to md@home for MangaDex Stats + * Interceptor to post to MD@Home for MangaDex Stats */ class MdAtHomeReportInterceptor( private val client: OkHttpClient, @@ -26,23 +27,20 @@ class MdAtHomeReportInterceptor( private val json: Json by injectLazy() - private val mdAtHomeUrlRegex = - Regex("""^https://[\w\d]+\.[\w\d]+\.mangadex(\b-test\b)?\.network.*${'$'}""") - override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() val response = chain.proceed(chain.request()) val url = originalRequest.url.toString() - if (!url.contains(mdAtHomeUrlRegex)) { + if (!url.contains(MD_AT_HOME_URL_REGEX)) { return response } val result = ImageReportDto( - url, + url = url, success = response.isSuccessful, bytes = response.peekBody(Long.MAX_VALUE).bytes().size, - cached = response.header("X-Cache", "") == "HIT", + cached = response.headers["X-Cache"] == "HIT", duration = response.receivedResponseAtMillis - response.sentRequestAtMillis, ) @@ -57,22 +55,38 @@ class MdAtHomeReportInterceptor( // Execute the report endpoint network call asynchronously to avoid blocking // the reader from showing the image once it's fully loaded if the report call // gets stuck, as it tend to happens sometimes. - client.newCall(reportRequest).enqueue( - object : Callback { - override fun onFailure(call: Call, e: IOException) { - Log.e("MangaDex", "Error trying to POST report to MD@Home: ${e.message}") - } + client.newCall(reportRequest).enqueue(REPORT_CALLBACK) - override fun onResponse(call: Call, response: Response) { - response.close() - } - }, - ) + if (response.isSuccessful) { + return response + } + + Log.e("MangaDex", "Error connecting to MD@Home node, fallback to uploads server") - return response + val fallbackUrl = MDConstants.cdnUrl.toHttpUrl().newBuilder() + .addPathSegments(originalRequest.url.pathSegments.joinToString("/")) + .build() + + return client.newCall(GET(fallbackUrl, headers)).execute() } companion object { private val JSON_MEDIA_TYPE = "application/json".toMediaType() + private val MD_AT_HOME_URL_REGEX = + """^https://[\w\d]+\.[\w\d]+\.mangadex(\b-test\b)?\.network.*${'$'}""".toRegex() + + private val REPORT_CALLBACK = object : Callback { + override fun onFailure(call: Call, e: okio.IOException) { + Log.e("MangaDex", "Error trying to POST report to MD@Home: ${e.message}") + } + + override fun onResponse(call: Call, response: Response) { + if (!response.isSuccessful) { + Log.e("MangaDex", "Error trying to POST report to MD@Home: HTTP error ${response.code}") + } + + response.close() + } + } } } diff --git a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/dto/ResponseDto.kt b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/dto/ResponseDto.kt index 47dd7ef5b9..68c7db9355 100644 --- a/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/dto/ResponseDto.kt +++ b/src/all/mangadex/src/eu/kanade/tachiyomi/extension/all/mangadex/dto/ResponseDto.kt @@ -10,7 +10,11 @@ data class PaginatedResponseDto<T : EntityDto>( val limit: Int = 0, val offset: Int = 0, val total: Int = 0, -) +) { + + val hasNextPage: Boolean + get() = limit + offset < total +} @Serializable data class ResponseDto<T : EntityDto>( diff --git a/src/all/mangapark/AndroidManifest.xml b/src/all/mangapark/AndroidManifest.xml index e832f73e17..9c5dbd1fba 100644 --- a/src/all/mangapark/AndroidManifest.xml +++ b/src/all/mangapark/AndroidManifest.xml @@ -1,7 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" -package="eu.kanade.tachiyomi.extension"> - +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".all.mangapark.MangaParkUrlActivity" diff --git a/src/all/mangaplus/AndroidManifest.xml b/src/all/mangaplus/AndroidManifest.xml index 9e6a755f12..c308de1b74 100644 --- a/src/all/mangaplus/AndroidManifest.xml +++ b/src/all/mangaplus/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -41,4 +40,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/all/mangaplus/assets/i18n/messages_en.properties b/src/all/mangaplus/assets/i18n/messages_en.properties new file mode 100644 index 0000000000..db98e72def --- /dev/null +++ b/src/all/mangaplus/assets/i18n/messages_en.properties @@ -0,0 +1,10 @@ +chapter_expired=The chapter reading period has expired. +image_quality=Image quality +image_quality_high=High +image_quality_low=Low +image_quality_medium=Medium +not_available=Title not available in this language. +split_double_pages=Split double pages +split_double_pages_summary=Only a few titles supports disabling this setting. +title_removed=This title was removed from the MANGA Plus catalogue. +unknown_error=An unknown error happened. \ No newline at end of file diff --git a/src/all/mangaplus/assets/i18n/messages_pt_br.properties b/src/all/mangaplus/assets/i18n/messages_pt_br.properties new file mode 100644 index 0000000000..6b07b5af6c --- /dev/null +++ b/src/all/mangaplus/assets/i18n/messages_pt_br.properties @@ -0,0 +1,10 @@ +chapter_expired=O período de leitura do capítulo expirou. +image_quality=Qualidade da imagem +image_quality_high=Alta +image_quality_low=Baixa +image_quality_medium=Média +not_available=Título não disponível neste idioma. +split_double_pages=Dividir as páginas duplas +split_double_pages_summary=Somente poucos títulos suportam a desativação desta configuração. +title_removed=Este título foi removido do catálogo do MANGA Plus. +unknown_error=Um erro desconhecido ocorreu. \ No newline at end of file diff --git a/src/all/mangaplus/assets/i18n/messages_vi.properties b/src/all/mangaplus/assets/i18n/messages_vi.properties new file mode 100644 index 0000000000..094aac664d --- /dev/null +++ b/src/all/mangaplus/assets/i18n/messages_vi.properties @@ -0,0 +1,10 @@ +chapter_expired=Đã hết thời gian đọc chương này. +image_quality=Chất lượng ảnh +image_quality_high=Cao +image_quality_low=Thấp +image_quality_medium=Vừa +not_available=Truyện không có sẵn ở ngôn ngữ này. +split_double_pages=Tách trang đôi +split_double_pages_summary=Chỉ một số truyện hỗ trợ không tách trang đôi. +title_removed=Truyện này đã bị gỡ khỏi MANGA Plus. +unknown_error=Đã xảy ra lỗi không xác định. \ No newline at end of file diff --git a/src/all/mangaplus/build.gradle b/src/all/mangaplus/build.gradle index 229805972b..32fec15bca 100644 --- a/src/all/mangaplus/build.gradle +++ b/src/all/mangaplus/build.gradle @@ -6,7 +6,11 @@ ext { extName = 'MANGA Plus by SHUEISHA' pkgNameSuffix = 'all.mangaplus' extClass = '.MangaPlusFactory' - extVersionCode = 42 + extVersionCode = 44 +} + +dependencies { + implementation(project(":lib-i18n")) } apply from: "$rootDir/common.gradle" diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt index 75f16885c2..67134c8a1f 100644 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt +++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlus.kt @@ -5,6 +5,7 @@ import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.lib.i18n.Intl import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.interceptor.rateLimitHost import eu.kanade.tachiyomi.source.ConfigurableSource @@ -57,7 +58,14 @@ class MangaPlus( private val json: Json by injectLazy() - private val intl by lazy { MangaPlusIntl(langCode) } + private val intl by lazy { + Intl( + language = lang, + baseLanguage = "en", + availableLanguages = setOf("en", "pt-BR", "vi"), + classLoader = this::class.java.classLoader!!, + ) + } private val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) @@ -82,7 +90,7 @@ class MangaPlus( val result = response.asMangaPlusResponse() checkNotNull(result.success) { - result.error!!.langPopup(langCode)?.body ?: intl.unknownError + result.error!!.langPopup(langCode)?.body ?: intl["unknown_error"] } val titleList = result.success.titleRankingView!!.titles @@ -105,7 +113,7 @@ class MangaPlus( val result = response.asMangaPlusResponse() checkNotNull(result.success) { - result.error!!.langPopup(langCode)?.body ?: intl.unknownError + result.error!!.langPopup(langCode)?.body ?: intl["unknown_error"] } // Fetch all titles to get newer thumbnail URLs in the interceptor. @@ -151,7 +159,7 @@ class MangaPlus( val result = response.asMangaPlusResponse() checkNotNull(result.success) { - result.error!!.langPopup(langCode)?.body ?: intl.unknownError + result.error!!.langPopup(langCode)?.body ?: intl["unknown_error"] } if (result.success.titleDetailView != null) { @@ -163,7 +171,7 @@ class MangaPlus( } if (result.success.mangaViewer != null) { - checkNotNull(result.success.mangaViewer.titleId) { intl.chapterExpired } + checkNotNull(result.success.mangaViewer.titleId) { intl["chapter_expired"] } val titleId = result.success.mangaViewer.titleId val cachedTitle = titleCache?.get(titleId) @@ -173,7 +181,7 @@ class MangaPlus( val titleResult = client.newCall(titleRequest).execute().asMangaPlusResponse() checkNotNull(titleResult.success) { - titleResult.error!!.langPopup(langCode)?.body ?: intl.unknownError + titleResult.error!!.langPopup(langCode)?.body ?: intl["unknown_error"] } titleResult.success.titleDetailView!! @@ -222,15 +230,15 @@ class MangaPlus( val error = result.error!!.langPopup(langCode) when { - error?.subject == NOT_FOUND_SUBJECT -> intl.titleRemoved + error?.subject == NOT_FOUND_SUBJECT -> intl["title_removed"] !error?.body.isNullOrEmpty() -> error!!.body - else -> intl.unknownError + else -> intl["unknown_error"] } } val titleDetails = result.success.titleDetailView!! .takeIf { it.title.language == langCode } - ?: throw Exception(intl.notAvailable) + ?: throw Exception(intl["not_available"]) return titleDetails.toSManga() } @@ -244,9 +252,9 @@ class MangaPlus( val error = result.error!!.langPopup(langCode) when { - error?.subject == NOT_FOUND_SUBJECT -> intl.titleRemoved + error?.subject == NOT_FOUND_SUBJECT -> intl["title_removed"] !error?.body.isNullOrEmpty() -> error!!.body - else -> intl.unknownError + else -> intl["unknown_error"] } } @@ -290,9 +298,9 @@ class MangaPlus( val error = result.error!!.langPopup(langCode) when { - error?.subject == NOT_FOUND_SUBJECT -> intl.chapterExpired + error?.subject == NOT_FOUND_SUBJECT -> intl["chapter_expired"] !error?.body.isNullOrEmpty() -> error!!.body - else -> intl.unknownError + else -> intl["unknown_error"] } } @@ -322,8 +330,12 @@ class MangaPlus( override fun setupPreferenceScreen(screen: PreferenceScreen) { val qualityPref = ListPreference(screen.context).apply { key = "${QUALITY_PREF_KEY}_$lang" - title = intl.imageQuality - entries = arrayOf(intl.imageQualityLow, intl.imageQualityMedium, intl.imageQualityHigh) + title = intl["image_quality"] + entries = arrayOf( + intl["image_quality_low"], + intl["image_quality_medium"], + intl["image_quality_high"], + ) entryValues = QUALITY_PREF_ENTRY_VALUES setDefaultValue(QUALITY_PREF_DEFAULT_VALUE) summary = "%s" @@ -331,8 +343,8 @@ class MangaPlus( val splitPref = SwitchPreferenceCompat(screen.context).apply { key = "${SPLIT_PREF_KEY}_$lang" - title = intl.splitDoublePages - summary = intl.splitDoublePagesSummary + title = intl["split_double_pages"] + summary = intl["split_double_pages_summary"] setDefaultValue(SPLIT_PREF_DEFAULT_VALUE) } diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt index c57c459f10..2aa7db7c76 100644 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt +++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusDto.kt @@ -146,6 +146,7 @@ enum class Language { PORTUGUESE_BR, RUSSIAN, THAI, + VIETNAMESE, } @Serializable diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt index 37a56d1a30..83b5a3c1b8 100644 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt +++ b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusFactory.kt @@ -12,5 +12,6 @@ class MangaPlusFactory : SourceFactory { MangaPlus("pt-BR", "ptb", Language.PORTUGUESE_BR), MangaPlus("ru", "rus", Language.RUSSIAN), MangaPlus("th", "tha", Language.THAI), + MangaPlus("vi", "vie", Language.VIETNAMESE), ) } diff --git a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusIntl.kt b/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusIntl.kt deleted file mode 100644 index 028768e97f..0000000000 --- a/src/all/mangaplus/src/eu/kanade/tachiyomi/extension/all/mangaplus/MangaPlusIntl.kt +++ /dev/null @@ -1,54 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.mangaplus - -class MangaPlusIntl(lang: Language) { - - val imageQuality: String = when (lang) { - Language.PORTUGUESE_BR -> "Qualidade da imagem" - else -> "Image quality" - } - - val imageQualityLow: String = when (lang) { - Language.PORTUGUESE_BR -> "Baixa" - else -> "Low" - } - - val imageQualityMedium: String = when (lang) { - Language.PORTUGUESE_BR -> "Média" - else -> "Medium" - } - - val imageQualityHigh: String = when (lang) { - Language.PORTUGUESE_BR -> "Alta" - else -> "High" - } - - val splitDoublePages: String = when (lang) { - Language.PORTUGUESE_BR -> "Dividir as páginas duplas" - else -> "Split double pages" - } - - val splitDoublePagesSummary: String = when (lang) { - Language.PORTUGUESE_BR -> "Somente poucos títulos suportam a desativação desta configuração." - else -> "Only a few titles supports disabling this setting." - } - - val chapterExpired: String = when (lang) { - Language.PORTUGUESE_BR -> "O período de leitura do capítulo expirou." - else -> "The chapter reading period has expired." - } - - val notAvailable: String = when (lang) { - Language.PORTUGUESE_BR -> "Título não disponível neste idioma." - else -> "Title not available in this language." - } - - val unknownError: String = when (lang) { - Language.PORTUGUESE_BR -> "Um erro desconhecido ocorreu." - else -> "An unknown error happened." - } - - val titleRemoved: String = when (lang) { - Language.PORTUGUESE_BR -> "Este título foi removido do catálogo do MANGA Plus." - else -> "This title was removed from the MANGA Plus catalogue." - } -} diff --git a/src/all/mangapluscreators/AndroidManifest.xml b/src/all/mangapluscreators/AndroidManifest.xml index 389c51256f..8072ee00db 100644 --- a/src/all/mangapluscreators/AndroidManifest.xml +++ b/src/all/mangapluscreators/AndroidManifest.xml @@ -1,3 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> - +<manifest /> diff --git a/src/all/mangatoon/AndroidManifest.xml b/src/all/mangatoon/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/mangatoon/AndroidManifest.xml +++ b/src/all/mangatoon/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/mangaup/AndroidManifest.xml b/src/all/mangaup/AndroidManifest.xml index 02005096f2..964fa77eb4 100644 --- a/src/all/mangaup/AndroidManifest.xml +++ b/src/all/mangaup/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/all/mango/AndroidManifest.xml b/src/all/mango/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/mango/AndroidManifest.xml +++ b/src/all/mango/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/meituatop/AndroidManifest.xml b/src/all/meituatop/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/meituatop/AndroidManifest.xml +++ b/src/all/meituatop/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/myreadingmanga/AndroidManifest.xml b/src/all/myreadingmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/myreadingmanga/AndroidManifest.xml +++ b/src/all/myreadingmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/netcomics/AndroidManifest.xml b/src/all/netcomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/netcomics/AndroidManifest.xml +++ b/src/all/netcomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/nhentai/AndroidManifest.xml b/src/all/nhentai/AndroidManifest.xml index ce28ef43d9..24458dcdaa 100644 --- a/src/all/nhentai/AndroidManifest.xml +++ b/src/all/nhentai/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/all/nhentai/build.gradle b/src/all/nhentai/build.gradle index a66b450855..45f226e94c 100644 --- a/src/all/nhentai/build.gradle +++ b/src/all/nhentai/build.gradle @@ -5,8 +5,12 @@ ext { extName = 'NHentai' pkgNameSuffix = 'all.nhentai' extClass = '.NHFactory' - extVersionCode = 37 + extVersionCode = 39 isNsfw = true } +dependencies { + implementation(project(":lib-randomua")) +} + apply from: "$rootDir/common.gradle" diff --git a/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt b/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt index 8023aeebd1..e11032add5 100644 --- a/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt +++ b/src/all/nhentai/src/eu/kanade/tachiyomi/extension/all/nhentai/NHentai.kt @@ -2,12 +2,18 @@ package eu.kanade.tachiyomi.extension.all.nhentai import android.app.Application import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getArtists import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getGroups import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getNumPages import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTagDescription import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTags import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTime +import eu.kanade.tachiyomi.lib.randomua.addRandomUAPreferenceToScreen +import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA +import eu.kanade.tachiyomi.lib.randomua.getPrefUAType +import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.interceptor.rateLimit @@ -44,14 +50,21 @@ open class NHentai( override val supportsLatest = true - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .rateLimit(4) - .build() - private val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) } + override val client: OkHttpClient by lazy { + network.cloudflareClient.newBuilder() + .setRandomUserAgent( + userAgentType = preferences.getPrefUAType(), + customUA = preferences.getPrefCustomUA(), + filterInclude = listOf("chrome"), + ) + .rateLimit(4) + .build() + } + private var displayFullTitle: Boolean = when (preferences.getString(TITLE_PREF, "full")) { "full" -> true else -> false @@ -60,13 +73,14 @@ open class NHentai( private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""") private fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim() - override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { - val serverPref = androidx.preference.ListPreference(screen.context).apply { + override fun setupPreferenceScreen(screen: PreferenceScreen) { + ListPreference(screen.context).apply { key = TITLE_PREF title = TITLE_PREF entries = arrayOf("Full Title", "Short Title") entryValues = arrayOf("full", "short") summary = "%s" + setDefaultValue("full") setOnPreferenceChangeListener { _, newValue -> displayFullTitle = when (newValue) { @@ -75,13 +89,9 @@ open class NHentai( } true } - } - - if (!preferences.contains(TITLE_PREF)) { - preferences.edit().putString(TITLE_PREF, "full").apply() - } + }.also(screen::addPreference) - screen.addPreference(serverPref) + addRandomUAPreferenceToScreen(screen) } override fun latestUpdatesRequest(page: Int) = GET(if (nhLang.isBlank()) "$baseUrl/?page=$page" else "$baseUrl/language/$nhLang/?page=$page", headers) diff --git a/src/all/ninemanga/AndroidManifest.xml b/src/all/ninemanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/ninemanga/AndroidManifest.xml +++ b/src/all/ninemanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/ninenineninehentai/AndroidManifest.xml b/src/all/ninenineninehentai/AndroidManifest.xml new file mode 100644 index 0000000000..d9e582cf32 --- /dev/null +++ b/src/all/ninenineninehentai/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + <application android:icon="@mipmap/ic_launcher"> + <activity + android:name=".all.ninenineninehentai.NineNineNineHentaiUrlActivity" + android:excludeFromRecents="true" + android:exported="true" + android:theme="@android:style/Theme.NoDisplay"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data android:host="999hentai.net"/> + <data android:scheme="https"/> + <data android:pathPattern="/hchapter/..*"/> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/src/all/ninenineninehentai/build.gradle b/src/all/ninenineninehentai/build.gradle new file mode 100644 index 0000000000..8effe1b120 --- /dev/null +++ b/src/all/ninenineninehentai/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = '999Hentai' + pkgNameSuffix = 'all.ninenineninehentai' + extClass = '.NineNineNineHentaiFactory' + extVersionCode = 3 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/ninenineninehentai/res/mipmap-hdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..f5d448ee4f Binary files /dev/null and b/src/all/ninenineninehentai/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/ninenineninehentai/res/mipmap-mdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..9296153092 Binary files /dev/null and b/src/all/ninenineninehentai/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/ninenineninehentai/res/mipmap-xhdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..2a76504d50 Binary files /dev/null and b/src/all/ninenineninehentai/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/ninenineninehentai/res/mipmap-xxhdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8df4c773f9 Binary files /dev/null and b/src/all/ninenineninehentai/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/ninenineninehentai/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/ninenineninehentai/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..0f3523c570 Binary files /dev/null and b/src/all/ninenineninehentai/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/ninenineninehentai/res/web_hi_res_512.png b/src/all/ninenineninehentai/res/web_hi_res_512.png new file mode 100644 index 0000000000..7e94372b4c Binary files /dev/null and b/src/all/ninenineninehentai/res/web_hi_res_512.png differ diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineHentaiFilters.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineHentaiFilters.kt new file mode 100644 index 0000000000..0526b14ffb --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineHentaiFilters.kt @@ -0,0 +1,69 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList + +abstract class SelectFilter( + displayName: String, + private val options: Array<Pair<String, String>>, +) : Filter.Select<String>( + displayName, + options.map { it.first }.toTypedArray(), +) { + val selected get() = options[state].second.takeUnless { it.isEmpty() } +} + +abstract class TextFilter(name: String) : Filter.Text(name) + +abstract class TagFilter(name: String) : TextFilter(name) { + val tags get() = state.split(",") + .map { it.trim().lowercase() } + .filter { it.isNotEmpty() } + .takeUnless { it.isEmpty() } +} + +abstract class PageFilter(name: String) : TextFilter(name) { + val value get() = state.trim().toIntOrNull() +} + +class SortFilter : SelectFilter( + "Sort By", + arrayOf( + Pair("Update", ""), + Pair("Popular", "Popular"), + Pair("Top", "Top"), + Pair("Name Ascending", "Name_ASC"), + Pair("Name Descending", "Name_DESC"), + ), +) + +class FormatFilter : SelectFilter( + "Format", + arrayOf( + Pair("", ""), + Pair("Manga", "manga"), + Pair("Doujinshi", "doujinshi"), + Pair("ArtistCG", "artistcg"), + Pair("GameCG", "gamecg"), + ), +) + +class MinPageFilter : PageFilter("Minimum Pages") + +class MaxPageFilter : PageFilter("Maximum Pages") + +class IncludedTagFilter : TagFilter("Include Tags") + +class ExcludedTagFilter : TagFilter("Exclude Tags") + +fun getFilters() = FilterList( + SortFilter(), + FormatFilter(), + Filter.Separator(), + MinPageFilter(), + MaxPageFilter(), + Filter.Separator(), + IncludedTagFilter(), + ExcludedTagFilter(), + Filter.Header("comma (,) separated tag/parody/character/artist/group"), +) diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentai.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentai.kt new file mode 100644 index 0000000000..225d0b0b62 --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentai.kt @@ -0,0 +1,305 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +import android.annotation.SuppressLint +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.extension.all.ninenineninehentai.Url.Companion.toAbsUrl +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import okhttp3.Headers +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat +import java.util.Locale + +open class NineNineNineHentai( + final override val lang: String, + private val siteLang: String = lang, +) : HttpSource(), ConfigurableSource { + + override val name = "999Hentai" + + override val baseUrl = "https://999hentai.net" + + private val apiUrl = "https://api.newsmama.top/api" + + override val supportsLatest = true + + private val json: Json by injectLazy() + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(1) + .build() + + private val preference by lazy { + Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) + } + + override fun popularMangaRequest(page: Int): Request { + val payload = GraphQL( + PopularVariables(size, page, 1, siteLang), + POPULAR_QUERY, + ) + + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) + } + + override fun popularMangaParse(response: Response): MangasPage { + val res = response.parseAs<ApiPopularResponse>() + val mangas = res.data.popular.edges + val dateMap = preference.dateMap + val entries = mangas.map { manga -> + manga.uploadDate?.let { dateMap[manga.id] = it } + manga.toSManga() + } + preference.dateMap = dateMap + val hasNextPage = mangas.size == size + + return MangasPage(entries, hasNextPage) + } + + override fun latestUpdatesRequest(page: Int) = searchMangaRequest(page, "", FilterList()) + + override fun latestUpdatesParse(response: Response) = searchMangaParse(response) + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + return if (query.startsWith(SEARCH_PREFIX)) { + val mangaId = query.substringAfter(SEARCH_PREFIX) + client.newCall(mangaFromIDRequest(mangaId)) + .asObservableSuccess() + .map(::searchMangaFromIDParse) + } else { + super.fetchSearchManga(page, query, filters) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val payload = GraphQL( + SearchVariables( + size = size, + page = page, + search = SearchPayload( + query = query.trim().takeUnless { it.isEmpty() }, + language = siteLang, + sortBy = filters.firstInstanceOrNull<SortFilter>()?.selected, + format = filters.firstInstanceOrNull<FormatFilter>()?.selected, + tags = filters.firstInstanceOrNull<IncludedTagFilter>()?.tags, + excludeTags = filters.firstInstanceOrNull<ExcludedTagFilter>()?.tags, + pagesRangeStart = filters.firstInstanceOrNull<MinPageFilter>()?.value, + pagesRangeEnd = filters.firstInstanceOrNull<MaxPageFilter>()?.value, + ), + ), + SEARCH_QUERY, + ) + + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) + } + + override fun searchMangaParse(response: Response): MangasPage { + val res = response.parseAs<ApiSearchResponse>() + val mangas = res.data.search.edges + val dateMap = preference.dateMap + val entries = mangas.map { manga -> + manga.uploadDate?.let { dateMap[manga.id] = it } + manga.toSManga() + } + preference.dateMap = dateMap + val hasNextPage = mangas.size == size + + return MangasPage(entries, hasNextPage) + } + + override fun getFilterList() = getFilters() + + private fun mangaFromIDRequest(id: String): Request { + val payload = GraphQL( + IdVariables(id), + DETAILS_QUERY, + ) + + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) + } + + private fun searchMangaFromIDParse(response: Response): MangasPage { + val res = response.parseAs<ApiDetailsResponse>() + + val manga = res.data.details + .takeIf { it.language == siteLang || lang == "all" } + ?.let { manga -> + preference.dateMap = preference.dateMap.also { dateMap -> + manga.uploadDate?.let { dateMap[manga.id] = it } + } + manga.toSManga() + } + + return MangasPage(listOfNotNull(manga), false) + } + + override fun mangaDetailsRequest(manga: SManga): Request { + return mangaFromIDRequest(manga.url) + } + + override fun mangaDetailsParse(response: Response): SManga { + val res = response.parseAs<ApiDetailsResponse>() + val manga = res.data.details + + preference.dateMap = preference.dateMap.also { dateMap -> + manga.uploadDate?.let { dateMap[manga.id] = it } + } + + return manga.toSManga() + } + + override fun getMangaUrl(manga: SManga) = "$baseUrl/hchapter/${manga.url}" + + override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { + val group = manga.description + ?.substringAfter("Group:", "") + ?.substringBefore("\n") + ?.trim() + ?.takeUnless { it.isEmpty() } + + return Observable.just( + listOf( + SChapter.create().apply { + name = "Chapter" + url = manga.url + date_upload = preference.dateMap[manga.url].parseDate() + scanlator = group + }, + ), + ) + } + + override fun getChapterUrl(chapter: SChapter) = "$baseUrl/hchapter/${chapter.url}" + + override fun pageListRequest(chapter: SChapter): Request { + val payload = GraphQL( + IdVariables(chapter.url), + PAGES_QUERY, + ) + + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) + } + + override fun pageListParse(response: Response): List<Page> { + val res = response.parseAs<ApiPageListResponse>() + + val pages = res.data.chapter.pages?.firstOrNull() + ?: return emptyList() + + val cdn = pages.urlPart.toAbsUrl() + + val selectedImages = when (preference.getString(PREF_IMG_QUALITY_KEY, "original")) { + "medium" -> pages.qualityMedium?.mapIndexed { i, it -> + it ?: pages.qualityOriginal[i] + } + else -> pages.qualityOriginal + } ?: pages.qualityOriginal + + return selectedImages.mapIndexed { index, image -> + Page(index, "", "$cdn/${image.url}") + } + } + + private inline fun <reified T> String.parseAs(): T = + json.decodeFromString(this) + + private inline fun <reified T> Response.parseAs(): T = + use { body.string() }.parseAs() + + private inline fun <reified T> List<*>.firstInstanceOrNull(): T? = + filterIsInstance<T>().firstOrNull() + + private inline fun <reified T : Any> T.toJsonRequestBody(): RequestBody = + json.encodeToString(this) + .toRequestBody(JSON_MEDIA_TYPE) + + private fun Headers.Builder.buildApiHeaders(requestBody: RequestBody) = this + .add("Content-Length", requestBody.contentLength().toString()) + .add("Content-Type", requestBody.contentType().toString()) + .build() + + private fun String?.parseDate(): Long { + return runCatching { + dateFormat.parse(this!!.trim())!!.time + }.getOrDefault(0L) + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + ListPreference(screen.context).apply { + key = PREF_IMG_QUALITY_KEY + title = "Default Image Quality" + entries = arrayOf("Original", "Medium") + entryValues = arrayOf("original", "medium") + setDefaultValue("original") + summary = "%s" + }.also(screen::addPreference) + } + + private var SharedPreferences.dateMap: MutableMap<String, String> + get() { + val jsonMap = getString(PREF_DATE_MAP_KEY, "{}")!! + val dateMap = runCatching { jsonMap.parseAs<MutableMap<String, String>>() } + return dateMap.getOrDefault(mutableMapOf()) + } + + @SuppressLint("ApplySharedPref") + set(dateMap) { + edit() + .putString(PREF_DATE_MAP_KEY, json.encodeToString(dateMap)) + .commit() + } + + override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Not Used") + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not Used") + + companion object { + private const val size = 20 + const val SEARCH_PREFIX = "id:" + + private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() + private val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) + } + + private const val PREF_DATE_MAP_KEY = "pref_date_map" + private const val PREF_IMG_QUALITY_KEY = "pref_image_quality" + } +} diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiDto.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiDto.kt new file mode 100644 index 0000000000..ffa1922b48 --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiDto.kt @@ -0,0 +1,159 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.util.Locale + +typealias ApiPopularResponse = Data<PopularResponse> + +typealias ApiSearchResponse = Data<SearchResponse> + +typealias ApiDetailsResponse = Data<DetailsResponse> + +typealias ApiPageListResponse = Data<PageList> + +@Serializable +data class Data<T>(val data: T) + +@Serializable +data class Edges<T>(val edges: List<T>) + +@Serializable +data class PopularResponse( + @SerialName("queryPopularChapters") val popular: Edges<ChapterResponse>, +) + +@Serializable +data class SearchResponse( + @SerialName("queryChapters") val search: Edges<ChapterResponse>, +) + +@Serializable +data class DetailsResponse( + @SerialName("queryChapter") val details: ChapterResponse, +) + +@Serializable +data class ChapterResponse( + @SerialName("_id") val id: String, + val name: String, + val uploadDate: String? = null, + val format: String? = null, + val language: String? = null, + val pages: Int? = null, + @SerialName("firstPics") val cover: List<Url>? = emptyList(), + val tags: List<Tag>? = emptyList(), +) { + fun toSManga() = SManga.create().apply { + url = id + title = name + thumbnail_url = cover?.firstOrNull()?.absUrl + author = this@ChapterResponse.author + artist = author + genre = genres + description = buildString { + if (formatParsed != null) append("Format: ${formatParsed}\n") + if (languageParsed != null) append("Language: $languageParsed\n") + if (group != null) append("Group: $group\n") + if (characters != null) append("Character(s): $characters\n") + if (parody != null) append("Parody: $parody\n") + if (pages != null) append("Pages: $pages\n") + } + status = SManga.COMPLETED + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + initialized = true + } + + private val formatParsed = when (format) { + "artistcg" -> "ArtistCG" + "gamecg" -> "GameCG" + else -> format?.capitalize() + } + + private val languageParsed = when (language) { + "en" -> "English" + "jp" -> "Japanese" + "cn" -> "Chinese" + "es" -> "Spanish" + else -> language + } + + private val author = tags?.firstOrNull { it.tagType == "artist" }?.tagName?.capitalize() + + private val group = tags?.filter { it.tagType == "group" } + ?.joinToString { it.tagName.capitalize() } + ?.takeUnless { it.isEmpty() } + + private val characters = tags?.filter { it.tagType == "character" } + ?.joinToString { it.tagName.capitalize() } + ?.takeUnless { it.isEmpty() } + + private val parody = tags?.filter { it.tagType == "parody" } + ?.joinToString { it.tagName.capitalize() } + ?.takeUnless { it.isEmpty() } + + private val genres = tags?.filterNot { it.tagType in filterTags } + ?.joinToString { it.tagName.capitalize() } + ?.takeUnless { it.isEmpty() } + + companion object { + private val filterTags = listOf("artist", "group", "character", "parody") + + private fun String.capitalize(): String { + return this.trim().split(" ").joinToString(" ") { word -> + word.replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase( + Locale.getDefault(), + ) + } else { + it.toString() + } + } + } + } + } +} + +@Serializable +data class Url(val url: String) { + val absUrl get() = url.toAbsUrl() + + companion object { + fun String.toAbsUrl(): String { + return if (this.matches(urlRegex)) { + this + } else { + cdnUrl + this + } + } + + private const val cdnUrl = "https://edge.anime-st.top/" + private val urlRegex = Regex("^https?://.*") + } +} + +@Serializable +data class Tag( + val tagName: String, + val tagType: String? = "genre", +) + +@Serializable +data class PageList( + @SerialName("queryChapter") val chapter: PageUrl, +) + +@Serializable +data class PageUrl( + @SerialName("pictureUrls") val pages: List<Pages?>? = emptyList(), +) + +@Serializable +data class Pages( + @SerialName("picCdn") val urlPart: String, + @SerialName("pics") val qualityOriginal: List<Url>, + @SerialName("picsM") val qualityMedium: List<Url?>? = emptyList(), +) diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiFactory.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiFactory.kt new file mode 100644 index 0000000000..eb3d6ba7be --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiFactory.kt @@ -0,0 +1,13 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +import eu.kanade.tachiyomi.source.SourceFactory + +class NineNineNineHentaiFactory : SourceFactory { + override fun createSources() = listOf( + NineNineNineHentai("all"), + NineNineNineHentai("en"), + NineNineNineHentai("ja", "jp"), + NineNineNineHentai("zh", "cn"), + NineNineNineHentai("es"), + ) +} diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiPayloadDto.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiPayloadDto.kt new file mode 100644 index 0000000000..e7ebc3b3ff --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiPayloadDto.kt @@ -0,0 +1,39 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +import kotlinx.serialization.Serializable + +@Serializable +data class GraphQL<T>( + val variables: T, + val query: String, +) + +@Serializable +data class PopularVariables( + val size: Int, + val page: Int, + val dateRange: Int, + val language: String, +) + +@Serializable +data class SearchVariables( + val size: Int, + val page: Int, + val search: SearchPayload, +) + +@Serializable +data class SearchPayload( + val query: String?, + val language: String, + val sortBy: String?, + val format: String?, + val tags: List<String>?, + val excludeTags: List<String>?, + val pagesRangeStart: Int?, + val pagesRangeEnd: Int?, +) + +@Serializable +data class IdVariables(val id: String) diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiQueries.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiQueries.kt new file mode 100644 index 0000000000..26759bb536 --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiQueries.kt @@ -0,0 +1,102 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +private fun buildQuery(queryAction: () -> String): String { + return queryAction() + .trimIndent() + .replace("%", "$") +} + +val POPULAR_QUERY: String = buildQuery { + """ + query( + %size: Int + %language: String + %dateRange: Int + %page: Int + ) { + queryPopularChapters( + size: %size + language: %language + dateRange: %dateRange + page: %page + ) { + edges { + _id + name + uploadDate + format + language + pages + firstPics + tags + } + } + } + """ +} + +val SEARCH_QUERY: String = buildQuery { + """ + query( + %search: SearchInput + %size: Int + %page: Int + ) { + queryChapters( + limit: %size + search: %search + page: %page + ) { + edges { + _id + name + uploadDate + format + language + pages + firstPics + tags + } + } + } + """ +} + +val DETAILS_QUERY: String = buildQuery { + """ + query( + %id: String + ) { + queryChapter( + chapterId: %id + ) { + _id + name + uploadDate + format + language + pages + firstPics + tags + } + } + """ +} + +val PAGES_QUERY: String = buildQuery { + """ + query( + %id: String + ) { + queryChapter( + chapterId: %id + ) { + pictureUrls { + picCdn + pics + picsM + } + } + } + """ +} diff --git a/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiUrlActivity.kt b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiUrlActivity.kt new file mode 100644 index 0000000000..d598b5fba9 --- /dev/null +++ b/src/all/ninenineninehentai/src/eu/kanade/tachiyomi/extension/all/ninenineninehentai/NineNineNineHentaiUrlActivity.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.extension.all.ninenineninehentai + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +class NineNineNineHentaiUrlActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size > 1) { + val id = pathSegments[1] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${NineNineNineHentai.SEARCH_PREFIX}$id") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("999HentaiUrlActivity", e.toString()) + } + } else { + Log.e("999HentaiUrlActivity", "could not parse uri from intent $intent") + } + + finish() + exitProcess(0) + } +} diff --git a/src/all/noisemanga/AndroidManifest.xml b/src/all/noisemanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/noisemanga/AndroidManifest.xml +++ b/src/all/noisemanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/novelcool/AndroidManifest.xml b/src/all/novelcool/AndroidManifest.xml new file mode 100644 index 0000000000..8072ee00db --- /dev/null +++ b/src/all/novelcool/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest /> diff --git a/src/all/novelcool/build.gradle b/src/all/novelcool/build.gradle new file mode 100644 index 0000000000..92f2e091eb --- /dev/null +++ b/src/all/novelcool/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'NovelCool' + pkgNameSuffix = 'all.novelcool' + extClass = '.NovelCoolFactory' + extVersionCode = 2 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/all/novelcool/res/mipmap-hdpi/ic_launcher.png b/src/all/novelcool/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..1630e02484 Binary files /dev/null and b/src/all/novelcool/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/novelcool/res/mipmap-mdpi/ic_launcher.png b/src/all/novelcool/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..579024f45c Binary files /dev/null and b/src/all/novelcool/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/novelcool/res/mipmap-xhdpi/ic_launcher.png b/src/all/novelcool/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..b47d8315d8 Binary files /dev/null and b/src/all/novelcool/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/novelcool/res/mipmap-xxhdpi/ic_launcher.png b/src/all/novelcool/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..f8b8e8b78a Binary files /dev/null and b/src/all/novelcool/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/novelcool/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/novelcool/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..6b682ecee2 Binary files /dev/null and b/src/all/novelcool/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/novelcool/res/web_hi_res_512.png b/src/all/novelcool/res/web_hi_res_512.png new file mode 100644 index 0000000000..84e3ff7823 Binary files /dev/null and b/src/all/novelcool/res/web_hi_res_512.png differ diff --git a/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCool.kt b/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCool.kt new file mode 100644 index 0000000000..5ae0038864 --- /dev/null +++ b/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCool.kt @@ -0,0 +1,455 @@ +package eu.kanade.tachiyomi.extension.all.novelcool + +import android.app.Application +import android.content.SharedPreferences +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.Interceptor +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import org.jsoup.select.Elements +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat +import java.util.Locale + +open class NovelCool( + final override val baseUrl: String, + final override val lang: String, + private val siteLang: String = lang, +) : ParsedHttpSource(), ConfigurableSource { + + override val name = "NovelCool" + + override val supportsLatest = true + + private val apiUrl = "https://api.novelcool.com" + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(1) + .build() + + private val pageClient by lazy { + client.newBuilder() + .addInterceptor(::jsRedirect) + .build() + } + + private val json: Json by injectLazy() + + private val preference by lazy { + Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) + } + + override fun fetchPopularManga(page: Int): Observable<MangasPage> { + return when (preference.useAppApi) { + true -> client.newCall(commonApiRequest("$apiUrl/elite/hot/", page)) + .asObservableSuccess() + .map(::commonApiResponseParse) + else -> super.fetchPopularManga(page) + } + } + + override fun popularMangaRequest(page: Int): Request { + // popular on the site only have novels + return GET("$baseUrl/category/new_list.html", headers) + } + + override fun popularMangaParse(response: Response): MangasPage { + runCatching { fetchGenres() } + + return super.popularMangaParse(response) + } + + override fun popularMangaNextPageSelector() = searchMangaNextPageSelector() + + override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element) + + override fun popularMangaSelector() = searchMangaSelector() + + override fun fetchLatestUpdates(page: Int): Observable<MangasPage> { + return when (preference.useAppApi) { + true -> client.newCall(commonApiRequest("$apiUrl/elite/latest/", page)) + .asObservableSuccess() + .map(::commonApiResponseParse) + else -> super.fetchLatestUpdates(page) + } + } + + override fun latestUpdatesRequest(page: Int): Request { + return GET("$baseUrl/category/latest.html", headers) + } + + override fun latestUpdatesParse(response: Response): MangasPage { + runCatching { fetchGenres() } + + return super.latestUpdatesParse(response) + } + + override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element) + + override fun latestUpdatesSelector() = searchMangaSelector() + + override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector() + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + return when (preference.useAppApi) { + true -> client.newCall(commonApiRequest("$apiUrl/book/search/", page, query)) + .asObservableSuccess() + .map(::commonApiResponseParse) + else -> super.fetchSearchManga(page, query, filters) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = "$baseUrl/search".toHttpUrl().newBuilder().apply { + addQueryParameter("name", query.trim()) + + filters.forEach { filter -> + when (filter) { + is AuthorFilter -> { + addQueryParameter("author", filter.state.trim()) + } + is GenreFilter -> { + addQueryParameter("category_id", filter.included.joinToString(",", ",")) + addQueryParameter("out_category_id", filter.excluded.joinToString(",", ",")) + } + is StatusFilter -> { + addQueryParameter("completed_series", filter.getValue()) + } + is RatingFilter -> { + addQueryParameter("rate_star", filter.getValue()) + } + else -> { } + } + } + + addQueryParameter("page", page.toString()) + }.build() + + return GET(url, headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val document = Jsoup.parse(response.peekBody(Long.MAX_VALUE).string()) + runCatching { fetchGenres(document) } + + return super.searchMangaParse(response) + } + + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + title = element.select(".book-pic").attr("title") + setUrlWithoutDomain(element.select("a").attr("href")) + thumbnail_url = element.select("img").imgAttr() + } + + override fun searchMangaSelector() = ".book-list .book-item:not(:has(.book-type-novel))" + + override fun searchMangaNextPageSelector() = "div.page-nav a div.next" + + private class AuthorFilter(title: String) : Filter.Text(title) + + private class GenreFilter(title: String, genres: List<Pair<String, String>>) : + Filter.Group<Genre>(title, genres.map { Genre(it.first, it.second) }) { + val included: List<String> + get() = state.filter { it.isIncluded() }.map { it.id } + + val excluded: List<String> + get() = state.filter { it.isExcluded() }.map { it.id } + } + class Genre(name: String, val id: String) : Filter.TriState(name) + + private fun getStatusList() = listOf( + Pair("All", ""), + Pair("Completed", "YES"), + Pair("Ongoing", "NO"), + ) + + private class StatusFilter(title: String, private val status: List<Pair<String, String>>) : + Filter.Select<String>(title, status.map { it.first }.toTypedArray()) { + fun getValue() = status[state].second + } + + private fun getRatingList() = listOf( + Pair("All", ""), + Pair("5 Star", "5"), + Pair("4 Star", "4"), + Pair("3 Star", "3"), + Pair("2 Star", "2"), + ) + private class RatingFilter(title: String, private val ratings: List<Pair<String, String>>) : + Filter.Select<String>(title, ratings.map { it.first }.toTypedArray()) { + fun getValue() = ratings[state].second + } + + override fun getFilterList(): FilterList { + if (preference.useAppApi) { + return FilterList(Filter.Header("Not supported when using App API")) + } + + val filters: MutableList<Filter<*>> = mutableListOf( + AuthorFilter("Author"), + StatusFilter("Status", getStatusList()), + RatingFilter("Rating", getRatingList()), + ) + + filters += if (genresList.isNotEmpty()) { + listOf( + GenreFilter("Genres", genresList), + ) + } else { + listOf( + Filter.Separator(), + Filter.Header("Press 'Reset' to attempt to show the genres"), + ) + } + + return FilterList(filters) + } + + private var fetchGenresAttempts = 0 + private var fetchGenresFailed = false + private var genresList: List<Pair<String, String>> = emptyList() + + private fun fetchGenres(document: Document? = null) { + if (fetchGenresAttempts < 3 && (genresList.isEmpty() || fetchGenresFailed) && !preference.useAppApi) { + val genres = runCatching { + if (document == null) { + client.newCall(genresRequest()).execute() + .use { parseGenres(it.asJsoup()) } + } else { + parseGenres(document) + } + } + + fetchGenresFailed = genres.isFailure + genresList = genres.getOrNull().orEmpty() + fetchGenresAttempts++ + } + } + + private fun genresRequest(): Request { + return GET("$baseUrl/search/", headers) + } + + private fun parseGenres(document: Document): List<Pair<String, String>> { + return document.selectFirst(".category-list") + ?.select(".category-id-item") + .orEmpty() + .map { div -> + Pair( + div.attr("title"), + div.attr("cate_id"), + ) + } + } + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.selectFirst("h1.bookinfo-title")!!.text() + description = document.selectFirst("div.bk-summary-txt")?.text() + genre = document.select(".bookinfo-category-list a").joinToString { it.text() } + author = document.selectFirst(".bookinfo-author > a")?.attr("title") + thumbnail_url = document.selectFirst(".bookinfo-pic-img")?.attr("abs:src") + status = document.select(".bookinfo-category-list a").first()?.text().parseStatus() + } + + private fun String?.parseStatus(): Int { + this ?: return SManga.UNKNOWN + return when { + this.lowercase() in completedStatusList -> SManga.COMPLETED + this.lowercase() in ongoingStatusList -> SManga.ONGOING + else -> SManga.UNKNOWN + } + } + + override fun chapterListSelector() = ".chapter-item-list a" + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + setUrlWithoutDomain(element.attr("href")) + name = element.attr("title") + date_upload = element.select(".chapter-item-time").text().parseDate() + } + + private fun String.parseDate(): Long { + return runCatching { DATE_FORMATTER.parse(this)?.time } + .getOrNull() ?: 0L + } + + override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { + return pageClient.newCall(pageListRequest(chapter)) + .asObservableSuccess() + .map(::pageListParse) + } + + override fun pageListRequest(chapter: SChapter): Request { + return super.pageListRequest(chapter).newBuilder() + .addHeader("Referer", baseUrl) + .build() + } + + override fun pageListParse(document: Document): List<Page> { + val script = document.select("script:containsData(all_imgs_url)").html() + + val images = imgRegex.find(script)?.groupValues?.get(1) + ?.let { json.decodeFromString<List<String>>("[$it]") } + ?: return singlePageParse(document) + + return images.mapIndexed { idx, img -> + Page(idx, "", img) + } + } + + private fun singlePageParse(document: Document): List<Page> { + return document.selectFirst(".mangaread-pagenav > .sl-page")?.select("option") + ?.mapIndexed { idx, page -> + Page(idx, page.attr("value")) + } ?: emptyList() + } + + override fun imageUrlParse(document: Document): String { + return document.select(".mangaread-manga-pic").attr("src") + } + + private fun Elements.imgAttr(): String { + return when { + hasAttr("lazy_url") -> attr("abs:lazy_url") + else -> attr("abs:src") + } + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + SwitchPreferenceCompat(screen.context).apply { + key = PREF_API_SEARCH + title = "Use App API for browse" + summary = "Results may be more reliable" + setDefaultValue(true) + }.also(screen::addPreference) + } + + private val SharedPreferences.useAppApi: Boolean + get() = getBoolean(PREF_API_SEARCH, true) + + private fun jsRedirect(chain: Interceptor.Chain): Response { + val request = chain.request() + val response = chain.proceed(request) + + val document = Jsoup.parse(response.peekBody(Long.MAX_VALUE).string()) + val jsRedirect = document.selectFirst("script:containsData(window.location.href)")?.html() + ?.substringAfter("\"") + ?.substringBefore("\"") + ?: return response + + val requestUrl = response.request.url + + val url = "${requestUrl.scheme}://${requestUrl.host}$jsRedirect".toHttpUrlOrNull() + ?: return response + + response.close() + + val newHeaders = headersBuilder() + .add("Referer", requestUrl.toString()) + .build() + + return chain.proceed( + request.newBuilder() + .url(url) + .headers(newHeaders) + .build(), + ) + } + + private fun commonApiRequest(url: String, page: Int, query: String? = null): Request { + val payload = NovelCoolBrowsePayload( + appId = appId, + lang = siteLang, + query = query, + type = "manga", + page = page.toString(), + size = size.toString(), + secret = appSecret, + ) + + val body = json.encodeToString(payload) + .toRequestBody(JSON_MEDIA_TYPE) + + val apiHeaders = headersBuilder() + .add("Content-Length", body.contentLength().toString()) + .add("Content-Type", body.contentType().toString()) + .build() + + return POST(url, apiHeaders, body) + } + + private fun commonApiResponseParse(response: Response): MangasPage { + runCatching { fetchGenres() } + + val browse = json.decodeFromString<NovelCoolBrowseResponse>(response.body.string()) + + val hasNextPage = browse.list?.size == size + + return browse.list?.map { + SManga.create().apply { + setUrlWithoutDomain(it.url) + title = it.name + thumbnail_url = it.cover + } + }.let { MangasPage(it ?: emptyList(), hasNextPage) } + } + + companion object { + private const val appId = "202201290625004" + private const val appSecret = "c73a8590641781f203660afca1d37ada" + private const val size = 20 + private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() + private val DATE_FORMATTER by lazy { + SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH) + } + private val imgRegex = Regex("""all_imgs_url\s*:\s*\[\s*([^]]*)\s*,\s*]""") + + private const val PREF_API_SEARCH = "pref_use_search_api" + + // copied from Madara + private val completedStatusList: Array<String> = arrayOf( + "completed", + "completo", + "completado", + "concluído", + "concluido", + "finalizado", + "terminé", + "hoàn thành", + ) + + private val ongoingStatusList: Array<String> = arrayOf( + "ongoing", "Продолжается", "updating", "em lançamento", "em lançamento", "em andamento", + "em andamento", "en cours", "ativo", "lançando", "Đang Tiến Hành", "devam ediyor", + "devam ediyor", "in corso", "in arrivo", "en curso", "en curso", "emision", + "curso", "en marcha", "Publicandose", "en emision", + ) + } +} diff --git a/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCoolDto.kt b/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCoolDto.kt new file mode 100644 index 0000000000..ab513d2e6d --- /dev/null +++ b/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCoolDto.kt @@ -0,0 +1,27 @@ +package eu.kanade.tachiyomi.extension.all.novelcool + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class NovelCoolBrowsePayload( + val appId: String, + @SerialName("keyword") val query: String? = null, + val lang: String, + @SerialName("lc_type") val type: String, + val page: String, + @SerialName("page_size") val size: String, + val secret: String, +) + +@Serializable +data class NovelCoolBrowseResponse( + val list: List<Manga>? = emptyList(), +) + +@Serializable +data class Manga( + val url: String, + val name: String, + val cover: String, +) diff --git a/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCoolFactory.kt b/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCoolFactory.kt new file mode 100644 index 0000000000..1417808cd5 --- /dev/null +++ b/src/all/novelcool/src/eu/kanade/tachiyomi/extension/all/novelcool/NovelCoolFactory.kt @@ -0,0 +1,16 @@ +package eu.kanade.tachiyomi.extension.all.novelcool + +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceFactory + +class NovelCoolFactory : SourceFactory { + override fun createSources(): List<Source> = listOf( + NovelCool("https://www.novelcool.com", "en"), + NovelCool("https://es.novelcool.com", "es"), + NovelCool("https://de.novelcool.com", "de"), + NovelCool("https://ru.novelcool.com", "ru"), + NovelCool("https://it.novelcool.com", "it"), + NovelCool("https://br.novelcool.com", "pt-BR", "br"), + NovelCool("https://fr.novelcool.com", "fr"), + ) +} diff --git a/src/all/peppercarrot/AndroidManifest.xml b/src/all/peppercarrot/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/peppercarrot/AndroidManifest.xml +++ b/src/all/peppercarrot/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/photos18/AndroidManifest.xml b/src/all/photos18/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/photos18/AndroidManifest.xml +++ b/src/all/photos18/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/pixiv/AndroidManifest.xml b/src/all/pixiv/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/pixiv/AndroidManifest.xml +++ b/src/all/pixiv/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/pixiv/build.gradle b/src/all/pixiv/build.gradle index 0215f94dbd..dba0fad2dc 100644 --- a/src/all/pixiv/build.gradle +++ b/src/all/pixiv/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Pixiv' pkgNameSuffix = 'all.pixiv' extClass = '.PixivFactory' - extVersionCode = 3 + extVersionCode = 7 isNsfw = true } diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt deleted file mode 100644 index bea80d1ce4..0000000000 --- a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Filters.kt +++ /dev/null @@ -1,41 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.pixiv -import eu.kanade.tachiyomi.source.model.Filter - -internal class FilterType : Filter.Select<String>("Type", values, 2) { - companion object { - val keys = arrayOf("all", "illust", "manga") - val values = arrayOf("All", "Illustrations", "Manga") - } - - val value: String get() = keys[state] -} - -internal class FilterRating : Filter.Select<String>("Rating", values, 0) { - companion object { - val keys = arrayOf("all", "safe", "r18") - val values = arrayOf("All", "All ages", "R-18") - } - - val value: String get() = keys[state] -} - -internal class FilterSearchMode : Filter.Select<String>("Mode", values, 1) { - companion object { - val keys = arrayOf("s_tag", "s_tag_full", "s_tc") - val values = arrayOf("Tags (partial)", "Tags (full)", "Title, description") - } - - val value: String get() = keys[state] -} - -internal class FilterOrder : Filter.Sort("Order", arrayOf("Date posted")) { - val value: String get() = if (state?.ascending == true) "date" else "date_d" -} - -internal class FilterDateBefore : Filter.Text("Posted before") { - val value: String? get() = state.ifEmpty { null } -} - -internal class FilterDateAfter : Filter.Text("Posted after") { - val value: String? get() = state.ifEmpty { null } -} diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt index 81cfa939d0..98a92ba10a 100644 --- a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Pixiv.kt @@ -1,216 +1,398 @@ package eu.kanade.tachiyomi.extension.all.pixiv -import android.util.LruCache -import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.online.HttpSource import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import okhttp3.Headers +import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request import okhttp3.Response import org.jsoup.Jsoup import rx.Observable import uy.kohesive.injekt.injectLazy -import java.net.URLEncoder -import java.text.SimpleDateFormat -import java.util.Locale class Pixiv(override val lang: String) : HttpSource() { override val name = "Pixiv" override val baseUrl = "https://www.pixiv.net" override val supportsLatest = true - private val siteLang: String = if (lang == "all") "ja" else lang - private val illustCache by lazy { LruCache<String, PixivIllust>(50) } - private val json: Json by injectLazy() - private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH) } - - override fun headersBuilder() = super.headersBuilder() - .add("Referer", "$baseUrl/") - .add("Accept-Language", siteLang) - - private fun apiRequest(method: String, path: String, params: Map<String, String> = emptyMap()) = Request( - url = baseUrl.toHttpUrl().newBuilder() - .addEncodedPathSegments("ajax$path") - .addEncodedQueryParameter("lang", siteLang) - .apply { params.forEach { (k, v) -> addEncodedQueryParameter(k, v) } } - .build(), - - headers = headersBuilder().add("Accept", "application/json").build(), - method = method, - ) - - private inline fun <reified T> apiResponseParse(response: Response): T { - if (!response.isSuccessful) { - throw Exception(response.message) + + override fun headersBuilder(): Headers.Builder = + super.headersBuilder().add("Referer", "$baseUrl/") + + private open inner class HttpCall(href: String?) { + val url: HttpUrl.Builder = baseUrl.toHttpUrl() + .run { href?.let { newBuilder(it)!! } ?: newBuilder() } + + val request: Request.Builder = Request.Builder() + .headers(headersBuilder().build()) + + fun execute(): Response = + client.newCall(request.url(url.build()).build()).execute() + } + + private inner class ApiCall(href: String?) : HttpCall(href) { + init { + url.addEncodedQueryParameter("lang", lang) + request.addHeader("Accept", "application/json") } - return response.body.string() - .let { json.decodeFromString<PixivApiResponse<T>>(it) } - .apply { if (error) throw Exception(message ?: response.message) } - .let { it.body!! } + inline fun <reified T> executeApi(): T = + json.decodeFromString<PixivApiResponse<T>>(execute().body.string()).body!! } - private fun illustUrlToId(url: String): String = - url.substringAfterLast("/") + private var popularMangaNextPage = 1 + private lateinit var popularMangaIterator: Iterator<SManga> + + override fun fetchPopularManga(page: Int): Observable<MangasPage> { + if (page == 1) { + popularMangaIterator = sequence { + val call = ApiCall("/touch/ajax/ranking/illust?mode=daily&type=manga") + + for (p in countUp(start = 1)) { + call.url.setEncodedQueryParameter("page", p.toString()) - private fun urlEncode(string: String): String = - URLEncoder.encode(string, "UTF-8").replace("+", "%20") + val entries = call.executeApi<PixivRankings>().ranking!! + if (entries.isEmpty()) break - private fun parseTimestamp(string: String) = - runCatching { dateFormat.parse(string)?.time!! }.getOrDefault(0) + val call = ApiCall("/touch/ajax/illust/details/many") + entries.forEach { call.url.addEncodedQueryParameter("illust_ids[]", it.illustId!!) } + + call.executeApi<PixivIllustsDetails>().illust_details!!.forEach { yield(it) } + } + } + .toSManga() + .iterator() - private fun parseSearchResult(result: PixivSearchResult) = SManga.create().apply { - url = "/artworks/${result.id!!}" - title = result.title ?: "" - thumbnail_url = result.url + popularMangaNextPage = 2 + } else { + require(page == popularMangaNextPage++) + } + + val mangas = popularMangaIterator.truncateToList(50) + return Observable.just(MangasPage(mangas, hasNextPage = mangas.isNotEmpty())) } - private fun fetchIllust(url: String): Observable<PixivIllust> = - Observable.fromCallable { illustCache.get(url) }.filter { it != null }.switchIfEmpty( - Observable.defer { - client.newCall(illustRequest(url)).asObservable() - .map { illustParse(it) } - .doOnNext { illustCache.put(url, it) } - }, - ) + private var searchNextPage = 1 + private var searchHash: Int? = null + private lateinit var searchIterator: Iterator<SManga> + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + val filters = filters.list as PixivFilters + val hash = Pair(query, filters).hashCode() + + if (hash != searchHash || page == 1) { + searchHash = hash + + lateinit var searchSequence: Sequence<PixivIllust> + lateinit var predicates: List<(PixivIllust) -> Boolean> + + if (query.isNotBlank()) { + searchSequence = makeIllustSearchSequence( + word = query, + order = filters.order, + mode = filters.rating, + sMode = "s_tc", + type = filters.type, + dateBefore = filters.dateBefore.ifBlank { null }, + dateAfter = filters.dateAfter.ifBlank { null }, + ) + + predicates = buildList { + filters.makeTagsPredicate()?.let(::add) + filters.makeUsersPredicate()?.let(::add) + } + } else if (filters.users.isNotBlank()) { + searchSequence = makeUserIllustSearchSequence( + nick = filters.users, + type = filters.type, + ) + + predicates = buildList { + filters.makeTagsPredicate()?.let(::add) + filters.makeRatingPredicate()?.let(::add) + } + } else { + searchSequence = makeIllustSearchSequence( + word = filters.tags.ifBlank { "漫画" }, + order = filters.order, + mode = filters.rating, + sMode = filters.searchMode, + type = filters.type, + dateBefore = filters.dateBefore.ifBlank { null }, + dateAfter = filters.dateAfter.ifBlank { null }, + ) + + predicates = emptyList() + } + + if (predicates.isNotEmpty()) { + searchSequence = searchSequence.filter { predicates.all { p -> p(it) } } + } - private fun illustRequest(url: String): Request = - apiRequest("GET", "/illust/${illustUrlToId(url)}") + searchIterator = searchSequence.toSManga().iterator() + searchNextPage = 2 + } else { + require(page == searchNextPage++) + } - private fun illustParse(response: Response): PixivIllust = - apiResponseParse(response) + val mangas = searchIterator.truncateToList(50).toList() + return Observable.just(MangasPage(mangas, hasNextPage = mangas.isNotEmpty())) + } - override fun popularMangaRequest(page: Int): Request = - searchMangaRequest(page, "", FilterList()) - - override fun popularMangaParse(response: Response) = MangasPage( - mangas = apiResponseParse<PixivSearchResults>(response) - .popular?.run { recent.orEmpty() + permanent.orEmpty() } - ?.map(::parseSearchResult) - .orEmpty(), - - hasNextPage = false, - ) - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val word = urlEncode(query.ifBlank { "漫画" }) - - val parameters = mutableMapOf( - "word" to query, - "order" to "date_d", - "mode" to "all", - "p" to page.toString(), - "s_mode" to "s_tag_full", - "type" to "manga", - ) - - filters.forEach { filter -> - when (filter) { - is FilterType -> parameters["type"] = filter.value - is FilterRating -> parameters["mode"] = filter.value - is FilterSearchMode -> parameters["s_mode"] = filter.value - is FilterOrder -> parameters["order"] = filter.value - is FilterDateBefore -> filter.value?.let { parameters["ecd"] = it } - is FilterDateAfter -> filter.value?.let { parameters["scd"] = it } - else -> {} + private fun makeIllustSearchSequence( + word: String, + sMode: String, + order: String?, + mode: String?, + type: String?, + dateBefore: String?, + dateAfter: String?, + ) = sequence<PixivIllust> { + val call = ApiCall("/touch/ajax/search/illusts") + + call.url.addQueryParameter("word", word) + call.url.addEncodedQueryParameter("s_mode", sMode) + type?.let { call.url.addEncodedQueryParameter("type", it) } + order?.let { call.url.addEncodedQueryParameter("order", it) } + mode?.let { call.url.addEncodedQueryParameter("mode", it) } + dateBefore?.let { call.url.addEncodedQueryParameter("ecd", it) } + dateAfter?.let { call.url.addEncodedQueryParameter("scd", it) } + + for (p in countUp(start = 1)) { + call.url.setEncodedQueryParameter("p", p.toString()) + + val illusts = call.executeApi<PixivResults>().illusts!! + if (illusts.isEmpty()) break + + for (illust in illusts) { + if (illust.is_ad_container == 1) continue + if (illust.type == "2") continue + + yield(illust) } } + } + + private fun makeUserIllustSearchSequence(nick: String, type: String?) = sequence<PixivIllust> { + val searchUsers = HttpCall("/search_user.php?s_mode=s_usr") + .apply { url.addQueryParameter("nick", nick) } - val endpoint = when (parameters["type"]) { - "all" -> "artworks" - "illust" -> "illustrations" - "manga" -> "manga" - else -> "" + val fetchUserIllusts = ApiCall("/touch/ajax/user/illusts") + .apply { type?.let { url.setEncodedQueryParameter("type", it) } } + + for (p in countUp(start = 1)) { + searchUsers.url.setEncodedQueryParameter("p", p.toString()) + + val userIds = Jsoup.parse(searchUsers.execute().body.string()) + .select(".user-recommendation-item > a").eachAttr("href") + .map { it.substringAfterLast('/') } + + if (userIds.isEmpty()) break + + for (userId in userIds) { + fetchUserIllusts.url.setEncodedQueryParameter("id", userId) + + for (p in countUp(start = 1)) { + fetchUserIllusts.url.setEncodedQueryParameter("p", p.toString()) + + val illusts = fetchUserIllusts.executeApi<PixivResults>().illusts!! + if (illusts.isEmpty()) break + + yieldAll(illusts) + } + } } + } - return apiRequest("GET", "/search/$endpoint/$word", parameters) + override fun getFilterList() = FilterList(PixivFilters()) + + private fun Sequence<PixivIllust>.toSManga() = sequence<SManga> { + val seriesIdsSeen = mutableSetOf<String>() + + forEach { illust -> + val series = illust.series + + if (series == null) { + val manga = SManga.create() + manga.setUrlWithoutDomain("/artworks/${illust.id!!}") + manga.title = illust.title ?: "(null)" + manga.thumbnail_url = illust.url + yield(manga) + } else if (seriesIdsSeen.add(series.id!!)) { + val manga = SManga.create() + manga.setUrlWithoutDomain("/user/${series.userId!!}/series/${series.id}") + manga.title = series.title ?: "(null)" + manga.thumbnail_url = series.coverImage ?: illust.url + yield(manga) + } + } } - override fun searchMangaParse(response: Response): MangasPage { - val mangas = apiResponseParse<PixivSearchResults>(response) - .run { illustManga ?: illust ?: manga }?.data - ?.filter { it.isAdContainer != true } - ?.map(::parseSearchResult) - .orEmpty() + private var latestMangaNextPage = 1 + private lateinit var latestMangaIterator: Iterator<SManga> - return MangasPage(mangas, hasNextPage = mangas.isNotEmpty()) + override fun fetchLatestUpdates(page: Int): Observable<MangasPage> { + if (page == 1) { + latestMangaIterator = sequence { + val call = ApiCall("/touch/ajax/latest?type=manga") + + for (p in countUp(start = 1)) { + call.url.setEncodedQueryParameter("p", p.toString()) + + val illusts = call.executeApi<PixivResults>().illusts!! + if (illusts.isEmpty()) break + + for (illust in illusts) { + if (illust.is_ad_container == 1) continue + yield(illust) + } + } + } + .toSManga() + .iterator() + + latestMangaNextPage = 2 + } else { + require(page == latestMangaNextPage++) + } + + val mangas = latestMangaIterator.truncateToList(50).toList() + return Observable.just(MangasPage(mangas, hasNextPage = mangas.isNotEmpty())) } - override fun latestUpdatesRequest(page: Int): Request = - searchMangaRequest(page, "", FilterList()) + private val getIllustCached by lazy { + lruCached<String, PixivIllust>(25) { illustId -> + val call = ApiCall("/touch/ajax/illust/details?illust_id=$illustId") + return@lruCached call.executeApi<PixivIllustDetails>().illust_details!! + } + } - override fun latestUpdatesParse(response: Response): MangasPage = - searchMangaParse(response) - - override fun mangaDetailsRequest(manga: SManga): Request = - illustRequest(manga.url) - - override fun mangaDetailsParse(response: Response) = SManga.create().apply { - val illust = illustParse(response) - - url = "/artworks/${illust.id!!}" - title = illust.title ?: "" - artist = illust.userName - author = illust.userName - description = illust.description?.let { Jsoup.parseBodyFragment(it).wholeText() } - genre = illust.tags?.tags?.mapNotNull { it.tag }?.joinToString() - thumbnail_url = illust.urls?.thumb - update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + private val getSeriesIllustsCached by lazy { + lruCached<String, List<PixivIllust>>(25) { seriesId -> + val call = ApiCall("/touch/ajax/illust/series_content/$seriesId") + var lastOrder = 0 + + return@lruCached buildList { + while (true) { + call.url.setEncodedQueryParameter("last_order", lastOrder.toString()) + + val illusts = call.executeApi<PixivSeriesContents>().series_contents!! + if (illusts.isEmpty()) break + + addAll(illusts) + lastOrder += illusts.size + } + } + } } - override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> = - fetchIllust(manga.url).map { illust -> - listOf( - SChapter.create().apply { - url = manga.url - name = "Oneshot" - date_upload = illust.uploadDate?.let(::parseTimestamp) ?: 0 - chapter_number = 0F - }, - ) + override fun fetchMangaDetails(manga: SManga): Observable<SManga> { + val (id, isSeries) = parseSMangaUrl(manga.url) + + if (isSeries) { + val series = ApiCall("/touch/ajax/illust/series/$id").executeApi<PixivSeries>() + val illusts = getSeriesIllustsCached(id) + + if (series.id != null && series.userId != null) { + manga.setUrlWithoutDomain("/user/${series.userId}/series/${series.id}") + } + + series.title?.let { manga.title = it } + series.caption?.let { manga.description = it } + + illusts.firstOrNull()?.author_details?.user_name?.let { + manga.artist = it + manga.author = it + } + + val tags = illusts.flatMap { it.tags ?: emptyList() }.toSet() + if (tags.isNotEmpty()) manga.genre = tags.joinToString() + + (series.coverImage ?: illusts.firstOrNull()?.url)?.let { manga.thumbnail_url = it } + } else { + val illust = getIllustCached(id) + + illust.id?.let { manga.setUrlWithoutDomain("/artworks/$it") } + illust.title?.let { manga.title = it } + + illust.author_details?.user_name?.let { + manga.artist = it + manga.author = it + } + + illust.comment?.let { manga.description = it } + illust.tags?.let { manga.genre = it.joinToString() } + illust.url?.let { manga.thumbnail_url = it } } - override fun chapterListRequest(manga: SManga): Request = - throw IllegalStateException("Not used") + return Observable.just(manga) + } + + override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { + val (id, isSeries) = parseSMangaUrl(manga.url) + + val illusts = when (isSeries) { + true -> getSeriesIllustsCached(id) + false -> listOf(getIllustCached(id)) + } + + val chapters = illusts.mapIndexed { i, illust -> + SChapter.create().apply { + setUrlWithoutDomain("/artworks/${illust.id!!}") + name = illust.title ?: "(null)" + date_upload = (illust.upload_timestamp ?: 0) * 1000 + chapter_number = i.toFloat() + } + } + + return Observable.just(chapters) + } + + override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { + val illustId = chapter.url.substringAfterLast('/') + + val pages = ApiCall("/ajax/illust/$illustId/pages") + .executeApi<List<PixivIllustPage>>() + .mapIndexed { i, it -> Page(i, chapter.url, it.urls!!.original!!) } + + return Observable.just(pages) + } override fun chapterListParse(response: Response): List<SChapter> = - throw IllegalStateException("Not used") + throw UnsupportedOperationException("Not used.") - override fun pageListRequest(chapter: SChapter): Request = - apiRequest("GET", "/illust/${illustUrlToId(chapter.url)}/pages") + override fun imageUrlParse(response: Response): String = + throw UnsupportedOperationException("Not used.") + + override fun latestUpdatesParse(response: Response): MangasPage = + throw UnsupportedOperationException("Not used.") + + override fun latestUpdatesRequest(page: Int): Request = + throw UnsupportedOperationException("Not used.") + + override fun mangaDetailsParse(response: Response): SManga = + throw UnsupportedOperationException("Not used.") override fun pageListParse(response: Response): List<Page> = - apiResponseParse<List<PixivPage>>(response) - .mapIndexed { i, it -> Page(index = i, imageUrl = it.urls?.original) } + throw UnsupportedOperationException("Not used.") - override fun imageUrlRequest(page: Page): Request = - throw IllegalStateException("Not used") + override fun popularMangaParse(response: Response): MangasPage = + throw UnsupportedOperationException("Not used.") - override fun imageUrlParse(response: Response): String = - throw IllegalStateException("Not used") - - override fun getMangaUrl(manga: SManga): String = - baseUrl + manga.url - - override fun getChapterUrl(chapter: SChapter): String = - baseUrl + chapter.url - - override fun getFilterList() = FilterList( - listOf( - FilterType(), - FilterRating(), - FilterSearchMode(), - FilterOrder(), - FilterDateBefore(), - FilterDateAfter(), - ), - ) + override fun popularMangaRequest(page: Int): Request = + throw UnsupportedOperationException("Not used.") + + override fun searchMangaParse(response: Response): MangasPage = + throw UnsupportedOperationException("Not used.") + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = + throw UnsupportedOperationException("Not used.") } diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFactory.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFactory.kt index 578b73272f..e4b10cec25 100644 --- a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFactory.kt +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFactory.kt @@ -5,5 +5,5 @@ import eu.kanade.tachiyomi.source.SourceFactory class PixivFactory : SourceFactory { override fun createSources(): List<Source> = - listOf("all", "ja", "en", "ko", "zh").map { lang -> Pixiv(lang) } + listOf("ja", "en", "ko", "zh").map { lang -> Pixiv(lang) } } diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFilters.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFilters.kt new file mode 100644 index 0000000000..0e14013f5b --- /dev/null +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivFilters.kt @@ -0,0 +1,61 @@ +package eu.kanade.tachiyomi.extension.all.pixiv +import eu.kanade.tachiyomi.source.model.Filter + +private val TYPE_VALUES = arrayOf("All", "Illustrations", "Manga") +private val TYPE_PARAMS = arrayOf(null, "illust", "manga") + +private val TAGS_MODE_VALUES = arrayOf("Partial", "Full") +private val TAGS_MODE_PARAMS = arrayOf("s_tag", "s_tag_full") + +private val RATING_VALUES = arrayOf("All", "All ages", "R-18") +private val RATING_PARAMS = arrayOf(null, "all", "r18") + +private val RATING_PREDICATES: Array<((PixivIllust) -> Boolean)?> = + arrayOf(null, { it.x_restrict == "0" }, { it.x_restrict == "1" }) + +internal class PixivFilters : MutableList<Filter<*>> by mutableListOf() { + private val typeFilter = object : Filter.Select<String>("Type", TYPE_VALUES, 2) {}.also(::add) + private val tagsFilter = object : Filter.Text("Tags") {}.also(::add) + private val tagsModeFilter = object : Filter.Select<String>("Tags mode", TAGS_MODE_VALUES, 0) {}.also(::add) + private val usersFilter = object : Filter.Text("Users") {}.also(::add) + private val ratingFilter = object : Filter.Select<String>("Rating", RATING_VALUES, 0) {}.also(::add) + + init { add(Filter.Header("(the following are ignored when the users filter is in use)")) } + + private val orderFilter = object : Filter.Sort("Order", arrayOf("Date posted")) {}.also(::add) + private val dateBeforeFilter = object : Filter.Text("Posted before") {}.also(::add) + private val dateAfterFilter = object : Filter.Text("Posted after") {}.also(::add) + + val type: String? get() = TYPE_PARAMS[typeFilter.state] + + val tags: String by tagsFilter::state + val searchMode: String get() = TAGS_MODE_PARAMS[tagsModeFilter.state] + + fun makeTagsPredicate(): ((PixivIllust) -> Boolean)? { + val tags = tags.ifBlank { return null }.split(' ') + + if (tagsModeFilter.state == 0) { + val regex = Regex(tags.joinToString("|") { Regex.escape(it) }) + return { it.tags?.any(regex::containsMatchIn) == true } + } else { + return { it.tags?.containsAll(tags) == true } + } + } + + val users: String by usersFilter::state + + fun makeUsersPredicate(): ((PixivIllust) -> Boolean)? { + val users = users.ifBlank { return null } + val regex = Regex(users.split(' ').joinToString("|") { Regex.escape(it) }) + + return { it.author_details?.user_name?.contains(regex) == true } + } + + val rating: String? get() = RATING_PARAMS[ratingFilter.state] + fun makeRatingPredicate() = RATING_PREDICATES[ratingFilter.state] + + val order: String? get() = orderFilter.state?.ascending?.let { "date" } + + val dateBefore: String by dateBeforeFilter::state + val dateAfter: String by dateAfterFilter::state +} diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivTypes.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivTypes.kt new file mode 100644 index 0000000000..9e6bcfcb7f --- /dev/null +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/PixivTypes.kt @@ -0,0 +1,84 @@ +package eu.kanade.tachiyomi.extension.all.pixiv +import kotlinx.serialization.Serializable + +@Serializable +internal data class PixivApiResponse<T>( + val body: T? = null, +) + +@Serializable +internal data class PixivResults( + val illusts: List<PixivIllust>? = null, +) + +@Serializable +internal data class PixivIllust( + val author_details: PixivAuthorDetails? = null, + val comment: String? = null, + val id: String? = null, + val is_ad_container: Int? = null, + val series: PixivSearchResultSeries? = null, + val tags: List<String>? = null, + val title: String? = null, + val type: String? = null, + val upload_timestamp: Long? = null, + val url: String? = null, + val x_restrict: String? = null, +) + +@Serializable +internal data class PixivSearchResultSeries( + val coverImage: String? = null, + val id: String? = null, + val title: String? = null, + val userId: String? = null, +) + +@Serializable +internal data class PixivIllustDetails( + val illust_details: PixivIllust? = null, +) + +@Serializable +internal data class PixivIllustsDetails( + val illust_details: List<PixivIllust>? = null, +) + +@Serializable +internal data class PixivIllustPage( + val urls: PixivIllustPageUrls? = null, +) + +@Serializable +internal data class PixivIllustPageUrls( + val original: String? = null, +) + +@Serializable +internal data class PixivAuthorDetails( + val user_name: String? = null, +) + +@Serializable +internal data class PixivSeries( + val caption: String? = null, + val coverImage: String? = null, + val id: String? = null, + val title: String? = null, + val userId: String? = null, +) + +@Serializable +internal data class PixivSeriesContents( + val series_contents: List<PixivIllust>? = null, +) + +@Serializable +internal data class PixivRankings( + val ranking: List<PixivRankingEntry>? = null, +) + +@Serializable +internal data class PixivRankingEntry( + val illustId: String? = null, +) diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt deleted file mode 100644 index f5dca8fe86..0000000000 --- a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Structures.kt +++ /dev/null @@ -1,69 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.pixiv - -import kotlinx.serialization.Serializable - -@Serializable -internal data class PixivApiResponse<T>( - val error: Boolean, - val body: T? = null, - val message: String? = null, -) - -@Serializable -internal data class PixivIllust( - val id: Int? = null, - val title: String? = null, - val userName: String? = null, - val description: String? = null, - val tags: PixivTags? = null, - val urls: PixivImageUrls? = null, - val uploadDate: String? = null, -) - -@Serializable -internal data class PixivSearchResult( - val id: Int? = null, - val title: String? = null, - val url: String? = null, - val isAdContainer: Boolean? = null, -) - -@Serializable -internal data class PixivTag( - val tag: String? = null, -) - -@Serializable -internal data class PixivTags( - val tags: List<PixivTag>? = null, -) - -@Serializable -internal data class PixivSearchResults( - val illustManga: PixivSearchResultsIllusts? = null, - val illust: PixivSearchResultsIllusts? = null, - val manga: PixivSearchResultsIllusts? = null, - val popular: PixivSearchResultsPopular? = null, -) - -@Serializable -internal data class PixivSearchResultsIllusts( - val data: List<PixivSearchResult>? = null, -) - -@Serializable -internal data class PixivSearchResultsPopular( - val permanent: List<PixivSearchResult>? = null, - val recent: List<PixivSearchResult>? = null, -) - -@Serializable -internal data class PixivPage( - val urls: PixivImageUrls? = null, -) - -@Serializable -internal data class PixivImageUrls( - val original: String? = null, - val thumb: String? = null, -) diff --git a/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Util.kt b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Util.kt new file mode 100644 index 0000000000..1ca14c1c0e --- /dev/null +++ b/src/all/pixiv/src/eu/kanade/tachiyomi/extension/all/pixiv/Util.kt @@ -0,0 +1,28 @@ +package eu.kanade.tachiyomi.extension.all.pixiv + +import android.util.LruCache + +internal fun countUp(start: Int = 0) = sequence<Int> { + yieldAll(start..Int.MAX_VALUE) + throw RuntimeException("Overflow") +} + +internal fun <T> Iterator<T>.truncateToList(count: Int): List<T> = buildList { + repeat(count) { + if (!hasNext()) return@buildList + add(next()) + } +} + +internal fun parseSMangaUrl(url: String): Pair<String, Boolean> { + val isSeries = url.getOrNull(1) != 'a' + return Pair(url.substringAfterLast('/'), isSeries) +} + +internal fun <K, V> lruCached(capacity: Int, compute: (K) -> V): (K) -> V { + val cache = object : LruCache<K, V>(capacity) { + override fun create(key: K): V = compute(key) + } + + return { key -> synchronized(cache) { cache.get(key) } } +} diff --git a/src/all/sandraandwoo/AndroidManifest.xml b/src/all/sandraandwoo/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/sandraandwoo/AndroidManifest.xml +++ b/src/all/sandraandwoo/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/simplycosplay/AndroidManifest.xml b/src/all/simplycosplay/AndroidManifest.xml new file mode 100644 index 0000000000..15f26f0fef --- /dev/null +++ b/src/all/simplycosplay/AndroidManifest.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <application> + <activity + android:name=".all.simplycosplay.SimplyCosplayUrlActivity" + android:excludeFromRecents="true" + android:exported="true" + android:theme="@android:style/Theme.NoDisplay"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data android:host="www.simply-cosplay.com" /> + <data android:host="simply-cosplay.com" /> + <data android:scheme="https" /> + <data android:pathPattern="/gallery/..*" /> + <data android:pathPattern="/image/..*" /> + + </intent-filter> + </activity> + </application> +</manifest> diff --git a/src/all/simplycosplay/build.gradle b/src/all/simplycosplay/build.gradle new file mode 100644 index 0000000000..c99a818f1f --- /dev/null +++ b/src/all/simplycosplay/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Simply Cosplay' + pkgNameSuffix = 'all.simplycosplay' + extClass = '.SimplyCosplay' + extVersionCode = 1 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" \ No newline at end of file diff --git a/src/all/simplycosplay/res/mipmap-hdpi/ic_launcher.png b/src/all/simplycosplay/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..fa27adc26b Binary files /dev/null and b/src/all/simplycosplay/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/all/simplycosplay/res/mipmap-mdpi/ic_launcher.png b/src/all/simplycosplay/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..3935d64588 Binary files /dev/null and b/src/all/simplycosplay/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/all/simplycosplay/res/mipmap-xhdpi/ic_launcher.png b/src/all/simplycosplay/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..23f5ee5a88 Binary files /dev/null and b/src/all/simplycosplay/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/all/simplycosplay/res/mipmap-xxhdpi/ic_launcher.png b/src/all/simplycosplay/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..88bbe4b20d Binary files /dev/null and b/src/all/simplycosplay/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/all/simplycosplay/res/mipmap-xxxhdpi/ic_launcher.png b/src/all/simplycosplay/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..bc9f7fdffa Binary files /dev/null and b/src/all/simplycosplay/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/all/simplycosplay/res/web_hi_res_512.png b/src/all/simplycosplay/res/web_hi_res_512.png new file mode 100644 index 0000000000..2614f6dcd9 Binary files /dev/null and b/src/all/simplycosplay/res/web_hi_res_512.png differ diff --git a/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplay.kt b/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplay.kt new file mode 100644 index 0000000000..9736f28614 --- /dev/null +++ b/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplay.kt @@ -0,0 +1,382 @@ +package eu.kanade.tachiyomi.extension.all.simplycosplay + +import android.app.Application +import android.content.SharedPreferences +import android.util.Log +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy +import java.io.IOException +import java.text.SimpleDateFormat +import java.util.Locale + +class SimplyCosplay : HttpSource(), ConfigurableSource { + + override val name = "Simply Cosplay" + + override val lang = "all" + + override val baseUrl = "https://www.simply-cosplay.com" + + private val apiUrl = "https://api.simply-porn.com/v2".toHttpUrl() + + override val supportsLatest = true + + override val client = network.cloudflareClient.newBuilder() + .addInterceptor(::tokenIntercept) + .rateLimit(2) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", baseUrl) + + private val json: Json by injectLazy() + + private val preference by lazy { + Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) + } + + private fun tokenIntercept(chain: Interceptor.Chain): Response { + val request = chain.request() + + if (request.url.host != apiUrl.host) { + return chain.proceed(request) + } + + val url = request.url.newBuilder() + .setQueryParameter("token", preference.getToken()) + .build() + + val response = chain.proceed( + request.newBuilder() + .url(url) + .build(), + ) + + if (response.isSuccessful.not() && response.code == 403) { + response.close() + + val newToken = fetchNewToken() + + preference.putToken(newToken) + + val newUrl = request.url.newBuilder() + .setQueryParameter("token", newToken) + .build() + + return chain.proceed( + request.newBuilder() + .url(newUrl) + .build(), + ) + } + + return response + } + + private fun fetchNewToken(): String { + val document = client.newCall(GET(baseUrl, headers)).execute().asJsoup() + + val scriptUrl = document.selectFirst("script[src*=main]") + ?.attr("abs:src") + ?: throw IOException(TOKEN_EXCEPTION) + + val scriptContent = client.newCall(GET(scriptUrl, headers)).execute() + .use { it.body.string() } + .replace("\'", "\"") + + return TokenRegex.find(scriptContent)?.groupValues?.get(1) + ?: throw IOException(TOKEN_EXCEPTION) + } + + private fun browseUrlBuilder(endPoint: String, sort: String, page: Int): HttpUrl.Builder { + return apiUrl.newBuilder().apply { + addPathSegment(endPoint) + addQueryParameter("sort", sort) + addQueryParameter("limit", limit.toString()) + addQueryParameter("page", page.toString()) + } + } + + override fun popularMangaRequest(page: Int): Request { + val url = browseUrlBuilder(preference.getDefaultBrowse(), "hot", page) + + return GET(url.build(), headers) + } + + override fun popularMangaParse(response: Response): MangasPage { + runCatching { fetchTags() } + + val result = response.parseAs<browseResponse>() + + val entries = result.data.map(BrowseItem::toSManga) + val hasNextPage = result.data.size >= limit + + return MangasPage(entries, hasNextPage) + } + + override fun latestUpdatesRequest(page: Int): Request { + val url = browseUrlBuilder(preference.getDefaultBrowse(), "new", page) + + return GET(url.build(), headers) + } + + override fun latestUpdatesParse(response: Response) = popularMangaParse(response) + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + return if (query.startsWith(SEARCH_PREFIX)) { + val url = query.substringAfter(SEARCH_PREFIX) + val manga = SManga.create().apply { this.url = url } + fetchMangaDetails(manga).map { + MangasPage(listOf(it), false) + } + } else { + super.fetchSearchManga(page, query, filters) + } + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val sort = filters.filterIsInstance<SortFilter>().firstOrNull()?.getSort() ?: "new" + + val url = browseUrlBuilder("search", sort, page).apply { + if (query.isNotEmpty()) { + addQueryParameter("query", query) + } + filters.map { filter -> + when (filter) { + is TagFilter -> { + filter.getSelected().forEachIndexed { index, tag -> + addQueryParameter( + "filter[tag_names][$index]", + tag.name.replace(" ", "+"), + ) + } + } + is TypeFilter -> { + filter.getValue().let { + if (it.isNotEmpty()) { + addQueryParameter("filter[type][0]", it) + } + } + } + else -> { } + } + } + } + + return GET(url.build(), headers) + } + + override fun searchMangaParse(response: Response) = popularMangaParse(response) + + private var tagList: List<String> = emptyList() + private var tagsFetchAttempt = 0 + private var tagsFetchFailed = false + + private fun fetchTags() { + if (tagsFetchAttempt < 3 && (tagList.isEmpty() || tagsFetchFailed)) { + val tags = runCatching { + client.newCall(tagsRequest()) + .execute().use(::tagsParse) + } + + tagsFetchFailed = tags.isFailure + tagList = tags.getOrElse { + Log.e("SimplyHentaiTags", it.stackTraceToString()) + emptyList() + } + tagsFetchAttempt++ + } + } + + private fun tagsRequest(): Request { + val url = apiUrl.newBuilder() + .addPathSegment("search") + .build() + + return GET(url, headers) + } + + private fun tagsParse(response: Response): List<String> { + val result = response.parseAs<TagsResponse>() + + return result.aggs.tag_names.buckets.map { + it.key.trim() + } + } + + class Tag(name: String) : Filter.CheckBox(name) + + class TagFilter(title: String, tags: List<String>) : + Filter.Group<Tag>(title, tags.map(::Tag)) { + + fun getSelected() = state.filter { it.state } + } + + class TypeFilter(title: String, private val types: List<String>) : + Filter.Select<String>(title, types.toTypedArray()) { + + fun getValue() = types[state].lowercase() + } + + class SortFilter(title: String, private val sorts: List<String>) : + Filter.Select<String>(title, sorts.toTypedArray()) { + + fun getSort() = sorts[state].lowercase() + } + + override fun getFilterList(): FilterList { + val filters: MutableList<Filter<*>> = mutableListOf( + SortFilter("Sort", listOf("New", "Hot")), + TypeFilter("Type", listOf("", "Image", "Gallery")), + ) + + if (tagList.isNotEmpty()) { + filters += TagFilter("Tags", tagList) + } else { + filters += listOf( + Filter.Separator(), + Filter.Header("Press 'Reset' to attempt to show tags"), + ) + } + + return FilterList(filters) + } + + private fun mangaUrlBuilder(dbUrl: String): HttpUrl.Builder { + val pathSegments = dbUrl.split("/") + val type = pathSegments[1] + val slug = pathSegments[3] + + return apiUrl.newBuilder().apply { + addPathSegment(type) + addPathSegments(slug) + } + } + + override fun mangaDetailsRequest(manga: SManga): Request { + val url = mangaUrlBuilder(manga.url) + + return GET(url.build(), headers) + } + + override fun mangaDetailsParse(response: Response): SManga { + val result = response.parseAs<detailsResponse>() + + return result.data.toSManga() + } + + override fun getMangaUrl(manga: SManga) = baseUrl + manga.url + + override fun fetchChapterList(manga: SManga): Observable<List<SChapter>> { + return Observable.just( + listOf( + SChapter.create().apply { + url = manga.url + name = manga.url.split("/")[1].replaceFirstChar { + if (it.isLowerCase()) { + it.titlecase( + Locale.ROOT, + ) + } else { + it.toString() + } + } + date_upload = manga.description?.substringAfterLast("Date: ").parseDate() + }, + ), + ) + } + + override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url + + override fun pageListRequest(chapter: SChapter): Request { + val url = mangaUrlBuilder(chapter.url) + + return GET(url.build(), headers) + } + + override fun pageListParse(response: Response): List<Page> { + val result = response.parseAs<pageResponse>() + + return result.data.images?.mapIndexedNotNull { index, image -> + if (image.urls.url.isNullOrEmpty()) { + null + } else { + Page(index, "", image.urls.url) + } + } + ?: Page(1, "", result.data.preview.urls.url).let(::listOf) + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + ListPreference(screen.context).apply { + key = BROWSE_TYPE_PREF_KEY + title = BROWSE_TYPE_TITLE + entries = arrayOf("Gallery", "Image") + entryValues = arrayOf("gallery", "image") + summary = "%s" + setDefaultValue("gallery") + }.also(screen::addPreference) + } + + private fun SharedPreferences.getDefaultBrowse() = + getString(BROWSE_TYPE_PREF_KEY, "gallery")!! + + private fun SharedPreferences.getToken() = + getString(DEFAULT_TOKEN_PREF, DEFAULT_FALLBACK_TOKEN) ?: DEFAULT_FALLBACK_TOKEN + + private fun SharedPreferences.putToken(token: String) = + edit().putString(DEFAULT_TOKEN_PREF, token).commit() + + private inline fun <reified T> Response.parseAs(): T { + return json.decodeFromString(body.string()) + } + + private fun String?.parseDate(): Long { + return runCatching { dateFormat.parse(this!!)!!.time } + .getOrDefault(0L) + } + + companion object { + private const val limit = 20 + const val SEARCH_PREFIX = "url:" + + private const val DEFAULT_TOKEN_PREF = "default_token_pref" + private const val DEFAULT_FALLBACK_TOKEN = "01730876" + private const val TOKEN_EXCEPTION = "Unable to fetch new Token" + private val TokenRegex = Regex("""token\s*:\s*"([^\"]+)""") + + private val dateFormat by lazy { SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss.SSS", Locale.ENGLISH) } + + private const val BROWSE_TYPE_PREF_KEY = "default_browse_type_key" + private const val BROWSE_TYPE_TITLE = "Default Browse List" + } + + override fun chapterListParse(response: Response) = + throw UnsupportedOperationException("Not implemented") + + override fun imageUrlParse(response: Response) = + throw UnsupportedOperationException("Not implemented") +} diff --git a/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplayDto.kt b/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplayDto.kt new file mode 100644 index 0000000000..9cbe15cdff --- /dev/null +++ b/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplayDto.kt @@ -0,0 +1,116 @@ +package eu.kanade.tachiyomi.extension.all.simplycosplay + +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.model.UpdateStrategy +import kotlinx.serialization.Serializable +import java.util.Locale + +typealias browseResponse = Data<List<BrowseItem>> + +typealias detailsResponse = Data<DetailsResponse> + +typealias pageResponse = Data<PageResponse> + +@Serializable +data class Data<T>(val data: T) + +@Serializable +data class BrowseItem( + val title: String? = null, + val slug: String, + val type: String, + val preview: Images, +) { + fun toSManga() = SManga.create().apply { + title = this@BrowseItem.title ?: "" + url = "/${type.lowercase().trim()}/new/$slug" + thumbnail_url = preview.urls.thumb.url + description = preview.publish_date?.let { "Date: $it" } + } +} + +@Serializable +data class TagsResponse( + val aggs: Agg, +) + +@Serializable +data class Agg( + val tag_names: TagNames, +) + +@Serializable +data class TagNames( + val buckets: List<GenreItem>, +) + +@Serializable +data class GenreItem( + val key: String, +) + +@Serializable +data class DetailsResponse( + val title: String? = null, + val slug: String, + val type: String, + val preview: Images, + val tags: List<Tag>? = emptyList(), + val image_count: Int? = null, +) { + fun toSManga() = SManga.create().apply { + title = this@DetailsResponse.title ?: "" + url = "/${type.lowercase().trim()}/new/$slug" + thumbnail_url = preview.urls.thumb.url + genre = tags?.mapNotNull { it -> + it.name?.trim()?.split(" ")?.let { genre -> + genre.map { + it.replaceFirstChar { char -> + if (char.isLowerCase()) { + char.titlecase( + Locale.ROOT, + ) + } else { + char.toString() + } + } + } + }?.joinToString(" ") + }?.joinToString() + description = buildString { + append("Type: $type\n") + image_count?.let { append("Images: $it\n") } + preview.publish_date?.let { append("Date: $it\n") } + } + update_strategy = UpdateStrategy.ONLY_FETCH_ONCE + status = SManga.COMPLETED + } +} + +@Serializable +data class PageResponse( + val images: List<Images>? = null, + val preview: Images, +) + +@Serializable +data class Images( + val publish_date: String? = null, + val urls: Urls, +) + +@Serializable +data class Urls( + val url: String? = null, + val thumb: Url, +) + +@Serializable +data class Url( + val url: String? = null, +) + +@Serializable +data class Tag( + val name: String? = null, +) diff --git a/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplayUrlActivity.kt b/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplayUrlActivity.kt new file mode 100644 index 0000000000..30bdb1ea8e --- /dev/null +++ b/src/all/simplycosplay/src/eu/kanade/tachiyomi/extension/all/simplycosplay/SimplyCosplayUrlActivity.kt @@ -0,0 +1,33 @@ +package eu.kanade.tachiyomi.extension.all.simplycosplay + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +class SimplyCosplayUrlActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size >= 3) { + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${SimplyCosplay.SEARCH_PREFIX}/${pathSegments[0]}/new/${pathSegments[2]}") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("SimplyCosplayUrlActivit", e.toString()) + } + } else { + Log.e("SimplyCosplayUrlActivit", "could not parse uri from intent $intent") + } + + finish() + exitProcess(0) + } +} diff --git a/src/all/simplyhentai/AndroidManifest.xml b/src/all/simplyhentai/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/simplyhentai/AndroidManifest.xml +++ b/src/all/simplyhentai/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/tachidesk/AndroidManifest.xml b/src/all/tachidesk/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/tachidesk/AndroidManifest.xml +++ b/src/all/tachidesk/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/tachidesk/build.gradle b/src/all/tachidesk/build.gradle index 19b917ad17..0b756ba38d 100644 --- a/src/all/tachidesk/build.gradle +++ b/src/all/tachidesk/build.gradle @@ -6,12 +6,7 @@ ext { extName = 'Suwayomi' pkgNameSuffix = 'all.tachidesk' extClass = '.Tachidesk' - extVersionCode = 10 -} - -dependencies { - implementation 'io.reactivex:rxandroid:1.2.1' - implementation 'io.reactivex:rxjava:1.3.8' + extVersionCode = 11 } apply from: "$rootDir/common.gradle" diff --git a/src/all/tachidesk/src/eu/kanade/tachiyomi/extension/all/tachidesk/Tachidesk.kt b/src/all/tachidesk/src/eu/kanade/tachiyomi/extension/all/tachidesk/Tachidesk.kt index cdcfd5cce6..69beabb0d8 100644 --- a/src/all/tachidesk/src/eu/kanade/tachiyomi/extension/all/tachidesk/Tachidesk.kt +++ b/src/all/tachidesk/src/eu/kanade/tachiyomi/extension/all/tachidesk/Tachidesk.kt @@ -30,7 +30,6 @@ import okhttp3.Response import okhttp3.internal.toImmutableList import rx.Observable import rx.Single -import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -206,7 +205,7 @@ class Tachidesk : ConfigurableSource, UnmeteredSource, HttpSource() { client.newCall(GET("$baseUrl/api/v1/category", headers)).execute() } .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) + .observeOn(Schedulers.io()) .subscribe( { response -> categoryList = try { diff --git a/src/all/tappytoon/AndroidManifest.xml b/src/all/tappytoon/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/tappytoon/AndroidManifest.xml +++ b/src/all/tappytoon/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/thelibraryofohara/AndroidManifest.xml b/src/all/thelibraryofohara/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/thelibraryofohara/AndroidManifest.xml +++ b/src/all/thelibraryofohara/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/toomics/AndroidManifest.xml b/src/all/toomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/toomics/AndroidManifest.xml +++ b/src/all/toomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/vinnieVeritas/AndroidManifest.xml b/src/all/vinnieVeritas/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/vinnieVeritas/AndroidManifest.xml +++ b/src/all/vinnieVeritas/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/xinmeitulu/AndroidManifest.xml b/src/all/xinmeitulu/AndroidManifest.xml index 0ecda378d4..be1cc85c1a 100644 --- a/src/all/xinmeitulu/AndroidManifest.xml +++ b/src/all/xinmeitulu/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -23,4 +22,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/all/xkcd/AndroidManifest.xml b/src/all/xkcd/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/all/xkcd/AndroidManifest.xml +++ b/src/all/xkcd/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/all/yaoimangaonline/AndroidManifest.xml b/src/all/yaoimangaonline/AndroidManifest.xml index 95481c3e28..8072ee00db 100644 --- a/src/all/yaoimangaonline/AndroidManifest.xml +++ b/src/all/yaoimangaonline/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension"/> +<manifest /> diff --git a/src/ar/gmanga/AndroidManifest.xml b/src/ar/gmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ar/gmanga/AndroidManifest.xml +++ b/src/ar/gmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ar/mangaae/AndroidManifest.xml b/src/ar/mangaae/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ar/mangaae/AndroidManifest.xml +++ b/src/ar/mangaae/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ar/remanga/AndroidManifest.xml b/src/ar/remanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ar/remanga/AndroidManifest.xml +++ b/src/ar/remanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ar/shqqaa/AndroidManifest.xml b/src/ar/shqqaa/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ar/shqqaa/AndroidManifest.xml +++ b/src/ar/shqqaa/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ar/teamx/AndroidManifest.xml b/src/ar/teamx/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ar/teamx/AndroidManifest.xml +++ b/src/ar/teamx/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ar/teamx/build.gradle b/src/ar/teamx/build.gradle index 1c639e7abb..38fe9c1cfd 100644 --- a/src/ar/teamx/build.gradle +++ b/src/ar/teamx/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Team X' pkgNameSuffix = 'ar.teamx' extClass = '.TeamX' - extVersionCode = 11 + extVersionCode = 13 } apply from: "$rootDir/common.gradle" diff --git a/src/ar/teamx/src/eu/kanade/tachiyomi/extension/ar/teamx/TeamX.kt b/src/ar/teamx/src/eu/kanade/tachiyomi/extension/ar/teamx/TeamX.kt index 6e11a4b0c2..ba8dcfcd50 100644 --- a/src/ar/teamx/src/eu/kanade/tachiyomi/extension/ar/teamx/TeamX.kt +++ b/src/ar/teamx/src/eu/kanade/tachiyomi/extension/ar/teamx/TeamX.kt @@ -22,7 +22,7 @@ class TeamX : ParsedHttpSource() { override val name = "Team X" - override val baseUrl = "https://team1x1.fun" + override val baseUrl = "https://teamxnovel.com" override val lang = "ar" @@ -190,7 +190,7 @@ class TeamX : ParsedHttpSource() { // Pages override fun pageListParse(document: Document): List<Page> { - return document.select("div.image_list img").mapIndexed { i, img -> + return document.select("div.image_list img[src]").mapIndexed { i, img -> Page(i, "", img.absUrl("src")) } } diff --git a/src/ca/fansubscat/AndroidManifest.xml b/src/ca/fansubscat/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ca/fansubscat/AndroidManifest.xml +++ b/src/ca/fansubscat/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/de/mangatube/AndroidManifest.xml b/src/de/mangatube/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/de/mangatube/AndroidManifest.xml +++ b/src/de/mangatube/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/de/wiemanga/AndroidManifest.xml b/src/de/wiemanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/de/wiemanga/AndroidManifest.xml +++ b/src/de/wiemanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/MangaRok/AndroidManifest.xml b/src/en/MangaRok/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/MangaRok/AndroidManifest.xml +++ b/src/en/MangaRok/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/allanime/AndroidManifest.xml b/src/en/allanime/AndroidManifest.xml index 8ce8c57a68..581bf58ca6 100644 --- a/src/en/allanime/AndroidManifest.xml +++ b/src/en/allanime/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" - xmlns:android="http://schemas.android.com/apk/res/android"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".en.allanime.AllAnimeUrlActivity" @@ -13,15 +12,10 @@ <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> - <data android:host="allanime.to"/> - <data android:host="allanime.co"/> - - <data - android:pathPattern="/manga/..*" - android:scheme="https" /> - <data - android:pathPattern="/read/..*" - android:scheme="https" /> + <data android:host="allanime.ai"/> + <data android:scheme="https"/> + <data android:pathPattern="/manga/..*"/> + <data android:pathPattern="/read/..*"/> </intent-filter> </activity> </application> diff --git a/src/en/allanime/build.gradle b/src/en/allanime/build.gradle index c3a5b0e589..6003d48631 100644 --- a/src/en/allanime/build.gradle +++ b/src/en/allanime/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'AllAnime' pkgNameSuffix = 'en.allanime' extClass = '.AllAnime' - extVersionCode = 5 + extVersionCode = 6 } apply from: "$rootDir/common.gradle" diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt index b13786d0b7..97e530a5a4 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnime.kt @@ -5,7 +5,10 @@ import android.content.SharedPreferences import androidx.preference.ListPreference import androidx.preference.PreferenceScreen import androidx.preference.SwitchPreferenceCompat -import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.buildApiHeaders +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.firstInstanceOrNull +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.parseAs +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.toJsonRequestBody import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.interceptor.rateLimit @@ -16,44 +19,30 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.OkHttpClient +import kotlinx.serialization.json.float import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.text.SimpleDateFormat -import java.util.Locale class AllAnime : ConfigurableSource, HttpSource() { override val name = "AllAnime" + override val baseUrl = "https://allanime.ai" + + private val apiUrl = "https://api.allanime.day/api" + override val lang = "en" override val supportsLatest = true - private val json: Json = Json { - ignoreUnknownKeys = true - explicitNulls = false - encodeDefaults = true - coerceInputValues = true - } - - private val preferences: SharedPreferences = + private val preferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) + } - override val baseUrl = "https://allanime.ai" - - private val apiUrl = "https://api.allanime.day/api" - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() + override val client = network.cloudflareClient.newBuilder() .addInterceptor { chain -> val request = chain.request() val frag = request.url.fragment @@ -80,24 +69,34 @@ class AllAnime : ConfigurableSource, HttpSource() { /* Popular */ override fun popularMangaRequest(page: Int): Request { - val payloadObj = ApiPopularPayload( - size = limit, - dateRange = 0, - page = page, - allowAdult = preferences.allowAdult, + val payload = GraphQL( + PopularVariables( + type = "manga", + size = limit, + dateRange = 0, + page = page, + allowAdult = preferences.allowAdult, + allowUnknown = false, + ), + POPULAR_QUERY, ) - return apiRequest(payloadObj) + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) } override fun popularMangaParse(response: Response): MangasPage { val result = response.parseAs<ApiPopularResponse>() - val titleStyle = preferences.titlePref val mangaList = result.data.popular.mangas - .mapNotNull { it.manga?.toSManga(titleStyle) } + .mapNotNull { it.manga?.toSManga() } + + val hasNextPage = result.data.popular.mangas.size == limit - return MangasPage(mangaList, mangaList.size == limit) + return MangasPage(mangaList, hasNextPage) } /* Latest */ @@ -118,28 +117,41 @@ class AllAnime : ConfigurableSource, HttpSource() { } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val payloadObj = ApiSearchPayload( - query = query, - size = limit, - page = page, - genres = filters.firstInstanceOrNull<GenreFilter>()?.included, - excludeGenres = filters.firstInstanceOrNull<GenreFilter>()?.excluded, - translationType = "sub", - countryOrigin = filters.firstInstanceOrNull<CountryFilter>()?.getValue() ?: "ALL", - allowAdult = preferences.allowAdult, + val payload = GraphQL( + SearchVariables( + search = SearchPayload( + query = query.takeUnless { it.isEmpty() }, + sortBy = filters.firstInstanceOrNull<SortFilter>()?.getValue(), + genres = filters.firstInstanceOrNull<GenreFilter>()?.included, + excludeGenres = filters.firstInstanceOrNull<GenreFilter>()?.excluded, + isManga = true, + allowAdult = preferences.allowAdult, + allowUnknown = false, + ), + size = limit, + page = page, + translationType = "sub", + countryOrigin = filters.firstInstanceOrNull<CountryFilter>()?.getValue() ?: "ALL", + ), + SEARCH_QUERY, ) - return apiRequest(payloadObj) + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) } override fun searchMangaParse(response: Response): MangasPage { val result = response.parseAs<ApiSearchResponse>() - val titleStyle = preferences.titlePref - val mangaList = result.data.mangas.mangas - .map { it.toSManga(titleStyle) } + val mangaList = result.data.mangas.edges + .map(SearchManga::toSManga) - return MangasPage(mangaList, mangaList.size == limit) + val hasNextPage = result.data.mangas.edges.size == limit + + return MangasPage(mangaList, hasNextPage) } override fun getFilterList() = getFilters() @@ -147,15 +159,23 @@ class AllAnime : ConfigurableSource, HttpSource() { /* Details */ override fun mangaDetailsRequest(manga: SManga): Request { val mangaId = manga.url.split("/")[2] - val payloadObj = ApiIDPayload(mangaId, DETAILS_QUERY) - return apiRequest(payloadObj) + val payload = GraphQL( + IDVariables(mangaId), + DETAILS_QUERY, + ) + + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) } override fun mangaDetailsParse(response: Response): SManga { val result = response.parseAs<ApiMangaDetailsResponse>() - return result.data.manga.toSManga(preferences.titlePref) + return result.data.manga.toSManga() } override fun getMangaUrl(manga: SManga): String { @@ -173,44 +193,32 @@ class AllAnime : ConfigurableSource, HttpSource() { override fun chapterListRequest(manga: SManga): Request { val mangaId = manga.url.split("/")[2] - val payloadObj = ApiIDPayload(mangaId, CHAPTERS_QUERY) - return apiRequest(payloadObj) - } + val payload = GraphQL( + ChapterListVariables( + id = "manga@$mangaId", + chapterNumStart = 0f, + chapterNumEnd = 9999f, + ), + CHAPTERS_QUERY, + ) - private fun chapterDetailsRequest(manga: SManga, start: String, end: String): Request { - val mangaId = manga.url.split("/")[2] - val payloadObj = ApiChapterListDetailsPayload(mangaId, start.toFloat(), end.toFloat()) + val requestBody = payload.toJsonRequestBody() - return apiRequest(payloadObj) + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) } private fun chapterListParse(response: Response, manga: SManga): List<SChapter> { val result = response.parseAs<ApiChapterListResponse>() - val chapters = result.data.manga.chapters.sub - ?.sortedBy { it.toFloat() } - ?: return emptyList() - val chapterDetails = client.newCall( - chapterDetailsRequest(manga, chapters.first(), chapters.last()), - ).execute() - .use { - it.parseAs<ApiChapterListDetailsResponse>() - }.data.chapterList - ?.sortedBy { it.chapterNum } + val chapters = result.data.chapterList?.sortedByDescending { it.chapterNum.float } + ?: return emptyList() val mangaUrl = manga.url.substringAfter("/manga/") - return chapterDetails?.zip(chapters)?.map { (details, chapterNum) -> - SChapter.create().apply { - name = "Chapter $chapterNum" - if (!details.title.isNullOrEmpty() && !details.title.contains(numberRegex)) { - name += ": ${details.title}" - } - url = "/read/$mangaUrl/chapter-$chapterNum-sub" - date_upload = details.uploadDates?.sub.parseDate() - } - }?.reversed() ?: emptyList() + return chapters.map { it.toSChapter(mangaUrl) } } override fun chapterListParse(response: Response): List<SChapter> { @@ -222,56 +230,38 @@ class AllAnime : ConfigurableSource, HttpSource() { } /* Pages */ - override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { - return client.newCall(pageListRequest(chapter)) - .asObservableSuccess() - .map { response -> - pageListParse(response, chapter) - } - } - override fun pageListRequest(chapter: SChapter): Request { val chapterUrl = chapter.url.split("/") val mangaId = chapterUrl[2] val chapterNo = chapterUrl[4].split("-")[1] - val payloadObj = ApiPageListPayload( - id = mangaId, - chapterNum = chapterNo, - translationType = "sub", + val payload = GraphQL( + PageListVariables( + id = mangaId, + chapterNum = chapterNo, + translationType = "sub", + ), + PAGE_QUERY, ) - return apiRequest(payloadObj) + val requestBody = payload.toJsonRequestBody() + + val apiHeaders = headersBuilder().buildApiHeaders(requestBody) + + return POST(apiUrl, apiHeaders, requestBody) } - private fun pageListParse(response: Response, chapter: SChapter): List<Page> { - val result = json.decodeFromString<ApiPageListResponse>(response.body.string()) - val pages = result.data.pageList?.serverList?.get(0) ?: return emptyList() - - val imageDomain = if (!pages.serverUrl.isNullOrEmpty()) { - pages.serverUrl.let { server -> - if (server.matches(urlRegex)) { - server - } else { - "https://$server" - } - } - } else { - // in rare cases, the api doesn't return server url - // for that, we try to parse the frontend html to get it - val chapterUrl = getChapterUrl(chapter) - val frontendRequest = GET(chapterUrl, headers) - val url = client.newCall(frontendRequest).execute().use { frontendResponse -> - val document = frontendResponse.asJsoup() - val script = document.select("script:containsData(window.__NUXT__)").firstOrNull() - imageUrlFromPageRegex.matchEntire(script.toString()) - ?.groupValues - ?.getOrNull(1) - ?.replace("\\u002F", "/") - ?.substringBeforeLast(pages.pictureUrls?.first().toString(), "") + override fun pageListParse(response: Response): List<Page> { + val result = response.parseAs<ApiPageListResponse>() + val pages = result.data.pageList?.edges?.get(0) ?: return emptyList() + + val imageDomain = pages.serverUrl?.let { server -> + if (server.matches(urlRegex)) { + server + } else { + "https://$server" } - url?.takeIf { it.isNotEmpty() } ?: return emptyList() - } + } ?: return emptyList() return pages.pictureUrls?.mapIndexed { index, image -> Page( @@ -281,38 +271,10 @@ class AllAnime : ConfigurableSource, HttpSource() { } ?: emptyList() } - override fun pageListParse(response: Response): List<Page> { - throw UnsupportedOperationException("Not used") - } - override fun imageUrlParse(response: Response): String { throw UnsupportedOperationException("Not used") } - /* Helpers */ - private inline fun <reified T> apiRequest(payloadObj: T): Request { - val payload = json.encodeToString(payloadObj) - .toRequestBody(JSON_MEDIA_TYPE) - - val newHeaders = headersBuilder() - .add("Content-Length", payload.contentLength().toString()) - .add("Content-Type", payload.contentType().toString()) - .build() - - return POST(apiUrl, newHeaders, payload) - } - - private inline fun <reified T> Response.parseAs(): T = json.decodeFromString(body.string()) - - private inline fun <reified R> List<*>.firstInstanceOrNull(): R? = - filterIsInstance<R>().firstOrNull() - - private fun String?.parseDate(): Long { - return runCatching { - dateFormat.parse(this!!)!!.time - }.getOrDefault(0L) - } - override fun setupPreferenceScreen(screen: PreferenceScreen) { ListPreference(screen.context).apply { key = IMAGE_QUALITY_PREF @@ -321,27 +283,15 @@ class AllAnime : ConfigurableSource, HttpSource() { entryValues = arrayOf("original", "800", "480") setDefaultValue(IMAGE_QUALITY_PREF_DEFAULT) summary = "Warning: Wp quality servers can be slow and might not work sometimes" - }.let { screen.addPreference(it) } - - ListPreference(screen.context).apply { - key = TITLE_PREF - title = "Preferred Title Style" - entries = arrayOf("Romaji", "English", "Native") - entryValues = arrayOf("romaji", "eng", "native") - setDefaultValue(TITLE_PREF_DEFAULT) - summary = "%s" - }.let { screen.addPreference(it) } + }.also(screen::addPreference) SwitchPreferenceCompat(screen.context).apply { key = SHOW_ADULT_PREF title = "Show Adult Content" setDefaultValue(SHOW_ADULT_PREF_DEFAULT) - }.let { screen.addPreference(it) } + }.also(screen::addPreference) } - private val SharedPreferences.titlePref - get() = getString(TITLE_PREF, TITLE_PREF_DEFAULT) - private val SharedPreferences.allowAdult get() = getBoolean(SHOW_ADULT_PREF, SHOW_ADULT_PREF_DEFAULT) @@ -350,22 +300,11 @@ class AllAnime : ConfigurableSource, HttpSource() { companion object { private const val limit = 20 - private val numberRegex by lazy { Regex("\\d") } - val whitespace by lazy { Regex("\\s+") } - val dateFormat by lazy { - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) - } const val SEARCH_PREFIX = "id:" - const val thumbnail_cdn = "https://wp.youtube-anime.com/aln.youtube-anime.com/" - private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() val urlRegex = Regex("^https?://.*") private const val image_cdn = "https://wp.youtube-anime.com" private val imageQualityRegex = Regex("^https?://(.*)#.*") - val titleSpecialCharactersRegex = Regex("[^a-z\\d]+") - private val imageUrlFromPageRegex = Regex("selectedPicturesServer:\\[\\{.*?url:\"(.*?)\".*?\\}\\]") - private const val TITLE_PREF = "pref_title" - private const val TITLE_PREF_DEFAULT = "romaji" private const val SHOW_ADULT_PREF = "pref_adult" private const val SHOW_ADULT_PREF_DEFAULT = false private const val IMAGE_QUALITY_PREF = "pref_quality" diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt index db5dabcefd..3c6ffd281f 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeDto.kt @@ -1,45 +1,53 @@ package eu.kanade.tachiyomi.extension.en.allanime +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.parseDate +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.parseDescription +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.parseStatus +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.parseThumbnailUrl +import eu.kanade.tachiyomi.extension.en.allanime.AllAnimeHelper.titleToSlug +import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import org.jsoup.Jsoup -import java.util.Locale +import kotlinx.serialization.json.JsonPrimitive + +typealias ApiPopularResponse = Data<PopularData> + +typealias ApiSearchResponse = Data<SearchData> + +typealias ApiMangaDetailsResponse = Data<MangaDetailsData> + +typealias ApiChapterListResponse = Data<ChapterListData> + +typealias ApiPageListResponse = Data<PageListData> @Serializable -data class ApiPopularResponse( - val data: PopularResponseData, -) { - @Serializable - data class PopularResponseData( - @SerialName("queryPopular") val popular: PopularData, - ) { - @Serializable - data class PopularData( - @SerialName("recommendations") val mangas: List<Popular>, - ) { - @Serializable - data class Popular( - @SerialName("anyCard") val manga: SearchManga? = null, - ) - } - } -} +data class Data<T>(val data: T) @Serializable -data class ApiSearchResponse( - val data: SearchResponseData, -) { - @Serializable - data class SearchResponseData( - val mangas: SearchResultMangas, - ) { - @Serializable - data class SearchResultMangas( - @SerialName("edges") val mangas: List<SearchManga>, - ) - } -} +data class Edges<T>(val edges: List<T>) + +// Popular +@Serializable +data class PopularData( + @SerialName("queryPopular") val popular: PopularMangas, +) + +@Serializable +data class PopularMangas( + @SerialName("recommendations") val mangas: List<PopularManga>, +) + +@Serializable +data class PopularManga( + @SerialName("anyCard") val manga: SearchManga? = null, +) + +// Search +@Serializable +data class SearchData( + val mangas: Edges<SearchManga>, +) @Serializable data class SearchManga( @@ -47,164 +55,101 @@ data class SearchManga( val name: String, val thumbnail: String? = null, val englishName: String? = null, - val nativeName: String? = null, ) { - fun toSManga(titleStyle: String?) = SManga.create().apply { - title = titleStyle.preferedName(name, englishName, nativeName) + fun toSManga() = SManga.create().apply { + title = englishName ?: name url = "/manga/$id/${name.titleToSlug()}" thumbnail_url = thumbnail?.parseThumbnailUrl() } } +// Details @Serializable -data class ApiMangaDetailsResponse( - val data: MangaDetailsData, -) { - @Serializable - data class MangaDetailsData( - val manga: Manga, - ) { - @Serializable - data class Manga( - @SerialName("_id") val id: String, - val name: String, - val thumbnail: String? = null, - val description: String? = null, - val authors: List<String>? = emptyList(), - val genres: List<String>? = emptyList(), - val tags: List<String>? = emptyList(), - val status: String? = null, - val altNames: List<String>? = emptyList(), - val englishName: String? = null, - val nativeName: String? = null, - ) { - fun toSManga(titleStyle: String?) = SManga.create().apply { - title = titleStyle.preferedName(name, englishName, nativeName) - url = "/manga/$id/${name.titleToSlug()}" - thumbnail_url = thumbnail?.parseThumbnailUrl() - description = this@Manga.description?.parseDescription() - if (!altNames.isNullOrEmpty()) { - if (description.isNullOrEmpty()) { - description = "Alternative Titles:\n" - } else { - description += "\n\nAlternative Titles:\n" - } - - description += altNames.joinToString("\n") { "• ${it.trim()}" } - } - if (authors?.isNotEmpty() == true) { - author = authors.first().trim() - artist = author - } - genre = "${genres?.joinToString { it.trim() }}, ${tags?.joinToString { it.trim() }}" - status = this@Manga.status.parseStatus() - } - } - } -} +data class MangaDetailsData( + val manga: Manga, +) @Serializable -data class ApiChapterListResponse( - val data: ChapterListData, +data class Manga( + @SerialName("_id") val id: String, + val name: String, + val thumbnail: String? = null, + val description: String? = null, + val authors: List<String>? = emptyList(), + val genres: List<String>? = emptyList(), + val tags: List<String>? = emptyList(), + val status: String? = null, + val altNames: List<String>? = emptyList(), + val englishName: String? = null, ) { - @Serializable - data class ChapterListData( - val manga: ChapterList, - ) { - @Serializable - data class ChapterList( - @SerialName("availableChaptersDetail") val chapters: AvailableChapters, - ) { - @Serializable - data class AvailableChapters( - val sub: List<String>? = null, - ) + fun toSManga() = SManga.create().apply { + title = englishName ?: name + url = "/manga/$id/${name.titleToSlug()}" + thumbnail_url = thumbnail?.parseThumbnailUrl() + description = this@Manga.description?.parseDescription() + if (!altNames.isNullOrEmpty()) { + if (description.isNullOrEmpty()) { + description = "Alternative Titles:\n" + } else { + description += "\n\nAlternative Titles:\n" + } + + description += altNames.joinToString("\n") { "• ${it.trim()}" } + } + if (authors?.isNotEmpty() == true) { + author = authors.first().trim() + artist = author } + genre = ((genres ?: emptyList()) + (tags ?: emptyList())) + .joinToString { it.trim() } + status = this@Manga.status.parseStatus() } } +// chapters details @Serializable -data class ApiChapterListDetailsResponse( - val data: ChapterListData, -) { - @Serializable - data class ChapterListData( - @SerialName("episodeInfos") val chapterList: List<ChapterData>? = emptyList(), - ) { - @Serializable - data class ChapterData( - @SerialName("episodeIdNum") val chapterNum: Float, - @SerialName("notes") val title: String? = null, - val uploadDates: DateDto? = null, - ) { - @Serializable - data class DateDto( - val sub: String? = null, - ) - } - } -} +data class ChapterListData( + @SerialName("episodeInfos") val chapterList: List<ChapterData>? = emptyList(), +) @Serializable -data class ApiPageListResponse( - val data: PageListData, +data class ChapterData( + @SerialName("episodeIdNum") val chapterNum: JsonPrimitive, + @SerialName("notes") val title: String? = null, + val uploadDates: DateDto? = null, ) { - @Serializable - data class PageListData( - @SerialName("chapterPages") val pageList: PageList?, - ) { - @Serializable - data class PageList( - @SerialName("edges") val serverList: List<Servers>?, - ) { - @Serializable - data class Servers( - @SerialName("pictureUrlHead") val serverUrl: String? = null, - val pictureUrls: List<PageUrl>?, - ) { - @Serializable - data class PageUrl( - val url: String, - ) - } + fun toSChapter(mangaUrl: String) = SChapter.create().apply { + name = "Chapter $chapterNum" + if (!title.isNullOrEmpty() && !title.contains(numberRegex)) { + name += ": $title" } + url = "/read/$mangaUrl/chapter-$chapterNum-sub" + date_upload = uploadDates?.sub.parseDate() } -} -fun String.parseThumbnailUrl(): String { - return if (this.matches(AllAnime.urlRegex)) { - this - } else { - "${AllAnime.thumbnail_cdn}$this?w=250" + companion object { + private val numberRegex by lazy { Regex("\\d") } } } -fun String?.parseStatus(): Int { - if (this == null) { - return SManga.UNKNOWN - } - - return when { - this.contains("releasing", true) -> SManga.ONGOING - this.contains("finished", true) -> SManga.COMPLETED - else -> SManga.UNKNOWN - } -} +@Serializable +data class DateDto( + val sub: String? = null, +) -private fun String.titleToSlug() = this.trim() - .lowercase(Locale.US) - .replace(AllAnime.titleSpecialCharactersRegex, "-") +// page lsit +@Serializable +data class PageListData( + @SerialName("chapterPages") val pageList: Edges<Servers>?, +) -private fun String?.preferedName(name: String, englishName: String?, nativeName: String?): String { - return when (this) { - "eng" -> englishName - "native" -> nativeName - else -> name - } ?: name -} +@Serializable +data class Servers( + @SerialName("pictureUrlHead") val serverUrl: String? = null, + val pictureUrls: List<PageUrl>?, +) -private fun String.parseDescription(): String { - return Jsoup.parse( - this.replace("<br>", "br2n"), - ).text().replace("br2n", "\n") -} +@Serializable +data class PageUrl( + val url: String, +) diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt index 60b0eeef08..4dce4326e1 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeFiters.kt @@ -3,91 +3,30 @@ package eu.kanade.tachiyomi.extension.en.allanime import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList -internal class Genre(name: String) : Filter.TriState(name) - -internal class CountryFilter(name: String, private val countries: List<Pair<String, String>>) : - Filter.Select<String>(name, countries.map { it.first }.toTypedArray()) { - fun getValue() = countries[state].second +abstract class SelectFilter(name: String, private val options: List<Pair<String, String>>) : + Filter.Select<String>(name, options.map { it.first }.toTypedArray()) { + fun getValue() = options[state].second.takeUnless { it.isEmpty() } } -internal class GenreFilter(title: String, genres: List<Genre>) : - Filter.Group<Genre>(title, genres) { - val included: List<String> - get() = state.filter { it.isIncluded() }.map { it.name } +internal class SortFilter(name: String, sorts: List<Pair<String, String>>) : SelectFilter(name, sorts) + +internal class CountryFilter(name: String, countries: List<Pair<String, String>>) : SelectFilter(name, countries) + +internal class Genre(name: String) : Filter.TriState(name) + +internal class GenreFilter(title: String, genres: List<String>) : + Filter.Group<Genre>(title, genres.map(::Genre)) { + val included: List<String>? + get() = state.filter { it.isIncluded() }.map { it.name }.takeUnless { it.isEmpty() } - val excluded: List<String> - get() = state.filter { it.isExcluded() }.map { it.name } + val excluded: List<String>? + get() = state.filter { it.isExcluded() }.map { it.name }.takeUnless { it.isEmpty() } } -private val genreList: List<Genre> = listOf( - Genre("4 Koma"), - Genre("Action"), - Genre("Adult"), - Genre("Adventure"), - Genre("Cars"), - Genre("Comedy"), - Genre("Cooking"), - Genre("Crossdressing"), - Genre("Dementia"), - Genre("Demons"), - Genre("Doujinshi"), - Genre("Drama"), - Genre("Ecchi"), - Genre("Fantasy"), - Genre("Game"), - Genre("Gender Bender"), - Genre("Gyaru"), - Genre("Harem"), - Genre("Historical"), - Genre("Horror"), - Genre("Isekai"), - Genre("Josei"), - Genre("Kids"), - Genre("Loli"), - Genre("Magic"), - Genre("Manhua"), - Genre("Manhwa"), - Genre("Martial Arts"), - Genre("Mature"), - Genre("Mecha"), - Genre("Medical"), - Genre("Military"), - Genre("Monster Girls"), - Genre("Music"), - Genre("Mystery"), - Genre("One Shot"), - Genre("Parody"), - Genre("Police"), - Genre("Post Apocalyptic"), - Genre("Psychological"), - Genre("Reincarnation"), - Genre("Reverse Harem"), - Genre("Romance"), - Genre("Samurai"), - Genre("School"), - Genre("Sci-Fi"), - Genre("Seinen"), - Genre("Shota"), - Genre("Shoujo"), - Genre("Shoujo Ai"), - Genre("Shounen"), - Genre("Shounen Ai"), - Genre("Slice of Life"), - Genre("Smut"), - Genre("Space"), - Genre("Sports"), - Genre("Super Power"), - Genre("Supernatural"), - Genre("Suspense"), - Genre("Thriller"), - Genre("Tragedy"), - Genre("Unknown"), - Genre("Vampire"), - Genre("Webtoons"), - Genre("Yaoi"), - Genre("Youkai"), - Genre("Yuri"), - Genre("Zombies"), +private val sortList = listOf( + Pair("Update", ""), + Pair("Name Ascending", "Name_ASC"), + Pair("Name Descending", "Name_DESC"), ) private val countryList: List<Pair<String, String>> = listOf( @@ -97,7 +36,79 @@ private val countryList: List<Pair<String, String>> = listOf( Pair("Korea", "KR"), ) +private val genreList: List<String> = listOf( + "4 Koma", + "Action", + "Adult", + "Adventure", + "Cars", + "Comedy", + "Cooking", + "Crossdressing", + "Dementia", + "Demons", + "Doujinshi", + "Drama", + "Ecchi", + "Fantasy", + "Game", + "Gender Bender", + "Gyaru", + "Harem", + "Historical", + "Horror", + "Isekai", + "Josei", + "Kids", + "Loli", + "Magic", + "Manhua", + "Manhwa", + "Martial Arts", + "Mature", + "Mecha", + "Medical", + "Military", + "Monster Girls", + "Music", + "Mystery", + "One Shot", + "Parody", + "Police", + "Post Apocalyptic", + "Psychological", + "Reincarnation", + "Reverse Harem", + "Romance", + "Samurai", + "School", + "Sci-Fi", + "Seinen", + "Shota", + "Shoujo", + "Shoujo Ai", + "Shounen", + "Shounen Ai", + "Slice of Life", + "Smut", + "Space", + "Sports", + "Super Power", + "Supernatural", + "Suspense", + "Thriller", + "Tragedy", + "Unknown", + "Vampire", + "Webtoons", + "Yaoi", + "Youkai", + "Yuri", + "Zombies", +) + fun getFilters() = FilterList( + SortFilter("Sort", sortList), CountryFilter("Countries", countryList), GenreFilter("Genres", genreList), ) diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeHelper.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeHelper.kt new file mode 100644 index 0000000000..f579c4493b --- /dev/null +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeHelper.kt @@ -0,0 +1,81 @@ +package eu.kanade.tachiyomi.extension.en.allanime + +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import okhttp3.Headers +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import org.jsoup.Jsoup +import java.text.SimpleDateFormat +import java.util.Locale + +object AllAnimeHelper { + + val json: Json = Json { + ignoreUnknownKeys = true + explicitNulls = false + encodeDefaults = true + coerceInputValues = true + } + + fun String.parseThumbnailUrl(): String { + return if (this.matches(AllAnime.urlRegex)) { + this + } else { + "$thumbnail_cdn$this?w=250" + } + } + + fun String?.parseStatus(): Int { + if (this == null) { + return SManga.UNKNOWN + } + + return when { + this.contains("releasing", true) -> SManga.ONGOING + this.contains("finished", true) -> SManga.COMPLETED + else -> SManga.UNKNOWN + } + } + + fun String.titleToSlug() = this.trim() + .lowercase(Locale.US) + .replace(titleSpecialCharactersRegex, "-") + + fun String.parseDescription(): String { + return Jsoup.parse( + this.replace("<br>", "br2n"), + ).text().replace("br2n", "\n") + } + + fun String?.parseDate(): Long { + return runCatching { + dateFormat.parse(this!!)!!.time + }.getOrDefault(0L) + } + + inline fun <reified T> Response.parseAs(): T = json.decodeFromString(body.string()) + + inline fun <reified T> List<*>.firstInstanceOrNull(): T? = + filterIsInstance<T>().firstOrNull() + + inline fun <reified T : Any> T.toJsonRequestBody(): RequestBody = + json.encodeToString(this) + .toRequestBody(JSON_MEDIA_TYPE) + + fun Headers.Builder.buildApiHeaders(requestBody: RequestBody) = this + .add("Content-Length", requestBody.contentLength().toString()) + .add("Content-Type", requestBody.contentType().toString()) + .build() + + private const val thumbnail_cdn = "https://wp.youtube-anime.com/aln.youtube-anime.com/" + private val titleSpecialCharactersRegex by lazy { Regex("[^a-z\\d]+") } + private val dateFormat by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH) + } + val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() +} diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt index c481290a6a..7f53253a13 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimePayloadDto.kt @@ -1,163 +1,59 @@ package eu.kanade.tachiyomi.extension.en.allanime +import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class ApiPopularPayload( - val variables: ApiPopularVariables, +data class GraphQL<T>( + val variables: T, val query: String, -) { - @Serializable - data class ApiPopularVariables( - val type: String, - val size: Int, - val dateRange: Int, - val page: Int, - val allowAdult: Boolean, - val allowUnknown: Boolean, - ) - - constructor( - type: String = "manga", - size: Int, - dateRange: Int, - page: Int, - allowAdult: Boolean = false, - allowUnknown: Boolean = false, - ) : this( - ApiPopularVariables( - type = type, - size = size, - dateRange = dateRange, - page = page, - allowAdult = allowAdult, - allowUnknown = allowUnknown, - ), - POPULAR_QUERY, - ) -} +) @Serializable -data class ApiSearchPayload( - val variables: ApiSearchVariables, - val query: String, -) { - @Serializable - data class ApiSearchVariables( - val search: SearchPayload, - val limit: Int, - val page: Int, - val translationType: String, - val countryOrigin: String, - ) - - @Serializable - data class SearchPayload( - val query: String, - val genres: List<String>?, - val excludeGenres: List<String>?, - val isManga: Boolean, - val allowAdult: Boolean, - val allowUnknown: Boolean, - ) - - constructor( - query: String, - size: Int, - page: Int, - genres: List<String>?, - excludeGenres: List<String>?, - translationType: String, - countryOrigin: String, - isManga: Boolean = true, - allowAdult: Boolean = false, - allowUnknown: Boolean = false, - ) : this( - ApiSearchVariables( - search = SearchPayload( - query = query, - genres = genres, - excludeGenres = excludeGenres, - isManga = isManga, - allowAdult = allowAdult, - allowUnknown = allowUnknown, - ), - limit = size, - page = page, - translationType = translationType, - countryOrigin = countryOrigin, - ), - SEARCH_QUERY, - ) -} +data class PopularVariables( + val type: String, + val size: Int, + val dateRange: Int, + val page: Int, + val allowAdult: Boolean, + val allowUnknown: Boolean, +) @Serializable -data class ApiIDPayload( - val variables: ApiIDVariables, - val query: String, -) { - @Serializable - data class ApiIDVariables( - val id: String, - ) - - constructor( - id: String, - graphqlQuery: String, - ) : this( - ApiIDVariables(id), - graphqlQuery, - ) -} +data class SearchVariables( + val search: SearchPayload, + @SerialName("limit") val size: Int, + val page: Int, + val translationType: String, + val countryOrigin: String, +) @Serializable -data class ApiChapterListDetailsPayload( - val variables: ApiChapterDetailsVariables, - val query: String, -) { - @Serializable - data class ApiChapterDetailsVariables( - val id: String, - val chapterNumStart: Float, - val chapterNumEnd: Float, - ) +data class SearchPayload( + val query: String?, + val sortBy: String?, + val genres: List<String>?, + val excludeGenres: List<String>?, + val isManga: Boolean, + val allowAdult: Boolean, + val allowUnknown: Boolean, +) - constructor( - id: String, - chapterNumStart: Float, - chapterNumEnd: Float, - ) : this( - ApiChapterDetailsVariables( - id = "manga@$id", - chapterNumStart = chapterNumStart, - chapterNumEnd = chapterNumEnd, - ), - CHAPTERS_DETAILS_QUERY, - ) -} +@Serializable +data class IDVariables( + val id: String, +) @Serializable -data class ApiPageListPayload( - val variables: ApiPageListVariables, - val query: String, -) { - @Serializable - data class ApiPageListVariables( - val id: String, - val chapterNum: String, - val translationType: String, - ) +data class ChapterListVariables( + val id: String, + val chapterNumStart: Float, + val chapterNumEnd: Float, +) - constructor( - id: String, - chapterNum: String, - translationType: String, - ) : this( - ApiPageListVariables( - id = id, - chapterNum = chapterNum, - translationType = translationType, - ), - PAGE_QUERY, - ) -} +@Serializable +data class PageListVariables( + val id: String, + val chapterNum: String, + val translationType: String, +) diff --git a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt index 054503b2b2..2128947b75 100644 --- a/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt +++ b/src/en/allanime/src/eu/kanade/tachiyomi/extension/en/allanime/AllAnimeQueries.kt @@ -1,11 +1,8 @@ package eu.kanade.tachiyomi.extension.en.allanime -import eu.kanade.tachiyomi.extension.en.allanime.AllAnime.Companion.whitespace - private fun buildQuery(queryAction: () -> String): String { return queryAction() .trimIndent() - .replace(whitespace, " ") .replace("%", "$") } @@ -33,7 +30,6 @@ val POPULAR_QUERY: String = buildQuery { name thumbnail englishName - nativeName } } } @@ -62,7 +58,6 @@ val SEARCH_QUERY: String = buildQuery { name thumbnail englishName - nativeName } } } @@ -83,23 +78,12 @@ val DETAILS_QUERY: String = buildQuery { status altNames englishName - nativeName } } """ } val CHAPTERS_QUERY: String = buildQuery { - """ - query (%id: String!) { - manga(_id: %id) { - availableChaptersDetail - } - } - """ -} - -val CHAPTERS_DETAILS_QUERY: String = buildQuery { """ query (%id: String!, %chapterNumStart: Float!, %chapterNumEnd: Float!) { episodeInfos( diff --git a/src/en/apairof2/AndroidManifest.xml b/src/en/apairof2/AndroidManifest.xml index 92fcba2325..fa0c911c9a 100644 --- a/src/en/apairof2/AndroidManifest.xml +++ b/src/en/apairof2/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt b/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt index 91f687155b..0d736289bb 100644 --- a/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt +++ b/src/en/apairof2/src/eu/kanade/tachiyomi/extension/en/apairof2/APairOf2.kt @@ -142,7 +142,7 @@ class APairOf2 : ParsedHttpSource() { return document.select(".swiper-slide img").mapIndexed { index, img -> Page( index = index, - imageUrl = img.imgAttr() + imageUrl = img.imgAttr(), ) } } diff --git a/src/en/aurora/AndroidManifest.xml b/src/en/aurora/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/en/aurora/AndroidManifest.xml +++ b/src/en/aurora/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/en/boommanga/AndroidManifest.xml b/src/en/boommanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/boommanga/AndroidManifest.xml +++ b/src/en/boommanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/buttsmithy/AndroidManifest.xml b/src/en/buttsmithy/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/buttsmithy/AndroidManifest.xml +++ b/src/en/buttsmithy/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/clonemanga/AndroidManifest.xml b/src/en/clonemanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/clonemanga/AndroidManifest.xml +++ b/src/en/clonemanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/collectedcurios/AndroidManifest.xml b/src/en/collectedcurios/AndroidManifest.xml new file mode 100644 index 0000000000..8072ee00db --- /dev/null +++ b/src/en/collectedcurios/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest /> diff --git a/src/en/collectedcurios/build.gradle b/src/en/collectedcurios/build.gradle new file mode 100644 index 0000000000..fee1c12a43 --- /dev/null +++ b/src/en/collectedcurios/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Collected Curios' + pkgNameSuffix = 'en.collectedcurios' + extClass = '.Collectedcurios' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/collectedcurios/res/mipmap-hdpi/ic_launcher.png b/src/en/collectedcurios/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..1f05e5105a Binary files /dev/null and b/src/en/collectedcurios/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/collectedcurios/res/mipmap-mdpi/ic_launcher.png b/src/en/collectedcurios/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..d98bb875a9 Binary files /dev/null and b/src/en/collectedcurios/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/collectedcurios/res/mipmap-xhdpi/ic_launcher.png b/src/en/collectedcurios/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..8a9f7dbdfd Binary files /dev/null and b/src/en/collectedcurios/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/collectedcurios/res/mipmap-xxhdpi/ic_launcher.png b/src/en/collectedcurios/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8614ea6015 Binary files /dev/null and b/src/en/collectedcurios/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/collectedcurios/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/collectedcurios/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7a28937344 Binary files /dev/null and b/src/en/collectedcurios/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/collectedcurios/res/web_hi_res_512.png b/src/en/collectedcurios/res/web_hi_res_512.png new file mode 100644 index 0000000000..dd8a674151 Binary files /dev/null and b/src/en/collectedcurios/res/web_hi_res_512.png differ diff --git a/src/en/collectedcurios/src/eu/kanade/tachiyomi/extension/en/collectedcurios/Collectedcurios.kt b/src/en/collectedcurios/src/eu/kanade/tachiyomi/extension/en/collectedcurios/Collectedcurios.kt new file mode 100644 index 0000000000..fac6bf8738 --- /dev/null +++ b/src/en/collectedcurios/src/eu/kanade/tachiyomi/extension/en/collectedcurios/Collectedcurios.kt @@ -0,0 +1,153 @@ +package eu.kanade.tachiyomi.extension.en.collectedcurios + +import android.util.Log +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Request +import okhttp3.Response +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import rx.Observable + +class Collectedcurios : ParsedHttpSource() { + + override val name = "Collected Curios" + + override val baseUrl = "https://www.collectedcurios.com" + + override val lang = "en" + + override val supportsLatest = false + + override fun fetchPopularManga(page: Int): Observable<MangasPage> { + return Observable.just( + MangasPage( + arrayListOf( + SManga.create().apply { + title = "Sequential Art" + artist = "Jolly Jack aka Phillip M Jackson" + author = "Jolly Jack aka Phillip M Jackson" + status = SManga.ONGOING + url = "/sequentialart.php" + description = "Sequential Art webcomic." + thumbnail_url = "https://www.collectedcurios.com/images/CC_2011_Sequential_Art_Button.jpg" + }, + + SManga.create().apply { + title = "Battle Bunnies" + artist = "Jolly Jack aka Phillip M Jackson" + author = "Jolly Jack aka Phillip M Jackson" + status = SManga.ONGOING + url = "/battlebunnies.php" + description = "Battle Bunnies webcomic." + thumbnail_url = "https://www.collectedcurios.com/images/CC_2011_Battle_Bunnies_Button.jpg" + }, + + /* + SManga.create().apply { + title = "Spider and Scorpion" + artist = "Jolly Jack aka Phillip M Jackson" + author = "Jolly Jack aka Phillip M Jackson" + status = SManga.ONGOING + url = "/spiderandscorpion.php" + description = "Spider and Scorpion webcomic." + thumbnail_url = "https://www.collectedcurios.com/images/CC_2011_Spider_And_Scorpion_Button.jpg" + }, + */ + ), + false, + ), + ) + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + return fetchPopularManga(1) + } + + override fun fetchMangaDetails(manga: SManga): Observable<SManga> { + return Observable.just(manga) + } + + override fun chapterListParse(response: Response): List<SChapter> { + val responseJs = response.asJsoup() + + val chapters = + if (responseJs.selectFirst("img[title=Last]") == null) { + responseJs.selectFirst("input[title=Jump to number]") + ?.attr("value")?.toInt() + } else { + responseJs.selectFirst("img[title=Last]")?.parent() + ?.attr("href")?.substringAfter("=")?.toInt() + } + + var chapterList = mutableListOf<SChapter>() + + for (i in chapters?.downTo(1)!!) { + chapterList.add( + SChapter.create().apply { + url = "${response.request.url}?s=$i" + name = "Chapter - $i" + chapter_number = i.toFloat() + date_upload = 0L + }, + ) + } + return chapterList + } + + override fun chapterListSelector() = throw Exception("Not used") + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + name = element.selectFirst(".w3-round")?.attr("value") ?: "Chapter" + } + + override fun pageListParse(document: Document): List<Page> = throw Exception("Not used") + + override fun fetchPageList(chapter: SChapter) = Observable.just(listOf(Page(0, chapter.url)))!! + + override fun imageUrlParse(response: Response): String { + val url = response.request.url.toString() + val document = response.asJsoup() + + return when { + url.contains("sequentialart") -> + document.selectFirst(".w3-image")!!.absUrl("src") + url.contains("battlebunnies") || url.contains("spiderandscorpion") -> + document.selectFirst("#strip")!!.absUrl("src") + else -> throw Exception("Could not find the image") + } + } + + override fun imageUrlParse(document: Document) = throw Exception("Not used") + + override fun popularMangaSelector(): String = throw Exception("Not used") + + override fun searchMangaFromElement(element: Element): SManga = throw Exception("Not used") + + override fun searchMangaNextPageSelector(): String? = throw Exception("Not used") + + override fun searchMangaSelector(): String = throw Exception("Not used") + + override fun popularMangaRequest(page: Int): Request = throw Exception("Not used") + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = throw Exception("Not used") + + override fun popularMangaNextPageSelector(): String? = throw Exception("Not used") + + override fun popularMangaFromElement(element: Element): SManga = throw Exception("Not used") + + override fun mangaDetailsParse(document: Document): SManga = throw Exception("Not used") + + override fun latestUpdatesNextPageSelector(): String? = throw Exception("Not used") + + override fun latestUpdatesFromElement(element: Element): SManga = throw Exception("Not used") + + override fun latestUpdatesRequest(page: Int): Request = throw Exception("Not used") + + override fun latestUpdatesSelector(): String = throw Exception("Not used") +} diff --git a/src/en/comicastle/AndroidManifest.xml b/src/en/comicastle/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/comicastle/AndroidManifest.xml +++ b/src/en/comicastle/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/comicextra/AndroidManifest.xml b/src/en/comicextra/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/comicextra/AndroidManifest.xml +++ b/src/en/comicextra/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/darklegacycomics/AndroidManifest.xml b/src/en/darklegacycomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/darklegacycomics/AndroidManifest.xml +++ b/src/en/darklegacycomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/digitalcomicmuseum/AndroidManifest.xml b/src/en/digitalcomicmuseum/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/en/digitalcomicmuseum/AndroidManifest.xml +++ b/src/en/digitalcomicmuseum/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/en/disasterscans/AndroidManifest.xml b/src/en/disasterscans/AndroidManifest.xml index dd672cb933..3b3e56a7a2 100644 --- a/src/en/disasterscans/AndroidManifest.xml +++ b/src/en/disasterscans/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -21,4 +20,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/en/doujins/AndroidManifest.xml b/src/en/doujins/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/doujins/AndroidManifest.xml +++ b/src/en/doujins/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/dynasty/AndroidManifest.xml b/src/en/dynasty/AndroidManifest.xml index 794320d441..ef85bdb79a 100644 --- a/src/en/dynasty/AndroidManifest.xml +++ b/src/en/dynasty/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".en.dynasty.DynastyUrlActivity" @@ -35,4 +34,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/en/earlymanga/AndroidManifest.xml b/src/en/earlymanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/earlymanga/AndroidManifest.xml +++ b/src/en/earlymanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/earlymanga/build.gradle b/src/en/earlymanga/build.gradle index db56a167a9..c15395794c 100644 --- a/src/en/earlymanga/build.gradle +++ b/src/en/earlymanga/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'EarlyManga' pkgNameSuffix = 'en.earlymanga' extClass = '.EarlyManga' - extVersionCode = 20 + extVersionCode = 21 } apply from: "$rootDir/common.gradle" diff --git a/src/en/earlymanga/src/eu/kanade/tachiyomi/extension/en/earlymanga/EarlyManga.kt b/src/en/earlymanga/src/eu/kanade/tachiyomi/extension/en/earlymanga/EarlyManga.kt index 6a92558117..59903bb9d5 100644 --- a/src/en/earlymanga/src/eu/kanade/tachiyomi/extension/en/earlymanga/EarlyManga.kt +++ b/src/en/earlymanga/src/eu/kanade/tachiyomi/extension/en/earlymanga/EarlyManga.kt @@ -28,7 +28,7 @@ class EarlyManga : ParsedHttpSource() { override val name = "EarlyManga" - override val baseUrl = "https://v1.earlym.org" + override val baseUrl = "https://earlycomic.com" override val lang = "en" diff --git a/src/en/eggporncomics/AndroidManifest.xml b/src/en/eggporncomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/eggporncomics/AndroidManifest.xml +++ b/src/en/eggporncomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/elanschool/AndroidManifest.xml b/src/en/elanschool/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/elanschool/AndroidManifest.xml +++ b/src/en/elanschool/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/existentialcomics/AndroidManifest.xml b/src/en/existentialcomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/existentialcomics/AndroidManifest.xml +++ b/src/en/existentialcomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/explosm/AndroidManifest.xml b/src/en/explosm/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/explosm/AndroidManifest.xml +++ b/src/en/explosm/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/grrlpower/AndroidManifest.xml b/src/en/grrlpower/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/grrlpower/AndroidManifest.xml +++ b/src/en/grrlpower/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/grrlpower/src/eu/kanade/tachiyomi/extension/en/grrlpower/GrrlPower.kt b/src/en/grrlpower/src/eu/kanade/tachiyomi/extension/en/grrlpower/GrrlPower.kt index 7279ace402..ddda5d2c79 100644 --- a/src/en/grrlpower/src/eu/kanade/tachiyomi/extension/en/grrlpower/GrrlPower.kt +++ b/src/en/grrlpower/src/eu/kanade/tachiyomi/extension/en/grrlpower/GrrlPower.kt @@ -93,8 +93,8 @@ class GrrlPower( name = link.text() setUrlWithoutDomain(link.attr("href")) date_upload = date?.time ?: 0L - // chapter_number isn't set as suggested by arkon - // https://github.com/tachiyomiorg/tachiyomi-extensions/pull/15717#discussion_r1138014748 + // chapter_number isn't set as suggested by arkon + // https://github.com/tachiyomiorg/tachiyomi-extensions/pull/15717#discussion_r1138014748 } } } diff --git a/src/en/gunnerkriggcourt/AndroidManifest.xml b/src/en/gunnerkriggcourt/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/gunnerkriggcourt/AndroidManifest.xml +++ b/src/en/gunnerkriggcourt/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/gwtb/AndroidManifest.xml b/src/en/gwtb/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/gwtb/AndroidManifest.xml +++ b/src/en/gwtb/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/hbrowse/AndroidManifest.xml b/src/en/hbrowse/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/hbrowse/AndroidManifest.xml +++ b/src/en/hbrowse/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/hentai2read/AndroidManifest.xml b/src/en/hentai2read/AndroidManifest.xml index 20a0f3b7fe..0fa9f56ec8 100644 --- a/src/en/hentai2read/AndroidManifest.xml +++ b/src/en/hentai2read/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/hentaidexy/AndroidManifest.xml b/src/en/hentaidexy/AndroidManifest.xml index 8f35f8fe2a..e586b0e240 100644 --- a/src/en/hentaidexy/AndroidManifest.xml +++ b/src/en/hentaidexy/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/hentaifox/AndroidManifest.xml b/src/en/hentaifox/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/hentaifox/AndroidManifest.xml +++ b/src/en/hentaifox/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/hentaihere/AndroidManifest.xml b/src/en/hentaihere/AndroidManifest.xml index 5fd2b3523f..453dec0072 100644 --- a/src/en/hentaihere/AndroidManifest.xml +++ b/src/en/hentaihere/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/hiveworks/AndroidManifest.xml b/src/en/hiveworks/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/hiveworks/AndroidManifest.xml +++ b/src/en/hiveworks/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/honkaiimpact3/AndroidManifest.xml b/src/en/honkaiimpact3/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/honkaiimpact3/AndroidManifest.xml +++ b/src/en/honkaiimpact3/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/irovedout/AndroidManifest.xml b/src/en/irovedout/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/en/irovedout/AndroidManifest.xml +++ b/src/en/irovedout/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/en/keenspot/AndroidManifest.xml b/src/en/keenspot/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/keenspot/AndroidManifest.xml +++ b/src/en/keenspot/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/killsixbilliondemons/AndroidManifest.xml b/src/en/killsixbilliondemons/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/killsixbilliondemons/AndroidManifest.xml +++ b/src/en/killsixbilliondemons/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/kouhaiwork/AndroidManifest.xml b/src/en/kouhaiwork/AndroidManifest.xml index 91abd1c18b..7335b11c44 100644 --- a/src/en/kouhaiwork/AndroidManifest.xml +++ b/src/en/kouhaiwork/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".en.kouhaiwork.KouhaiActivity" android:theme="@android:style/Theme.NoDisplay" diff --git a/src/en/latisbooks/AndroidManifest.xml b/src/en/latisbooks/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/latisbooks/AndroidManifest.xml +++ b/src/en/latisbooks/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/latisbooks/build.gradle b/src/en/latisbooks/build.gradle index ab4e4dc384..51ba73f1bd 100644 --- a/src/en/latisbooks/build.gradle +++ b/src/en/latisbooks/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Latis Books' pkgNameSuffix = 'en.latisbooks' extClass = '.Latisbooks' - extVersionCode = 4 + extVersionCode = 5 isNsfw = true } diff --git a/src/en/latisbooks/src/eu/kanade/tachiyomi/extension/en/latisbooks/Latisbooks.kt b/src/en/latisbooks/src/eu/kanade/tachiyomi/extension/en/latisbooks/Latisbooks.kt index 3dc054b283..e3cc0517e4 100644 --- a/src/en/latisbooks/src/eu/kanade/tachiyomi/extension/en/latisbooks/Latisbooks.kt +++ b/src/en/latisbooks/src/eu/kanade/tachiyomi/extension/en/latisbooks/Latisbooks.kt @@ -37,7 +37,7 @@ class Latisbooks : HttpSource() { initialized = true title = "Bodysuit 23" url = "/archive/" - thumbnail_url = response.asJsoup().select("img.thumb-image").firstOrNull()?.attr("abs:data-src") + thumbnail_url = "https://images.squarespace-cdn.com/content/v1/56595108e4b01110e1cf8735/1511856223610-NSB8O5OJ1F6KPQL0ZGBH/image-asset.jpeg" } } @@ -121,7 +121,7 @@ class Latisbooks : HttpSource() { val blocks = response.asJsoup().select("div.content-wrapper div.row div.col") // Handle multiple images per page (e.g. Page 23+24) - val pages = blocks.select("img.thumb-image") + val pages = blocks.select("div.image-block-wrapper img") .mapIndexed { i, it -> Page(i, "", it.attr("abs:data-src")) } .toMutableList() diff --git a/src/en/lemonfont/AndroidManifest.xml b/src/en/lemonfont/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/lemonfont/AndroidManifest.xml +++ b/src/en/lemonfont/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/likemanga/AndroidManifest.xml b/src/en/likemanga/AndroidManifest.xml new file mode 100644 index 0000000000..8072ee00db --- /dev/null +++ b/src/en/likemanga/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest /> diff --git a/src/en/likemanga/build.gradle b/src/en/likemanga/build.gradle new file mode 100644 index 0000000000..a1fe18997e --- /dev/null +++ b/src/en/likemanga/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'LikeManga' + pkgNameSuffix = 'en.likemanga' + extClass = '.LikeManga' + extVersionCode = 1 +} + +apply from: "$rootDir/common.gradle" diff --git a/src/en/likemanga/res/mipmap-hdpi/ic_launcher.png b/src/en/likemanga/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..0992d97287 Binary files /dev/null and b/src/en/likemanga/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/en/likemanga/res/mipmap-mdpi/ic_launcher.png b/src/en/likemanga/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..4159c59d51 Binary files /dev/null and b/src/en/likemanga/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/en/likemanga/res/mipmap-xhdpi/ic_launcher.png b/src/en/likemanga/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..f55409dd99 Binary files /dev/null and b/src/en/likemanga/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/en/likemanga/res/mipmap-xxhdpi/ic_launcher.png b/src/en/likemanga/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7a367aa387 Binary files /dev/null and b/src/en/likemanga/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/en/likemanga/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/likemanga/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..7c485d4aa2 Binary files /dev/null and b/src/en/likemanga/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/en/likemanga/res/web_hi_res_512.png b/src/en/likemanga/res/web_hi_res_512.png new file mode 100644 index 0000000000..c15b6655d8 Binary files /dev/null and b/src/en/likemanga/res/web_hi_res_512.png differ diff --git a/src/en/likemanga/src/eu/kanade/tachiyomi/extension/en/likemanga/LikeManga.kt b/src/en/likemanga/src/eu/kanade/tachiyomi/extension/en/likemanga/LikeManga.kt new file mode 100644 index 0000000000..c98d5e1bce --- /dev/null +++ b/src/en/likemanga/src/eu/kanade/tachiyomi/extension/en/likemanga/LikeManga.kt @@ -0,0 +1,276 @@ +package eu.kanade.tachiyomi.extension.en.likemanga + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Request +import okhttp3.Response +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import org.jsoup.nodes.Element +import uy.kohesive.injekt.injectLazy +import java.text.SimpleDateFormat +import java.util.Locale + +class LikeManga : ParsedHttpSource() { + + override val name = "LikeManga" + + override val lang = "en" + + override val baseUrl = "https://likemanga.io" + + override val supportsLatest = true + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(1, 2) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", "$baseUrl/") + + private val json: Json by injectLazy() + + override fun popularMangaRequest(page: Int): Request { + return searchMangaRequest(page, "", FilterList(SortFilter("top-manga"))) + } + + override fun popularMangaParse(response: Response) = searchMangaParse(response) + override fun popularMangaFromElement(element: Element) = searchMangaFromElement(element) + override fun popularMangaSelector() = searchMangaSelector() + override fun popularMangaNextPageSelector() = searchMangaNextPageSelector() + + override fun latestUpdatesRequest(page: Int): Request { + return searchMangaRequest(page, "", FilterList(SortFilter("lastest-chap"))) + } + + override fun latestUpdatesParse(response: Response) = searchMangaParse(response) + override fun latestUpdatesFromElement(element: Element) = searchMangaFromElement(element) + override fun latestUpdatesSelector() = searchMangaSelector() + override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector() + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val url = baseUrl.toHttpUrl().newBuilder().apply { + addQueryParameter("act", "searchadvance") + filters.forEach { filter -> + when (filter) { + is GenreFilter -> { + filter.checked?.forEach { + addQueryParameter("f[genres][]", it) + } + } + is ChapterCountFilter -> { + filter.selected?.let { + addQueryParameter("f[min_num_chapter]", it) + } + } + is StatusFilter -> { + filter.selected?.let { + addQueryParameter("f[status]", it) + } + } + is SortFilter -> { + filter.selected?.let { + addQueryParameter("f[sortby]", it) + } + } + else -> {} + } + } + if (query.isNotEmpty()) { + addQueryParameter("f[keyword]", query.trim()) + } + if (page > 1) { + addQueryParameter("pageNum", page.toString()) + } + }.build() + + return GET(url, headers) + } + + private var genresList: List<Pair<String, String>> = emptyList() + + private fun parseGenres(document: Document): List<Pair<String, String>> { + return document.selectFirst("div.search_genres") + ?.select("div.form-check") + .orEmpty() + .mapNotNull { + val label = it.selectFirst("label") + ?.text()?.trim() ?: return@mapNotNull null + + val value = it.selectFirst("input") + ?.attr("value") ?: return@mapNotNull null + + Pair(label, value) + } + } + + override fun getFilterList(): FilterList { + val filters: MutableList<Filter<*>> = mutableListOf( + SortFilter(), + StatusFilter(), + ChapterCountFilter(), + ) + + filters += if (genresList.isEmpty()) { + listOf( + Filter.Separator(), + Filter.Header("Press 'reset' to attempt to show Genres"), + ) + } else { + listOf( + GenreFilter("Genre", genresList), + ) + } + + return FilterList(filters) + } + + override fun searchMangaParse(response: Response): MangasPage { + if (genresList.isEmpty()) { + val document = response.peekBody(Long.MAX_VALUE).string() + .let { Jsoup.parse(it, response.request.url.toString()) } + + genresList = parseGenres(document) + } + + return super.searchMangaParse(response) + } + + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + thumbnail_url = element.selectFirst("img")?.imgAttr() + title = element.select(".title-manga").text() + } + + override fun searchMangaSelector() = "div.card-body div.card" + override fun searchMangaNextPageSelector() = "ul.pagination a:contains(»)" + + override fun mangaDetailsParse(document: Document) = SManga.create().apply { + title = document.select("#title-detail-manga").text() + thumbnail_url = document.selectFirst(".detail-info img")?.imgAttr() + description = document.selectFirst("#summary_shortened")?.text()?.trim() + genre = document.select(".list-info a[href*=/genres/]").joinToString { it.text() } + status = document.selectFirst(".list-info .status p:nth-child(2)")?.text().parseStatus() + author = document.selectFirst(".list-info .author p:nth-child(2)")?.text() + ?.takeUnless { it.trim() == "Updating" } + } + + private fun String?.parseStatus(): Int { + if (this == null) return SManga.UNKNOWN + + return when { + contains("Complete", true) -> SManga.COMPLETED + contains("In process", true) -> SManga.ONGOING + contains("Pause", true) -> SManga.ON_HIATUS + else -> SManga.UNKNOWN + } + } + + override fun chapterListParse(response: Response): List<SChapter> { + val document = response.use { it.asJsoup() } + + val chapters = document.select(chapterListSelector()) + .map(::chapterFromElement) + .toMutableList() + + val lastPage = document.select("div.chapters_pagination a:not(.next)").last() + ?.attr("onclick") + ?.substringAfter("(") + ?.substringBefore(")") + ?.toIntOrNull() + ?: return chapters + + val id = document.select("#title-detail-manga").attr("data-manga") + .toIntOrNull() ?: return chapters + + for (page in 2..lastPage) { + chapters.addAll(fetchAjaxChapterList(id, page)) + } + + return chapters + } + + private fun fetchAjaxChapterList(id: Int, page: Int): List<SChapter> { + val request = ajaxChapterListRequest(id, page) + val response = client.newCall(request).execute() + + if (!response.isSuccessful) { + response.close() + return emptyList() + } + + return ajaxChapterListParse(response) + } + + private fun ajaxChapterListRequest(id: Int, page: Int): Request { + val url = baseUrl.toHttpUrl().newBuilder().apply { + addQueryParameter("act", "ajax") + addQueryParameter("code", "load_list_chapter") + addQueryParameter("manga_id", id.toString()) + addQueryParameter("page_num", page.toString()) + addQueryParameter("chap_id", "0") + addQueryParameter("keyword", "") + }.build() + + return GET(url, headers) + } + + private fun ajaxChapterListParse(response: Response): List<SChapter> { + val responseJson = response.use { json.parseToJsonElement(it.body.string()) }.jsonObject + val htmlString = responseJson["list_chap"]!!.jsonPrimitive.content + val document = Jsoup.parseBodyFragment(htmlString, response.request.url.toString()) + + return document.select(chapterListSelector()) + .map(::chapterFromElement) + } + + override fun chapterFromElement(element: Element) = SChapter.create().apply { + setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) + name = element.select("a").text() + date_upload = element.selectFirst(".chapter-release-date")?.text().parseDate() + } + + override fun chapterListSelector() = ".wp-manga-chapter" + + private fun String?.parseDate(): Long { + return runCatching { + dateFormat.parse(this!!)!!.time + }.getOrDefault(0L) + } + + override fun pageListParse(document: Document): List<Page> { + return document.select(".reading-detail img:not(noscript img)").mapIndexed { i, img -> + Page(i, "", img.imgAttr()) + } + } + + private fun Element.imgAttr(): String? { + return when { + hasAttr("data-cfsrc") -> attr("abs:data-cfsrc") + hasAttr("data-src") -> attr("abs:data-src") + hasAttr("data-lazy-src") -> attr("abs:data-lazy-src") + hasAttr("srcset") -> attr("abs:srcset").substringBefore(" ") + else -> attr("abs:src") + } + } + + override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not Used") + + companion object { + val dateFormat by lazy { + SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH) + } + } +} diff --git a/src/en/likemanga/src/eu/kanade/tachiyomi/extension/en/likemanga/LikeMangaFilters.kt b/src/en/likemanga/src/eu/kanade/tachiyomi/extension/en/likemanga/LikeMangaFilters.kt new file mode 100644 index 0000000000..21946c7825 --- /dev/null +++ b/src/en/likemanga/src/eu/kanade/tachiyomi/extension/en/likemanga/LikeMangaFilters.kt @@ -0,0 +1,71 @@ +package eu.kanade.tachiyomi.extension.en.likemanga + +import eu.kanade.tachiyomi.source.model.Filter + +abstract class SelectFilter( + name: String, + private val options: List<Pair<String, String>>, + defaultValue: String? = null, +) : Filter.Select<String>( + name, + options.map { it.first }.toTypedArray(), + options.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: 0, +) { + val selected get() = options[state].second.takeUnless { it.isEmpty() } +} + +class CheckBoxFilter( + name: String, + val value: String, +) : Filter.CheckBox(name) + +class GenreFilter( + name: String, + genres: List<Pair<String, String>>, +) : Filter.Group<CheckBoxFilter>( + name, + genres.map { CheckBoxFilter(it.first, it.second) }, +) { + val checked get() = state.filter { it.state }.map { it.value }.takeUnless { it.isEmpty() } +} + +class SortFilter(default: String? = null) : SelectFilter( + "Sort By", + listOf( + Pair("", ""), + Pair("Lasted update", "lastest-chap"), + Pair("Lasted manga", "lastest-manga"), + Pair("Top all", "top-manga"), + Pair("Top month", "top-month"), + Pair("Top week", "top-week"), + Pair("Top day", "top-day"), + Pair("Follow", "follow"), + Pair("Comments", "comment"), + Pair("Number of Chapters", "num-chap"), + ), + default, +) + +class StatusFilter : SelectFilter( + "Status", + listOf( + Pair("All", ""), + Pair("Complete", "Complete"), + Pair("In process", "In process"), + Pair("Pause", "Pause"), + ), +) + +class ChapterCountFilter : SelectFilter( + "Number of Chapters", + listOf( + Pair("", ""), + Pair(">= 0 chapter", "1"), + Pair(">= 50 chapter", "50"), + Pair(">= 100 chapter", "100"), + Pair(">= 200 chapter", "200"), + Pair(">= 300 chapter", "300"), + Pair(">= 400 chapter", "400"), + Pair(">= 500 chapter", "500"), + ), +) diff --git a/src/en/loadingartist/AndroidManifest.xml b/src/en/loadingartist/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/loadingartist/AndroidManifest.xml +++ b/src/en/loadingartist/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/lynxscans/build.gradle b/src/en/lynxscans/build.gradle deleted file mode 100644 index 4b62f029fb..0000000000 --- a/src/en/lynxscans/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' - -ext { - extName = 'LynxScans' - pkgNameSuffix = 'en.lynxscans' - extClass = '.LynxScans' - extVersionCode = 7 -} - -apply from: "$rootDir/common.gradle" diff --git a/src/en/lynxscans/res/mipmap-hdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 1dd6cfde42..0000000000 Binary files a/src/en/lynxscans/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/lynxscans/res/mipmap-mdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 1c411bc85c..0000000000 Binary files a/src/en/lynxscans/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/lynxscans/res/mipmap-xhdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index f9083e6c81..0000000000 Binary files a/src/en/lynxscans/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/lynxscans/res/mipmap-xxhdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 951a16c8f1..0000000000 Binary files a/src/en/lynxscans/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 5e87a4ede4..0000000000 Binary files a/src/en/lynxscans/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/en/lynxscans/res/web_hi_res_512.png b/src/en/lynxscans/res/web_hi_res_512.png deleted file mode 100644 index 65950d349a..0000000000 Binary files a/src/en/lynxscans/res/web_hi_res_512.png and /dev/null differ diff --git a/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScans.kt b/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScans.kt deleted file mode 100644 index a151a0a205..0000000000 --- a/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScans.kt +++ /dev/null @@ -1,130 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.lynxscans - -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.HttpSource -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.json.Json -import okhttp3.Request -import okhttp3.Response -import uy.kohesive.injekt.injectLazy - -class LynxScans : HttpSource() { - override val baseUrl: String = "https://lynxscans.com" - private val apiUrl: String = "https://api.lynxscans.com/api" - - override val lang: String = "en" - override val name: String = "LynxScans" - - override val versionId = 2 - - override val supportsLatest: Boolean = true - - private val json: Json by injectLazy() - - // Popular - override fun popularMangaRequest(page: Int): Request { - return GET("$apiUrl/comics?page=$page", headers) - } - - override fun popularMangaParse(response: Response): MangasPage { - val data = json.decodeFromString<Popular>(response.body.string()) - - val titles = data.comics.data.map(PopularComicsData::toSManga) - - return MangasPage(titles, !data.comics.next_page_url.isNullOrEmpty()) - } - - // Latest - override fun latestUpdatesRequest(page: Int): Request { - return GET("$apiUrl/latest?page=$page", headers) - } - - override fun latestUpdatesParse(response: Response): MangasPage { - val data = json.decodeFromString<Latest>(response.body.string()) - - val titles = data.chapters.data.distinctBy { it.comic_titleSlug }.map(LatestChaptersData::toSManga) - - return MangasPage(titles, !data.chapters.next_page_url.isNullOrEmpty()) - } - - // Search - override fun searchMangaParse(response: Response): MangasPage { - throw UnsupportedOperationException("Not used") - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - throw Exception("Search is not supported") - } - - // Details - override fun mangaDetailsParse(response: Response): SManga { - val data = json.decodeFromString<MangaDetails>(response.body.string()) - - return data.comic.toSManga() - } - - override fun mangaDetailsRequest(manga: SManga): Request { - val titleId = manga.url.substringAfterLast("/") - - return GET("$apiUrl/comics/$titleId", headers) - } - - override fun getMangaUrl(manga: SManga): String { - val titleId = manga.url.substringAfterLast("/") - - return "$baseUrl/comics/$titleId" - } - - // Chapters - override fun chapterListRequest(manga: SManga): Request { - return mangaDetailsRequest(manga) - } - - override fun chapterListParse(response: Response): List<SChapter> { - val data = json.decodeFromString<MangaDetails>(response.body.string()) - - val chapters: MutableList<SChapter> = mutableListOf() - - data.comic.volumes.forEach { volume -> - volume.chapters.forEach { chapter -> - chapters.add( - SChapter.create().apply { - url = "/comics/${data.comic.titleSlug}/volume/${volume.number}/chapter/${chapter.number}" - name = volume.name + " " + (if (!chapter.name.contains("chapter", true)) "Chapter ${chapter.number} " else "") + chapter.name - }, - ) - } - } - - return chapters - } - - override fun getChapterUrl(chapter: SChapter): String { - val chapterPath = chapter.url.substringAfter("/") - - return "$baseUrl/$chapterPath" - } - - // Page - override fun pageListRequest(chapter: SChapter): Request { - val chapterPath = chapter.url.substringAfter("/") - - return GET("$apiUrl/$chapterPath", headers) - } - - override fun pageListParse(response: Response): List<Page> { - val data = json.decodeFromString<PageList>(response.body.string()) - - return data.pages.mapIndexed { idx, it -> - Page(idx, imageUrl = it.thumb) - } - } - - // Unused - override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not Used") -} diff --git a/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScansDto.kt b/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScansDto.kt deleted file mode 100644 index 81780f5c56..0000000000 --- a/src/en/lynxscans/src/eu/kanade/tachiyomi/extension/en/lynxscans/LynxScansDto.kt +++ /dev/null @@ -1,110 +0,0 @@ -package eu.kanade.tachiyomi.extension.en.lynxscans - -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.Serializable - -@Serializable -data class Latest( - val chapters: LatestChapters, -) - -@Serializable -data class LatestChapters( - val next_page_url: String?, - val data: List<LatestChaptersData>, -) - -@Serializable -data class LatestChaptersData( - val comic_title: String, - val comic_thumb: String, - val comic_titleSlug: String, -) { - fun toSManga(): SManga = SManga.create().apply { - title = this@LatestChaptersData.comic_title - thumbnail_url = this@LatestChaptersData.comic_thumb - url = "/comics/" + this@LatestChaptersData.comic_titleSlug - } -} - -@Serializable -data class Popular( - val comics: PopularComics, -) - -@Serializable -data class PopularComics( - val next_page_url: String?, - val data: List<PopularComicsData>, -) - -@Serializable -data class PopularComicsData( - val title: String, - val thumb: String, - val titleSlug: String, -) { - fun toSManga(): SManga = SManga.create().apply { - title = this@PopularComicsData.title - thumbnail_url = this@PopularComicsData.thumb - url = "/comics/" + this@PopularComicsData.titleSlug - } -} - -@Serializable -data class MangaDetails( - val comic: MangaDetailsComicData, -) - -@Serializable -data class MangaDetailsComicData( - val title: String, - val thumb: String, - val titleSlug: String, - val artist: String, - val author: String, - val description: String, - val tags: List<MangaDetailsTag>, - - val volumes: List<MangaDetailsVolume>, - -) { - fun toSManga(): SManga = SManga.create().apply { - title = this@MangaDetailsComicData.title - thumbnail_url = this@MangaDetailsComicData.thumb - url = "/comics/" + this@MangaDetailsComicData.titleSlug - author = if (this@MangaDetailsComicData.author != "blank") this@MangaDetailsComicData.author else null - artist = if (this@MangaDetailsComicData.artist != "blank") this@MangaDetailsComicData.artist else null - description = this@MangaDetailsComicData.description - genre = this@MangaDetailsComicData.tags.joinToString { it.name } - status = SManga.UNKNOWN - } -} - -@Serializable -data class MangaDetailsTag( - val name: String, -) - -@Serializable -data class MangaDetailsVolume( - val chapters: List<MangaDetailsChapter>, - val name: String, - val number: Int, -) - -@Serializable -data class MangaDetailsChapter( - val name: String, - val number: Int, -) - -@Serializable -data class PageList( - val pages: List<Page>, -) - -@Serializable -data class Page( - val thumb: String, -) diff --git a/src/en/madokami/AndroidManifest.xml b/src/en/madokami/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/madokami/AndroidManifest.xml +++ b/src/en/madokami/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/manga1s/AndroidManifest.xml b/src/en/manga1s/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/manga1s/AndroidManifest.xml +++ b/src/en/manga1s/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangademon/AndroidManifest.xml b/src/en/mangademon/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangademon/AndroidManifest.xml +++ b/src/en/mangademon/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangademon/build.gradle b/src/en/mangademon/build.gradle index 36737d11c2..3150415d06 100644 --- a/src/en/mangademon/build.gradle +++ b/src/en/mangademon/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Manga Demon' pkgNameSuffix = 'en.mangademon' extClass = '.MangaDemon' - extVersionCode = 1 + extVersionCode = 4 } apply from: "$rootDir/common.gradle" diff --git a/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt index 52967697d5..7e353c6bef 100644 --- a/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt +++ b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemon.kt @@ -1,17 +1,21 @@ package eu.kanade.tachiyomi.extension.en.mangademon import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.network.asObservableSuccess +import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource -import okhttp3.Headers +import eu.kanade.tachiyomi.util.asJsoup import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Request +import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import rx.Observable import java.text.SimpleDateFormat import java.util.Locale @@ -20,10 +24,14 @@ class MangaDemon : ParsedHttpSource() { override val lang = "en" override val supportsLatest = true override val name = "Manga Demon" - override val baseUrl = "https://mangademon.org" + override val baseUrl = "https://manga-demon.org" - override fun headersBuilder(): Headers.Builder = Headers.Builder() - .add("referrer", "origin") + override val client = network.cloudflareClient.newBuilder() + .rateLimit(1) + .build() + + override fun headersBuilder() = super.headersBuilder() + .add("Referer", baseUrl) // latest override fun latestUpdatesRequest(page: Int): Request { @@ -34,14 +42,12 @@ class MangaDemon : ParsedHttpSource() { override fun latestUpdatesSelector() = "div.leftside" - override fun latestUpdatesFromElement(element: Element): SManga { - return SManga.create().apply { - element.select("a").apply() { - title = attr("title").dropLast(4) - url = attr("href") - } - thumbnail_url = element.select("img").attr("abs:src") + override fun latestUpdatesFromElement(element: Element) = SManga.create().apply { + element.select("a").apply { + title = attr("title") + setUrlWithoutDomain(attr("href")) } + thumbnail_url = element.select("img").attr("abs:src") } // Popular @@ -56,27 +62,61 @@ class MangaDemon : ParsedHttpSource() { override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element) // Search + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + return if (query.isNotEmpty()) { + super.fetchSearchManga(page, query, filters) + } else { + client.newCall(filterSearchRequest(page, filters)) + .asObservableSuccess() + .map(::filterSearchParse) + } + } + + private fun filterSearchRequest(page: Int, filters: FilterList): Request { + val url = "$baseUrl/browse.php".toHttpUrl().newBuilder().apply { + addQueryParameter("list", page.toString()) + filters.forEach { filter -> + when (filter) { + is GenreFilter -> { + filter.checked.forEach { genre -> + addQueryParameter("genre[]", genre) + } + } + is StatusFilter -> { + addQueryParameter("status", filter.selected) + } + is SortFilter -> { + addQueryParameter("orderby", filter.selected) + } + else -> {} + } + } + }.build() + + return GET(url, headers) + } + + private fun filterSearchParse(response: Response) = popularMangaParse(response) + + override fun getFilterList() = getFilters() + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { val url = "$baseUrl/search.php".toHttpUrl().newBuilder() .addQueryParameter("manga", query) .build() - return POST("$url", headers) + return GET(url, headers) } override fun searchMangaSelector() = "a.boxsizing" - override fun searchMangaFromElement(element: Element): SManga { - return SManga.create().apply { - element.select("a").first().let { - title = element.select("li.boxsizing").text() - url = (element.attr("href")) - val urlsorter = title.replace(":", "%20") - thumbnail_url = ("https://readermc.org/images/thumbnails/$urlsorter.webp") - } - } + override fun searchMangaFromElement(element: Element) = SManga.create().apply { + title = element.text() + setUrlWithoutDomain(element.attr("href")) + val urlSorter = title.replace(":", "%20") + thumbnail_url = ("https://readermc.org/images/thumbnails/$urlSorter.webp") } - override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector() + override fun searchMangaNextPageSelector() = null // Manga details override fun mangaDetailsParse(document: Document): SManga { @@ -105,7 +145,7 @@ class MangaDemon : ParsedHttpSource() { override fun chapterFromElement(element: Element): SChapter { return SChapter.create().apply { element.select("a").let { urlElement -> - url = (urlElement.attr("href")) + setUrlWithoutDomain(urlElement.attr("href")) name = element.select("strong.chapter-title").text() } val date = element.select("time.chapter-update").text() @@ -122,11 +162,43 @@ class MangaDemon : ParsedHttpSource() { private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } + + private val loadMoreEndpointRegex by lazy { Regex("""GET[^/]+([^=]+)""") } } override fun pageListParse(document: Document): List<Page> { - return document.select("img.imgholder") - .mapIndexed { i, el -> Page(i, "", el.attr("src")) } + val baseImages = document.select("img.imgholder") + .map { it.attr("abs:src") } + .toMutableList() + + baseImages.addAll(loadMoreImages(document)) + + return baseImages.mapIndexed { i, img -> Page(i, "", img) } + } + + private fun loadMoreImages(document: Document): List<String> { + val buttonHtml = document.selectFirst("img.imgholder ~ button") + ?.attr("onclick")?.replace("\"", "\'") + ?: return emptyList() + + val id = buttonHtml.substringAfter("\'").substringBefore("\'").trim() + val funcName = buttonHtml.substringBefore("(").trim() + + val endpoint = document.selectFirst("script:containsData($funcName)") + ?.data() + ?.let { loadMoreEndpointRegex.find(it)?.groupValues?.get(1) } + ?: return emptyList() + + val response = client.newCall(GET("$baseUrl$endpoint=$id", headers)).execute() + + if (!response.isSuccessful) { + response.close() + return emptyList() + } + + return response.use { it.asJsoup() } + .select("img") + .map { it.attr("abs:src") } } override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") diff --git a/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemonFilters.kt b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemonFilters.kt new file mode 100644 index 0000000000..62a88395f2 --- /dev/null +++ b/src/en/mangademon/src/eu/kanade/tachiyomi/extension/en/mangademon/MangaDemonFilters.kt @@ -0,0 +1,93 @@ +package eu.kanade.tachiyomi.extension.en.mangademon + +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.source.model.FilterList + +abstract class SelectFilter( + name: String, + private val options: List<Pair<String, String>>, +) : Filter.Select<String>( + name, + options.map { it.first }.toTypedArray(), +) { + val selected get() = options[state].second +} + +class StatusFilter : SelectFilter("Status", status) { + companion object { + private val status = listOf( + Pair("All", "all"), + Pair("Ongoing", "ongoing"), + Pair("Completed", "completed"), + ) + } +} + +class SortFilter : SelectFilter("Sort", sort) { + companion object { + private val sort = listOf( + Pair("Top Views", "VIEWS DESC"), + Pair("A To Z", "NAME ASC"), + ) + } +} + +class CheckBoxFilter( + name: String, + val value: String, +) : Filter.CheckBox(name) + +class GenreFilter : Filter.Group<CheckBoxFilter>( + "Genre", + genres.map { CheckBoxFilter(it.first, it.second) }, +) { + val checked get() = state.filter { it.state }.map { it.value } + + companion object { + private val genres = listOf( + Pair("All", "all"), + Pair("Action", "1"), + Pair("Adventure", "2"), + Pair("Comedy", "3"), + Pair("Cooking", "34"), + Pair("Doujinshi", "25"), + Pair("Drama", "4"), + Pair("Ecchi", "19"), + Pair("Fantasy", "5"), + Pair("Gender Bender", "30"), + Pair("Harem", "10"), + Pair("Historical", "28"), + Pair("Horror", "8"), + Pair("Isekai", "33"), + Pair("Josei", "31"), + Pair("Martial Arts", "6"), + Pair("Mature", "22"), + Pair("Mecha", "32"), + Pair("Mystery", "15"), + Pair("One Shot", "26"), + Pair("Psychological", "11"), + Pair("Romance", "12"), + Pair("School Life", "13"), + Pair("Sci-fi", "16"), + Pair("Seinen", "17"), + Pair("Shoujo", "14"), + Pair("Shoujo Ai", "23"), + Pair("Shounen", "7"), + Pair("Shounen Ai", "29"), + Pair("Slice of Life", "21"), + Pair("Smut", "27"), + Pair("Sports", "20"), + Pair("Supernatural", "9"), + Pair("Tragedy", "18"), + Pair("Webtoons", "24"), + ) + } +} + +fun getFilters() = FilterList( + Filter.Header("Ignored when using text search"), + Filter.Separator(), + SortFilter(), + StatusFilter(), + GenreFilter(), +) diff --git a/src/en/mangadoom/AndroidManifest.xml b/src/en/mangadoom/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangadoom/AndroidManifest.xml +++ b/src/en/mangadoom/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangafox/AndroidManifest.xml b/src/en/mangafox/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/en/mangafox/AndroidManifest.xml +++ b/src/en/mangafox/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/en/mangafreak/AndroidManifest.xml b/src/en/mangafreak/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangafreak/AndroidManifest.xml +++ b/src/en/mangafreak/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangago/AndroidManifest.xml b/src/en/mangago/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangago/AndroidManifest.xml +++ b/src/en/mangago/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangago/build.gradle b/src/en/mangago/build.gradle index 01b6e02b9d..b6beb0dda8 100644 --- a/src/en/mangago/build.gradle +++ b/src/en/mangago/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Mangago' pkgNameSuffix = 'en.mangago' extClass = '.Mangago' - extVersionCode = 12 + extVersionCode = 13 isNsfw = true } @@ -13,4 +13,4 @@ apply from: "$rootDir/common.gradle" dependencies { implementation(project(':lib-cryptoaes')) -} \ No newline at end of file +} diff --git a/src/en/mangago/src/eu/kanade/tachiyomi/extension/en/mangago/Mangago.kt b/src/en/mangago/src/eu/kanade/tachiyomi/extension/en/mangago/Mangago.kt index 8dd753fb83..71d2afb932 100644 --- a/src/en/mangago/src/eu/kanade/tachiyomi/extension/en/mangago/Mangago.kt +++ b/src/en/mangago/src/eu/kanade/tachiyomi/extension/en/mangago/Mangago.kt @@ -46,10 +46,11 @@ class Mangago : ParsedHttpSource() { .addInterceptor { chain -> val response = chain.proceed(chain.request()) - val key = - response.request.url.queryParameter("desckey") ?: return@addInterceptor response - val cols = response.request.url.queryParameter("cols")?.toIntOrNull() - ?: return@addInterceptor response + val fragment = response.request.url.fragment ?: return@addInterceptor response + + // desckey=...&cols=... + val key = fragment.substringAfter("desckey=").substringBefore("&") + val cols = fragment.substringAfter("&cols=").toIntOrNull() ?: return@addInterceptor response val image = unscrambleImage(response.body.byteStream(), key, cols) val body = image.toResponseBody("image/jpeg".toMediaTypeOrNull()) @@ -172,13 +173,14 @@ class Mangago : ParsedHttpSource() { override fun chapterListSelector() = "table#chapter_table > tbody > tr, table.uk-table > tbody > tr" override fun chapterFromElement(element: Element) = SChapter.create().apply { - val link = element.getElementsByTag("a") + val link = element.select("a.chico") setUrlWithoutDomain(link.attr("href")) name = link.text().trim() date_upload = runCatching { dateFormat.parse(element.select("td:last-child").text().trim())?.time }.getOrNull() ?: 0L + scanlator = element.selectFirst("td.no a, td.uk-table-shrink a")?.text()?.trim() } override fun pageListParse(document: Document): List<Page> { @@ -231,7 +233,7 @@ class Mangago : ParsedHttpSource() { .split(",") .mapIndexed { idx, it -> val url = if (it.contains("cspiclink")) { - "$it?desckey=${getDescramblingKey(deobfChapterJs, it)}&cols=$cols" + "$it#desckey=${getDescramblingKey(deobfChapterJs, it)}&cols=$cols" } else { it } diff --git a/src/en/mangahasu/AndroidManifest.xml b/src/en/mangahasu/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangahasu/AndroidManifest.xml +++ b/src/en/mangahasu/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangahere/AndroidManifest.xml b/src/en/mangahere/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangahere/AndroidManifest.xml +++ b/src/en/mangahere/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangajar/AndroidManifest.xml b/src/en/mangajar/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangajar/AndroidManifest.xml +++ b/src/en/mangajar/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangakatana/AndroidManifest.xml b/src/en/mangakatana/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangakatana/AndroidManifest.xml +++ b/src/en/mangakatana/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangamiso/AndroidManifest.xml b/src/en/mangamiso/AndroidManifest.xml index e0616d4fc7..6e274ca355 100644 --- a/src/en/mangamiso/AndroidManifest.xml +++ b/src/en/mangamiso/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/mangapill/AndroidManifest.xml b/src/en/mangapill/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangapill/AndroidManifest.xml +++ b/src/en/mangapill/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangapill/src/eu/kanade/tachiyomi/extension/en/mangapill/MangaPill.kt b/src/en/mangapill/src/eu/kanade/tachiyomi/extension/en/mangapill/MangaPill.kt index 9414c4e86d..c1e8fcda21 100644 --- a/src/en/mangapill/src/eu/kanade/tachiyomi/extension/en/mangapill/MangaPill.kt +++ b/src/en/mangapill/src/eu/kanade/tachiyomi/extension/en/mangapill/MangaPill.kt @@ -22,7 +22,7 @@ class MangaPill : ParsedHttpSource() { override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient override fun headersBuilder() = super.headersBuilder().add("Referer", "$baseUrl/") - + override fun popularMangaRequest(page: Int): Request = latestUpdatesRequest(page) override fun latestUpdatesRequest(page: Int): Request { diff --git a/src/en/mangaplex/AndroidManifest.xml b/src/en/mangaplex/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangaplex/AndroidManifest.xml +++ b/src/en/mangaplex/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangarawclub/AndroidManifest.xml b/src/en/mangarawclub/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangarawclub/AndroidManifest.xml +++ b/src/en/mangarawclub/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangasail/AndroidManifest.xml b/src/en/mangasail/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangasail/AndroidManifest.xml +++ b/src/en/mangasail/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/mangatown/AndroidManifest.xml b/src/en/mangatown/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/mangatown/AndroidManifest.xml +++ b/src/en/mangatown/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/manta/AndroidManifest.xml b/src/en/manta/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/manta/AndroidManifest.xml +++ b/src/en/manta/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/megatokyo/AndroidManifest.xml b/src/en/megatokyo/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/megatokyo/AndroidManifest.xml +++ b/src/en/megatokyo/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/multporn/AndroidManifest.xml b/src/en/multporn/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/multporn/AndroidManifest.xml +++ b/src/en/multporn/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/myhentaicomics/AndroidManifest.xml b/src/en/myhentaicomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/myhentaicomics/AndroidManifest.xml +++ b/src/en/myhentaicomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/myhentaigallery/AndroidManifest.xml b/src/en/myhentaigallery/AndroidManifest.xml index 47c9f73022..ab175f15ef 100644 --- a/src/en/myhentaigallery/AndroidManifest.xml +++ b/src/en/myhentaigallery/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/nineanime/AndroidManifest.xml b/src/en/nineanime/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/nineanime/AndroidManifest.xml +++ b/src/en/nineanime/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/ninehentai/AndroidManifest.xml b/src/en/ninehentai/AndroidManifest.xml index caf8fce1c6..3c7172aa63 100644 --- a/src/en/ninehentai/AndroidManifest.xml +++ b/src/en/ninehentai/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/nuxscans/AndroidManifest.xml b/src/en/nuxscans/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/nuxscans/AndroidManifest.xml +++ b/src/en/nuxscans/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/oglaf/AndroidManifest.xml b/src/en/oglaf/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/oglaf/AndroidManifest.xml +++ b/src/en/oglaf/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/oots/AndroidManifest.xml b/src/en/oots/AndroidManifest.xml index 0d7e33d584..cd55ad9174 100644 --- a/src/en/oots/AndroidManifest.xml +++ b/src/en/oots/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/oppaistream/AndroidManifest.xml b/src/en/oppaistream/AndroidManifest.xml index 26a0a07c96..b6d4c3dd2d 100644 --- a/src/en/oppaistream/AndroidManifest.xml +++ b/src/en/oppaistream/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/patchfriday/AndroidManifest.xml b/src/en/patchfriday/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/patchfriday/AndroidManifest.xml +++ b/src/en/patchfriday/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/purplecress/AndroidManifest.xml b/src/en/purplecress/AndroidManifest.xml index 30310f4f7e..3fa2c02d5d 100644 --- a/src/en/purplecress/AndroidManifest.xml +++ b/src/en/purplecress/AndroidManifest.xml @@ -1,7 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" - xmlns:android="http://schemas.android.com/apk/res/android"> - +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".en.purplecress.PurpleCressURLActivity" @@ -34,4 +32,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/en/pururin/AndroidManifest.xml b/src/en/pururin/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/pururin/AndroidManifest.xml +++ b/src/en/pururin/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/questionablecontent/AndroidManifest.xml b/src/en/questionablecontent/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/questionablecontent/AndroidManifest.xml +++ b/src/en/questionablecontent/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/questionablecontent/src/eu/kanade/tachiyomi/extension/en/questionablecontent/QuestionableContent.kt b/src/en/questionablecontent/src/eu/kanade/tachiyomi/extension/en/questionablecontent/QuestionableContent.kt index f34524ff94..e174ca68c2 100644 --- a/src/en/questionablecontent/src/eu/kanade/tachiyomi/extension/en/questionablecontent/QuestionableContent.kt +++ b/src/en/questionablecontent/src/eu/kanade/tachiyomi/extension/en/questionablecontent/QuestionableContent.kt @@ -16,7 +16,6 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response -import okhttp3.internal.http.HTTP_UNAUTHORIZED import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable @@ -113,7 +112,6 @@ class QuestionableContent : ParsedHttpSource(), ConfigurableSource { title = "Show author's notes" summary = "Enable to see the author's notes at the end of chapters (if they're there)." setDefaultValue(false) - } screen.addPreference(authorsNotesPref) } diff --git a/src/en/rainofsnow/AndroidManifest.xml b/src/en/rainofsnow/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/rainofsnow/AndroidManifest.xml +++ b/src/en/rainofsnow/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/randowiz/AndroidManifest.xml b/src/en/randowiz/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/randowiz/AndroidManifest.xml +++ b/src/en/randowiz/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/rawmanga/AndroidManifest.xml b/src/en/rawmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/rawmanga/AndroidManifest.xml +++ b/src/en/rawmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/readcomiconline/AndroidManifest.xml b/src/en/readcomiconline/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/readcomiconline/AndroidManifest.xml +++ b/src/en/readcomiconline/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/readm/AndroidManifest.xml b/src/en/readm/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/readm/AndroidManifest.xml +++ b/src/en/readm/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/readmangatoday/AndroidManifest.xml b/src/en/readmangatoday/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/readmangatoday/AndroidManifest.xml +++ b/src/en/readmangatoday/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/reallifecomics/AndroidManifest.xml b/src/en/reallifecomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/reallifecomics/AndroidManifest.xml +++ b/src/en/reallifecomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/rmanga/AndroidManifest.xml b/src/en/rmanga/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/en/rmanga/AndroidManifest.xml +++ b/src/en/rmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/en/schlockmercenary/AndroidManifest.xml b/src/en/schlockmercenary/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/schlockmercenary/AndroidManifest.xml +++ b/src/en/schlockmercenary/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/silentmangaaudition/AndroidManifest.xml b/src/en/silentmangaaudition/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/silentmangaaudition/AndroidManifest.xml +++ b/src/en/silentmangaaudition/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/solarandsundry/AndroidManifest.xml b/src/en/solarandsundry/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/solarandsundry/AndroidManifest.xml +++ b/src/en/solarandsundry/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/soushiyofamiliar/AndroidManifest.xml b/src/en/soushiyofamiliar/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/soushiyofamiliar/AndroidManifest.xml +++ b/src/en/soushiyofamiliar/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/swordscomic/AndroidManifest.xml b/src/en/swordscomic/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/swordscomic/AndroidManifest.xml +++ b/src/en/swordscomic/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/tapastic/AndroidManifest.xml b/src/en/tapastic/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/tapastic/AndroidManifest.xml +++ b/src/en/tapastic/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/tcbscans/AndroidManifest.xml b/src/en/tcbscans/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/tcbscans/AndroidManifest.xml +++ b/src/en/tcbscans/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/templescan/AndroidManifest.xml b/src/en/templescan/AndroidManifest.xml new file mode 100644 index 0000000000..15f9ec69df --- /dev/null +++ b/src/en/templescan/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> + + <application> + <activity + android:name=".en.templescan.TempleScanUrlActivity" + android:excludeFromRecents="true" + android:exported="true" + android:theme="@android:style/Theme.NoDisplay"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data + android:host="templescan.net" + android:pathPattern="/comic/..*" + android:scheme="https" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/src/en/templescan/build.gradle b/src/en/templescan/build.gradle new file mode 100644 index 0000000000..3036369113 --- /dev/null +++ b/src/en/templescan/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Temple Scan' + pkgNameSuffix = 'en.templescan' + extClass = '.TempleScan' + extVersionCode = 33 +} + +apply from: "$rootDir/common.gradle" diff --git a/multisrc/overrides/madara/templescan/res/mipmap-hdpi/ic_launcher.png b/src/en/templescan/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/templescan/res/mipmap-hdpi/ic_launcher.png rename to src/en/templescan/res/mipmap-hdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/templescan/res/mipmap-mdpi/ic_launcher.png b/src/en/templescan/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/templescan/res/mipmap-mdpi/ic_launcher.png rename to src/en/templescan/res/mipmap-mdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/templescan/res/mipmap-xhdpi/ic_launcher.png b/src/en/templescan/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/templescan/res/mipmap-xhdpi/ic_launcher.png rename to src/en/templescan/res/mipmap-xhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/templescan/res/mipmap-xxhdpi/ic_launcher.png b/src/en/templescan/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/templescan/res/mipmap-xxhdpi/ic_launcher.png rename to src/en/templescan/res/mipmap-xxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/templescan/res/mipmap-xxxhdpi/ic_launcher.png b/src/en/templescan/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from multisrc/overrides/madara/templescan/res/mipmap-xxxhdpi/ic_launcher.png rename to src/en/templescan/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/multisrc/overrides/madara/templescan/res/web_hi_res_512.png b/src/en/templescan/res/web_hi_res_512.png similarity index 100% rename from multisrc/overrides/madara/templescan/res/web_hi_res_512.png rename to src/en/templescan/res/web_hi_res_512.png diff --git a/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt new file mode 100644 index 0000000000..5e82ea62cc --- /dev/null +++ b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScan.kt @@ -0,0 +1,217 @@ +package eu.kanade.tachiyomi.extension.en.templescan + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.Response +import org.jsoup.nodes.Document +import rx.Observable +import uy.kohesive.injekt.injectLazy +import java.util.Calendar +import kotlin.math.min + +class TempleScan : HttpSource() { + + override val name = "Temple Scan" + + override val lang = "en" + + override val baseUrl = "https://templescan.net" + + override val supportsLatest = true + + override val versionId = 2 + + override val client = network.cloudflareClient.newBuilder() + .rateLimit(1) + .build() + + private val json: Json by injectLazy() + + private val homeDocument: Document by lazy { + val response = client.newCall(GET(baseUrl, headers)).execute() + + if (response.isSuccessful.not()) { + response.close() + throw Exception("Http Error ${response.code}") + } + + response.use { it.asJsoup() } + } + + private val seriesCache: List<Series> by lazy { + homeDocument.selectFirst("script:containsData(proyectos)") + ?.data() + ?.let { proyectosRegex.find(it)?.groupValues?.get(1) } + ?.let(json::decodeFromString) + ?: throw Exception("Unable to extract series information") + } + + private lateinit var filteredSeriesCache: List<Series> + + private fun List<Series>.toMangasPage(page: Int): MangasPage { + val end = min(page * limit, this.size) + val entries = this.subList((page - 1) * limit, end) + .map(Series::toSManga) + + return MangasPage(entries, end < this.size) + } + + @Serializable + data class Series( + @SerialName("nombre") val name: String, + val slug: String, + @SerialName("portada") val cover: String, + ) { + fun toSManga() = SManga.create().apply { + url = "/comic/$slug" + title = name + thumbnail_url = cover + } + } + + override fun fetchPopularManga(page: Int): Observable<MangasPage> { + val mangasPage = seriesCache.toMangasPage(page) + + return Observable.just(mangasPage) + } + + override fun fetchLatestUpdates(page: Int): Observable<MangasPage> { + val slugs = homeDocument.select("section:contains(new release) figure") + .mapNotNull { element -> + element.selectFirst("a")?.attr("abs:href") + ?.substringAfter("/comic/") + ?.substringBefore("/") + } + + val entries = slugs.mapNotNull { slug -> + seriesCache.firstOrNull { it.slug == slug }?.toSManga() + } + + val mangasPage = MangasPage(entries, false) + + return Observable.just(mangasPage) + } + + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> { + if (query.startsWith(SEARCH_PREFIX)) { + val url = "/comic/${query.substringAfter(SEARCH_PREFIX)}" + val manga = SManga.create().apply { this.url = url } + return fetchMangaDetails(manga).map { + val newManga = it.apply { this.url = url } + MangasPage(listOf(newManga), false) + } + } + + if (page == 1) { + filteredSeriesCache = seriesCache.filter { + it.name.contains(query.trim(), true) + } + } + + val mangasPage = filteredSeriesCache.toMangasPage(page) + + return Observable.just(mangasPage) + } + + override fun mangaDetailsParse(response: Response): SManga { + val document = response.asJsoup() + + return SManga.create().apply { + thumbnail_url = document.select(".max-w-80 > img").attr("abs:src") + description = document.select("section[id=section-sinopsis] p").text() + title = document.select("h1").text() + genre = document.select("div.flex div:contains(gen) + div a").joinToString { it.text().trim() } + author = document.selectFirst("div.flex div:contains(aut) + div")?.text() + } + } + + override fun chapterListParse(response: Response): List<SChapter> { + val elements = response.asJsoup() + .select("div.contenedor-capitulo-miniatura") + + return elements.map { element -> + SChapter.create().apply { + setUrlWithoutDomain(element.select("a").attr("href")) + name = element.select("div[id=name]").text() + date_upload = element.select("time").text().let { + runCatching { it.parseRelativeDate() }.getOrDefault(0L) + } + } + } + } + + override fun pageListParse(response: Response): List<Page> { + val elements = response.asJsoup() + .select("main div img") + + return elements.mapIndexed { index, element -> + Page(index, "", element.attr("abs:src")) + } + } + + private fun String.parseRelativeDate(): Long { + val now = Calendar.getInstance().apply { + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } + + var parsedDate = 0L + + val relativeDate = this.split(" ")[0].trim().toInt() + + when { + "second" in this -> { + parsedDate = now.apply { add(Calendar.SECOND, -relativeDate) }.timeInMillis + } + "minute" in this -> { + parsedDate = now.apply { add(Calendar.MINUTE, -relativeDate) }.timeInMillis + } + "hour" in this -> { + parsedDate = now.apply { add(Calendar.HOUR, -relativeDate) }.timeInMillis + } + "day" in this -> { + parsedDate = now.apply { add(Calendar.DAY_OF_YEAR, -relativeDate) }.timeInMillis + } + "week" in this -> { + parsedDate = now.apply { add(Calendar.WEEK_OF_YEAR, -relativeDate) }.timeInMillis + } + "month" in this -> { + parsedDate = now.apply { add(Calendar.MONTH, -relativeDate) }.timeInMillis + } + "year" in this -> { + parsedDate = now.apply { add(Calendar.YEAR, -relativeDate) }.timeInMillis + } + } + return parsedDate + } + + companion object { + private val proyectosRegex by lazy { + Regex("""proyectos\s*=\s*([^;]+)""") + } + + private const val limit = 20 + const val SEARCH_PREFIX = "slug:" + } + + override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException("Not Used") + override fun popularMangaParse(response: Response) = throw UnsupportedOperationException("Not Used") + override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not Used") + override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not Used") + override fun imageUrlParse(response: Response) = throw UnsupportedOperationException("Not Used") + override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException("Not Used") + override fun searchMangaParse(response: Response) = throw UnsupportedOperationException("Not Used") +} diff --git a/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt new file mode 100644 index 0000000000..df6c5a9e1f --- /dev/null +++ b/src/en/templescan/src/eu/kanade/tachiyomi/extension/en/templescan/TempleScanUrlActivity.kt @@ -0,0 +1,34 @@ +package eu.kanade.tachiyomi.extension.en.templescan + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.os.Bundle +import android.util.Log +import kotlin.system.exitProcess + +class TempleScanUrlActivity : Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val pathSegments = intent?.data?.pathSegments + if (pathSegments != null && pathSegments.size > 1) { + val slug = pathSegments[1] + val mainIntent = Intent().apply { + action = "eu.kanade.tachiyomi.SEARCH" + putExtra("query", "${TempleScan.SEARCH_PREFIX}$slug") + putExtra("filter", packageName) + } + + try { + startActivity(mainIntent) + } catch (e: ActivityNotFoundException) { + Log.e("TempleScanUrlActivity", e.toString()) + } + } else { + Log.e("TempleScanUrlActivity", "could not parse uri from intent $intent") + } + + finish() + exitProcess(0) + } +} diff --git a/src/en/theduckwebcomics/AndroidManifest.xml b/src/en/theduckwebcomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/theduckwebcomics/AndroidManifest.xml +++ b/src/en/theduckwebcomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/thepropertyofhate/AndroidManifest.xml b/src/en/thepropertyofhate/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/thepropertyofhate/AndroidManifest.xml +++ b/src/en/thepropertyofhate/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/tsumino/AndroidManifest.xml b/src/en/tsumino/AndroidManifest.xml index 74061a8571..5c0ee55538 100644 --- a/src/en/tsumino/AndroidManifest.xml +++ b/src/en/tsumino/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/vgperson/AndroidManifest.xml b/src/en/vgperson/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/vgperson/AndroidManifest.xml +++ b/src/en/vgperson/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/vizshonenjump/AndroidManifest.xml b/src/en/vizshonenjump/AndroidManifest.xml index 604e24eba3..c0cbab96e0 100644 --- a/src/en/vizshonenjump/AndroidManifest.xml +++ b/src/en/vizshonenjump/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/en/voyceme/AndroidManifest.xml b/src/en/voyceme/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/voyceme/AndroidManifest.xml +++ b/src/en/voyceme/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/voyceme/build.gradle b/src/en/voyceme/build.gradle index b75a7a4804..9743e42cbd 100644 --- a/src/en/voyceme/build.gradle +++ b/src/en/voyceme/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Voyce.Me' pkgNameSuffix = 'en.voyceme' extClass = '.VoyceMe' - extVersionCode = 2 + extVersionCode = 3 } apply from: "$rootDir/common.gradle" diff --git a/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMe.kt b/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMe.kt index 46b705a9b0..0e5a30e2b7 100644 --- a/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMe.kt +++ b/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMe.kt @@ -2,41 +2,32 @@ package eu.kanade.tachiyomi.extension.en.voyceme import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.asObservableSuccess -import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.util.asJsoup +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.buildJsonObject -import kotlinx.serialization.json.decodeFromJsonElement -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import kotlinx.serialization.json.put -import kotlinx.serialization.json.putJsonObject import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response -import org.jsoup.Jsoup -import org.jsoup.parser.Parser -import rx.Observable import uy.kohesive.injekt.injectLazy -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Locale import java.util.concurrent.TimeUnit class VoyceMe : HttpSource() { - override val name = "Voyce.Me" + // Renamed from "Voyce.Me" to "VoyceMe" as the site uses. + override val id = 4815322300278778429 + + override val name = "VoyceMe" override val baseUrl = "http://voyce.me" @@ -45,7 +36,8 @@ class VoyceMe : HttpSource() { override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .rateLimit(2, 1, TimeUnit.SECONDS) + .rateLimitHost(GRAPHQL_URL.toHttpUrl(), 1, 1, TimeUnit.SECONDS) + .rateLimitHost(STATIC_URL.toHttpUrl(), 2, 1, TimeUnit.SECONDS) .build() private val json: Json by injectLazy() @@ -55,23 +47,16 @@ class VoyceMe : HttpSource() { .add("Origin", baseUrl) .add("Referer", "$baseUrl/") - private fun genericComicBookFromObject(comic: VoyceMeComic): SManga = - SManga.create().apply { - title = comic.title - url = "/series/${comic.slug}" - thumbnail_url = STATIC_URL + comic.thumbnail - } - override fun popularMangaRequest(page: Int): Request { - val payload = buildJsonObject { - put("query", POPULAR_QUERY) - putJsonObject("variables") { - put("offset", (page - 1) * POPULAR_PER_PAGE) - put("limit", POPULAR_PER_PAGE) - } - } + val payload = GraphQlQuery( + query = POPULAR_QUERY, + variables = PopularQueryVariables( + offset = (page - 1) * POPULAR_PER_PAGE, + limit = POPULAR_PER_PAGE, + ), + ) - val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + val body = json.encodeToString(payload).toRequestBody(JSON_MEDIA_TYPE) val newHeaders = headersBuilder() .add("Content-Length", body.contentLength().toString()) @@ -82,26 +67,24 @@ class VoyceMe : HttpSource() { } override fun popularMangaParse(response: Response): MangasPage { - val result = json.parseToJsonElement(response.body.string()).jsonObject + val comicList = response.parseAs<VoyceMeSeriesResponse>() + .data.series.map(VoyceMeComic::toSManga) - val comicList = result["data"]!!.jsonObject["voyce_series"]!! - .let { json.decodeFromJsonElement<List<VoyceMeComic>>(it) } - .map(::genericComicBookFromObject) val hasNextPage = comicList.size == POPULAR_PER_PAGE return MangasPage(comicList, hasNextPage) } override fun latestUpdatesRequest(page: Int): Request { - val payload = buildJsonObject { - put("query", LATEST_QUERY) - putJsonObject("variables") { - put("offset", (page - 1) * POPULAR_PER_PAGE) - put("limit", POPULAR_PER_PAGE) - } - } + val payload = GraphQlQuery( + query = LATEST_QUERY, + variables = LatestQueryVariables( + offset = (page - 1) * POPULAR_PER_PAGE, + limit = POPULAR_PER_PAGE, + ), + ) - val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + val body = json.encodeToString(payload).toRequestBody(JSON_MEDIA_TYPE) val newHeaders = headersBuilder() .add("Content-Length", body.contentLength().toString()) @@ -111,28 +94,19 @@ class VoyceMe : HttpSource() { return POST(GRAPHQL_URL, newHeaders, body) } - override fun latestUpdatesParse(response: Response): MangasPage { - val result = json.parseToJsonElement(response.body.string()).jsonObject - - val comicList = result["data"]!!.jsonObject["voyce_series"]!! - .let { json.decodeFromJsonElement<List<VoyceMeComic>>(it) } - .map(::genericComicBookFromObject) - val hasNextPage = comicList.size == POPULAR_PER_PAGE - - return MangasPage(comicList, hasNextPage) - } + override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val payload = buildJsonObject { - put("query", SEARCH_QUERY) - putJsonObject("variables") { - put("searchTerm", "%$query%") - put("offset", (page - 1) * POPULAR_PER_PAGE) - put("limit", POPULAR_PER_PAGE) - } - } + val payload = GraphQlQuery( + query = SEARCH_QUERY, + variables = SearchQueryVariables( + searchTerm = "%$query%", + offset = (page - 1) * POPULAR_PER_PAGE, + limit = POPULAR_PER_PAGE, + ), + ) - val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + val body = json.encodeToString(payload).toRequestBody(JSON_MEDIA_TYPE) val newHeaders = headersBuilder() .add("Content-Length", body.contentLength().toString()) @@ -142,39 +116,19 @@ class VoyceMe : HttpSource() { return POST(GRAPHQL_URL, newHeaders, body) } - override fun searchMangaParse(response: Response): MangasPage { - val result = json.parseToJsonElement(response.body.string()).jsonObject - - val comicList = result["data"]!!.jsonObject["voyce_series"]!! - .let { json.decodeFromJsonElement<List<VoyceMeComic>>(it) } - .map(::genericComicBookFromObject) - val hasNextPage = comicList.size == POPULAR_PER_PAGE - - return MangasPage(comicList, hasNextPage) - } - - // Workaround to allow "Open in browser" use the real URL. - override fun fetchMangaDetails(manga: SManga): Observable<SManga> { - return client.newCall(mangaDetailsApiRequest(manga)) - .asObservableSuccess() - .map { response -> - mangaDetailsParse(response).apply { initialized = true } - } - } + override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) - private fun mangaDetailsApiRequest(manga: SManga): Request { + override fun mangaDetailsRequest(manga: SManga): Request { val comicSlug = manga.url .substringAfter("/series/") .substringBefore("/") - val payload = buildJsonObject { - put("query", DETAILS_QUERY) - putJsonObject("variables") { - put("slug", comicSlug) - } - } + val payload = GraphQlQuery( + query = DETAILS_QUERY, + variables = DetailsQueryVariables(slug = comicSlug), + ) - val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + val body = json.encodeToString(payload).toRequestBody(JSON_MEDIA_TYPE) val newHeaders = headersBuilder() .add("Content-Length", body.contentLength().toString()) @@ -185,18 +139,11 @@ class VoyceMe : HttpSource() { return POST(GRAPHQL_URL, newHeaders, body) } - override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { - val result = json.parseToJsonElement(response.body.string()).jsonObject - val comic = result["data"]!!.jsonObject["voyce_series"]!!.jsonArray[0].jsonObject - .let { json.decodeFromJsonElement<VoyceMeComic>(it) } - - title = comic.title - author = comic.author?.username.orEmpty() - description = Parser.unescapeEntities(comic.description.orEmpty(), true) - .let { Jsoup.parse(it).text() } - status = comic.status.orEmpty().toStatus() - genre = comic.genres.mapNotNull { it.genre?.title }.joinToString(", ") - thumbnail_url = STATIC_URL + comic.thumbnail + override fun getMangaUrl(manga: SManga) = baseUrl + manga.url + + override fun mangaDetailsParse(response: Response): SManga { + return response.parseAs<VoyceMeSeriesResponse>() + .data.series.first().toSManga() } override fun chapterListRequest(manga: SManga): Request { @@ -204,14 +151,12 @@ class VoyceMe : HttpSource() { .substringAfter("/series/") .substringBefore("/") - val payload = buildJsonObject { - put("query", CHAPTERS_QUERY) - putJsonObject("variables") { - put("slug", comicSlug) - } - } + val payload = GraphQlQuery( + query = CHAPTERS_QUERY, + variables = ChaptersQueryVariables(slug = comicSlug), + ) - val body = payload.toString().toRequestBody(JSON_MEDIA_TYPE) + val body = json.encodeToString(payload).toRequestBody(JSON_MEDIA_TYPE) val newHeaders = headersBuilder() .add("Content-Length", body.contentLength().toString()) @@ -223,72 +168,41 @@ class VoyceMe : HttpSource() { } override fun chapterListParse(response: Response): List<SChapter> { - val result = json.parseToJsonElement(response.body.string()).jsonObject - val comicBook = result["data"]!!.jsonObject["voyce_series"]!!.jsonArray[0].jsonObject - .let { json.decodeFromJsonElement<VoyceMeComic>(it) } + val comic = response.parseAs<VoyceMeSeriesResponse>().data.series.first() - return comicBook.chapters - .map { chapter -> chapterFromObject(chapter, comicBook) } - .distinctBy { chapter -> chapter.name } + return comic.chapters + .map { it.toSChapter(comic.slug) } + .distinctBy(SChapter::name) } - private fun chapterFromObject(chapter: VoyceMeChapter, comic: VoyceMeComic): SChapter = - SChapter.create().apply { - name = chapter.title - date_upload = chapter.createdAt.toDate() - url = "/series/${comic.slug}/${chapter.id}#comic" - } - override fun pageListRequest(chapter: SChapter): Request { - val newHeaders = headersBuilder() - .set("Referer", baseUrl + chapter.url.substringBeforeLast("/")) - .build() - - return GET(baseUrl + chapter.url, newHeaders) - } - - private fun pageListApiRequest(buildId: String, chapterUrl: String): Request { - val newHeaders = headersBuilder() - .set("Referer", baseUrl + chapterUrl) - .build() - - val comicSlug = chapterUrl - .substringAfter("/series/") - .substringBefore("/") - val chapterId = chapterUrl + val chapterId = chapter.url .substringAfterLast("/") .substringBefore("#") + .toInt() - return GET("$baseUrl/_next/data/$buildId/series/$comicSlug/$chapterId.json", newHeaders) - } - - override fun pageListParse(response: Response): List<Page> { - // GraphQL endpoints do not have the chapter images, so we need - // to get the buildId to fetch the chapter from NextJS static data. - val document = response.asJsoup() - val nextData = document.selectFirst("script#__NEXT_DATA__")!!.data() - val nextJson = json.parseToJsonElement(nextData).jsonObject + val payload = GraphQlQuery( + query = PAGES_QUERY, + variables = PagesQueryVariables(chapterId = chapterId), + ) - val buildId = nextJson["buildId"]!!.jsonPrimitive.content - val chapterUrl = response.request.url.toString().substringAfter(baseUrl) + val body = json.encodeToString(payload).toRequestBody(JSON_MEDIA_TYPE) - val dataRequest = pageListApiRequest(buildId, chapterUrl) - val dataResponse = client.newCall(dataRequest).execute() - val dataJson = json.parseToJsonElement(dataResponse.body.string()).jsonObject + val newHeaders = headersBuilder() + .add("Content-Length", body.contentLength().toString()) + .add("Content-Type", body.contentType().toString()) + .build() - val comic = dataJson["pageProps"]!!.jsonObject["series"]!! - .let { json.decodeFromJsonElement<VoyceMeComic>(it) } + return POST(GRAPHQL_URL, newHeaders, body) + } - val chapterId = response.request.url.toString() - .substringAfterLast("/") - .substringBefore("#") - .toInt() - val chapter = comic.chapters.firstOrNull { it.id == chapterId } - ?: throw Exception(CHAPTER_DATA_NOT_FOUND) + override fun getChapterUrl(chapter: SChapter) = baseUrl + chapter.url - return chapter.images.mapIndexed { i, page -> - Page(i, baseUrl, STATIC_URL + page.image) - } + override fun pageListParse(response: Response): List<Page> { + return response.parseAs<VoyceMeChapterImagesResponse>().data.images + .mapIndexed { i, page -> + Page(i, baseUrl, STATIC_URL + page.image) + } } override fun imageUrlParse(response: Response): String = "" @@ -302,33 +216,19 @@ class VoyceMe : HttpSource() { return GET(page.imageUrl!!, newHeaders) } - private fun String.toDate(): Long { - return try { - DATE_FORMATTER.parse(this)?.time ?: 0L - } catch (e: ParseException) { - 0L - } - } - - private fun String.toStatus(): Int = when (this) { - "completed" -> SManga.COMPLETED - "ongoing" -> SManga.ONGOING - else -> SManga.UNKNOWN + private inline fun <reified T> Response.parseAs(): T = use { + json.decodeFromString(it.body.string()) } companion object { private const val ACCEPT_ALL = "*/*" private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" - private const val STATIC_URL = "https://dlkfxmdtxtzpb.cloudfront.net/" + const val STATIC_URL = "https://dlkfxmdtxtzpb.cloudfront.net/" private const val GRAPHQL_URL = "https://graphql.voyce.me/v1/graphql" private const val POPULAR_PER_PAGE = 10 private val JSON_MEDIA_TYPE = "application/json; charset=utf-8".toMediaTypeOrNull() - - private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } - - private const val CHAPTER_DATA_NOT_FOUND = "Chapter data not found in website." } } diff --git a/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeDto.kt b/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeDto.kt index 45308fc4f2..0a04f3e7fc 100644 --- a/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeDto.kt +++ b/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeDto.kt @@ -1,7 +1,13 @@ package eu.kanade.tachiyomi.extension.en.voyceme +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import org.jsoup.Jsoup +import org.jsoup.parser.Parser +import java.text.SimpleDateFormat +import java.util.Locale @Serializable data class VoyceMeComic( @@ -14,7 +20,24 @@ data class VoyceMeComic( val status: String? = "", val thumbnail: String = "", val title: String = "", -) +) { + + fun toSManga(): SManga = SManga.create().apply { + title = this@VoyceMeComic.title + author = this@VoyceMeComic.author?.username.orEmpty() + description = Parser + .unescapeEntities(this@VoyceMeComic.description.orEmpty(), true) + .let { Jsoup.parseBodyFragment(it).text() } + status = when (this@VoyceMeComic.status.orEmpty()) { + "completed" -> SManga.COMPLETED + "ongoing" -> SManga.ONGOING + else -> SManga.UNKNOWN + } + genre = genres.mapNotNull { it.genre?.title }.joinToString(", ") + url = "/series/$slug" + thumbnail_url = VoyceMe.STATIC_URL + thumbnail + } +} @Serializable data class VoyceMeAuthor( @@ -37,9 +60,67 @@ data class VoyceMeChapter( val id: Int = -1, val images: List<VoyceMePage> = emptyList(), val title: String = "", -) +) { + + fun toSChapter(comicSlug: String): SChapter = SChapter.create().apply { + name = title + date_upload = runCatching { DATE_FORMATTER.parse(createdAt)?.time } + .getOrNull() ?: 0L + url = "/series/$comicSlug/$id#comic" + } + + companion object { + private val DATE_FORMATTER by lazy { SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH) } + } +} @Serializable data class VoyceMePage( val image: String = "", ) + +@Serializable +data class GraphQlQuery<T>( + val variables: T, + val query: String, +) + +@Serializable +data class GraphQlResponse<T>(val data: T) + +typealias VoyceMeSeriesResponse = GraphQlResponse<VoyceMeSeriesCollection> +typealias VoyceMeChapterImagesResponse = GraphQlResponse<VoyceChapterImagesCollection> + +@Serializable +data class VoyceMeSeriesCollection( + @SerialName("voyce_series") + val series: List<VoyceMeComic> = emptyList(), +) + +@Serializable +data class VoyceChapterImagesCollection( + @SerialName("voyce_chapter_images") + val images: List<VoyceMePage> = emptyList(), +) + +@Serializable +data class PopularQueryVariables( + val offset: Int, + val limit: Int, +) + +@Serializable +data class SearchQueryVariables( + val offset: Int, + val limit: Int, + val searchTerm: String, +) + +@Serializable +data class DetailsQueryVariables(val slug: String) + +@Serializable +data class PagesQueryVariables(val chapterId: Int) + +typealias LatestQueryVariables = PopularQueryVariables +typealias ChaptersQueryVariables = DetailsQueryVariables diff --git a/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeQueries.kt b/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeQueries.kt index 2a0326cfd9..18d62a7a43 100644 --- a/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeQueries.kt +++ b/src/en/voyceme/src/eu/kanade/tachiyomi/extension/en/voyceme/VoyceMeQueries.kt @@ -113,3 +113,16 @@ val CHAPTERS_QUERY: String = buildQuery { } """.trimIndent() } + +val PAGES_QUERY: String = buildQuery { + """ + query(%chapterId: Int!) { + voyce_chapter_images( + where: { chapter_id: { _eq: %chapterId } }, + order_by: { sort_order: asc } + ) { + image + } + } + """.trimIndent() +} diff --git a/src/en/vyvymanga/AndroidManifest.xml b/src/en/vyvymanga/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/en/vyvymanga/AndroidManifest.xml +++ b/src/en/vyvymanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/en/warforrayuba/AndroidManifest.xml b/src/en/warforrayuba/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/warforrayuba/AndroidManifest.xml +++ b/src/en/warforrayuba/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/webcomics/AndroidManifest.xml b/src/en/webcomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/webcomics/AndroidManifest.xml +++ b/src/en/webcomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/webnovel/AndroidManifest.xml b/src/en/webnovel/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/webnovel/AndroidManifest.xml +++ b/src/en/webnovel/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/en/zeroscans/AndroidManifest.xml b/src/en/zeroscans/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/en/zeroscans/AndroidManifest.xml +++ b/src/en/zeroscans/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/heavenmanga/AndroidManifest.xml b/src/es/heavenmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/heavenmanga/AndroidManifest.xml +++ b/src/es/heavenmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/ikuhentai/AndroidManifest.xml b/src/es/ikuhentai/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/ikuhentai/AndroidManifest.xml +++ b/src/es/ikuhentai/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/inmanga/AndroidManifest.xml b/src/es/inmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/inmanga/AndroidManifest.xml +++ b/src/es/inmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/kingsofdarkness/AndroidManifest.xml b/src/es/kingsofdarkness/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/kingsofdarkness/AndroidManifest.xml +++ b/src/es/kingsofdarkness/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/kumanga/AndroidManifest.xml b/src/es/kumanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/kumanga/AndroidManifest.xml +++ b/src/es/kumanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/lectormanga/AndroidManifest.xml b/src/es/lectormanga/AndroidManifest.xml index 93055d399f..9ebcab8550 100644 --- a/src/es/lectormanga/AndroidManifest.xml +++ b/src/es/lectormanga/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/es/leermangasxyz/AndroidManifest.xml b/src/es/leermangasxyz/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/es/leermangasxyz/AndroidManifest.xml +++ b/src/es/leermangasxyz/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/es/mangamx/AndroidManifest.xml b/src/es/mangamx/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/mangamx/AndroidManifest.xml +++ b/src/es/mangamx/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/mangatigre/AndroidManifest.xml b/src/es/mangatigre/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/mangatigre/AndroidManifest.xml +++ b/src/es/mangatigre/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/manhwasnet/AndroidManifest.xml b/src/es/manhwasnet/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/manhwasnet/AndroidManifest.xml +++ b/src/es/manhwasnet/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/es/manhwasnet/build.gradle b/src/es/manhwasnet/build.gradle index d993e87a40..9f42f27a27 100644 --- a/src/es/manhwasnet/build.gradle +++ b/src/es/manhwasnet/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Manhwas.net' pkgNameSuffix = 'es.manhwasnet' extClass = '.ManhwasNet' - extVersionCode = 6 + extVersionCode = 8 isNsfw = true } diff --git a/src/es/manhwasnet/src/eu/kanade/tachiyomi/extension/es/manhwasnet/ManhwasNet.kt b/src/es/manhwasnet/src/eu/kanade/tachiyomi/extension/es/manhwasnet/ManhwasNet.kt index ed3ff9ab3a..402727931c 100644 --- a/src/es/manhwasnet/src/eu/kanade/tachiyomi/extension/es/manhwasnet/ManhwasNet.kt +++ b/src/es/manhwasnet/src/eu/kanade/tachiyomi/extension/es/manhwasnet/ManhwasNet.kt @@ -1,5 +1,7 @@ package eu.kanade.tachiyomi.extension.es.manhwasnet +import android.webkit.CookieManager +import app.cash.quickjs.QuickJs import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList @@ -7,10 +9,15 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Cookie +import okhttp3.CookieJar +import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Request import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import java.io.IOException import java.util.Calendar class ManhwasNet : ParsedHttpSource() { @@ -20,20 +27,47 @@ class ManhwasNet : ParsedHttpSource() { override val name: String = "Manhwas.net" override val supportsLatest: Boolean = true - override val client = network.cloudflareClient.newBuilder() + private val cookieManager by lazy { CookieManager.getInstance() } + + override val client = network.client.newBuilder() .addInterceptor { chain -> - val originalRequest = chain.request() - val url = originalRequest.url.toString() - val response = chain.proceed(originalRequest) + val request = chain.request() + val url = request.url.toString() + val response = chain.proceed(request) if (response.headers["x-sucuri-cache"].isNullOrEmpty() && url.startsWith(baseUrl) && response.headers["x-sucuri-id"] != null) { - throw Exception("Sitio protegido - Abra en WebView para desbloquear.") + val script = response.asJsoup().selectFirst("script")?.data() + if (script != null) { + val a = script.split("(r)")[0].dropLast(1) + "r=r.replace('document.cookie','cookie');" + QuickJs.create().use { + val b = it.evaluate(a) as String + val sucuriCookie = it.evaluate(b.replace("location.", "").replace("reload();", "")) as String + val cookieName = sucuriCookie.split("=")[0] + val cookieValue = sucuriCookie.split("=")[1].replace(";path", "") + cookieManager.setCookie(url, "$cookieName=$cookieValue") + } + val newResponse = chain.proceed(request) + if (!newResponse.headers["x-sucuri-cache"].isNullOrEmpty()) return@addInterceptor newResponse + } + throw IOException("Sitio protegido - Abra en WebView para intentar desbloquear.") } return@addInterceptor response } + .cookieJar( + object : CookieJar { + override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) = + cookies.filter { it.matches(url) }.forEach { + cookieManager.setCookie(url.toString(), it.toString()) + } + + override fun loadForRequest(url: HttpUrl) = + cookieManager.getCookie(url.toString())?.split("; ") + ?.mapNotNull { Cookie.parse(url, it) } ?: emptyList() + }, + ) .build() override fun headersBuilder() = super.headersBuilder() - .set("Referer", "$baseUrl/") + .add("Referer", "$baseUrl/") override fun popularMangaRequest(page: Int): Request { val url = "$baseUrl/biblioteca".toHttpUrlOrNull()!!.newBuilder() @@ -52,7 +86,7 @@ class ManhwasNet : ParsedHttpSource() { } override fun latestUpdatesRequest(page: Int): Request { - return GET("$baseUrl/esp") + return GET("$baseUrl/esp", headers) } override fun latestUpdatesSelector() = popularMangaSelector() @@ -84,7 +118,7 @@ class ManhwasNet : ParsedHttpSource() { } } url.addQueryParameter("page", page.toString()) - return GET(url.build().toString()) + return GET(url.build().toString(), headers) } override fun searchMangaSelector() = popularMangaSelector() @@ -113,7 +147,7 @@ class ManhwasNet : ParsedHttpSource() { } override fun pageListParse(document: Document): List<Page> { - return document.select("#chapter_imgs img").mapIndexed { i, img -> + return document.select("#chapter_imgs img[src][src!=\"\"]").mapIndexed { i, img -> val url = img.attr("abs:src") Page(i, imageUrl = url) } diff --git a/src/es/olympusscanlation/AndroidManifest.xml b/src/es/olympusscanlation/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/es/olympusscanlation/AndroidManifest.xml +++ b/src/es/olympusscanlation/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/es/olympusscanlation/build.gradle b/src/es/olympusscanlation/build.gradle index 7b45e02081..5fe7a45f0f 100644 --- a/src/es/olympusscanlation/build.gradle +++ b/src/es/olympusscanlation/build.gradle @@ -7,7 +7,7 @@ ext { extName = 'Olympus Scanlation' pkgNameSuffix = 'es.olympusscanlation' extClass = '.OlympusScanlation' - extVersionCode = 3 + extVersionCode = 4 } -apply from: "$rootDir/common.gradle" \ No newline at end of file +apply from: "$rootDir/common.gradle" diff --git a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt index 826bce9868..518017fd40 100644 --- a/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt +++ b/src/es/olympusscanlation/src/eu/kanade/tachiyomi/extension/es/olympusscanlation/OlympusScanlation.kt @@ -18,8 +18,8 @@ import java.util.Locale class OlympusScanlation : HttpSource() { - override val baseUrl: String = "https://olympusscans.com" - private val apiBaseUrl: String = "https://dashboard.olympusscans.com" + override val baseUrl: String = "https://olympusv2.gg" + private val apiBaseUrl: String = "https://dashboard.olympusv2.gg" override val lang: String = "es" override val name: String = "Olympus Scanlation" override val versionId = 2 diff --git a/src/es/tmohentai/AndroidManifest.xml b/src/es/tmohentai/AndroidManifest.xml index 0356323240..7d60875374 100644 --- a/src/es/tmohentai/AndroidManifest.xml +++ b/src/es/tmohentai/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/es/tumangaonline/AndroidManifest.xml b/src/es/tumangaonline/AndroidManifest.xml index 7bb9dc39d6..35abf9090c 100644 --- a/src/es/tumangaonline/AndroidManifest.xml +++ b/src/es/tumangaonline/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/es/vcpvmp/AndroidManifest.xml b/src/es/vcpvmp/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/es/vcpvmp/AndroidManifest.xml +++ b/src/es/vcpvmp/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/animesama/AndroidManifest.xml b/src/fr/animesama/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/animesama/AndroidManifest.xml +++ b/src/fr/animesama/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/aralosbd/AndroidManifest.xml b/src/fr/aralosbd/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/aralosbd/AndroidManifest.xml +++ b/src/fr/aralosbd/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/aralosbd/build.gradle b/src/fr/aralosbd/build.gradle index 4f9d416b6d..a9dcce055b 100644 --- a/src/fr/aralosbd/build.gradle +++ b/src/fr/aralosbd/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'AralosBD' pkgNameSuffix = 'fr.aralosbd' extClass = '.AralosBD' - extVersionCode = 4 + extVersionCode = 6 } apply from: "$rootDir/common.gradle" diff --git a/src/fr/aralosbd/ic_launcher-playstore.png b/src/fr/aralosbd/ic_launcher-playstore.png deleted file mode 100644 index 19b77f665a..0000000000 Binary files a/src/fr/aralosbd/ic_launcher-playstore.png and /dev/null differ diff --git a/src/fr/aralosbd/res/mipmap-hdpi/ic_launcher.png b/src/fr/aralosbd/res/mipmap-hdpi/ic_launcher.png index 2830267296..56d4541c36 100644 Binary files a/src/fr/aralosbd/res/mipmap-hdpi/ic_launcher.png and b/src/fr/aralosbd/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/fr/aralosbd/res/mipmap-mdpi/ic_launcher.png b/src/fr/aralosbd/res/mipmap-mdpi/ic_launcher.png index 09fa504faa..21aa94f14e 100644 Binary files a/src/fr/aralosbd/res/mipmap-mdpi/ic_launcher.png and b/src/fr/aralosbd/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/fr/aralosbd/res/mipmap-xhdpi/ic_launcher.png b/src/fr/aralosbd/res/mipmap-xhdpi/ic_launcher.png index 47b43dad4f..2c0dadf79d 100644 Binary files a/src/fr/aralosbd/res/mipmap-xhdpi/ic_launcher.png and b/src/fr/aralosbd/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/fr/aralosbd/res/mipmap-xxhdpi/ic_launcher.png b/src/fr/aralosbd/res/mipmap-xxhdpi/ic_launcher.png index 5a84b5e344..1b79c51394 100644 Binary files a/src/fr/aralosbd/res/mipmap-xxhdpi/ic_launcher.png and b/src/fr/aralosbd/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/fr/aralosbd/res/mipmap-xxxhdpi/ic_launcher.png b/src/fr/aralosbd/res/mipmap-xxxhdpi/ic_launcher.png index f8bcb7a52f..843c146e75 100644 Binary files a/src/fr/aralosbd/res/mipmap-xxxhdpi/ic_launcher.png and b/src/fr/aralosbd/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/fr/aralosbd/res/web_hi_res_512.png b/src/fr/aralosbd/res/web_hi_res_512.png index 6a45a326f6..a0d2b24aa0 100644 Binary files a/src/fr/aralosbd/res/web_hi_res_512.png and b/src/fr/aralosbd/res/web_hi_res_512.png differ diff --git a/src/fr/aralosbd/src/eu/kanade/tachiyomi/extension/fr/aralosbd/AralosBD.kt b/src/fr/aralosbd/src/eu/kanade/tachiyomi/extension/fr/aralosbd/AralosBD.kt index 4d2fe2e666..b281d55961 100644 --- a/src/fr/aralosbd/src/eu/kanade/tachiyomi/extension/fr/aralosbd/AralosBD.kt +++ b/src/fr/aralosbd/src/eu/kanade/tachiyomi/extension/fr/aralosbd/AralosBD.kt @@ -26,6 +26,7 @@ class AralosBD : HttpSource() { val BOLD_REGEX = "\\*+\\s*([^\\*]*)\\s*\\*+".toRegex() val ITALIC_REGEX = "_+\\s*([^_]*)\\s*_+".toRegex() val ICON_REGEX = ":+[a-zA-Z]+:".toRegex() + val PAGE_REGEX = "page:([0-9]+)".toRegex() } private fun cleanString(string: String): String { @@ -46,28 +47,60 @@ class AralosBD : HttpSource() { private val json: Json by injectLazy() override fun popularMangaRequest(page: Int): Request { - // This is the search query used for the 'recommandations' in the front page - return GET("$baseUrl/manga/search?s=sort:allviews;limit:16;-id:3", headers) + // Let's use a request that just query everything by page, sorted by total views (title + chapters) + return GET("$baseUrl/manga/search?s=sort:allviews;limit:24;-id:3;page:${page - 1};order:desc", headers) } override fun popularMangaParse(response: Response): MangasPage { val searchResult = json.decodeFromString<AralosBDSearchResult>(response.body.string()) - return MangasPage(searchResult.mangas.map(::searchMangaToSManga), false) + var hasNextPage = false + val pageMatch = PAGE_REGEX.find(response.request.url.toString()) + if (pageMatch != null && pageMatch.groupValues.count() > 1) { + val currentPage = pageMatch.groupValues[1].toInt() + hasNextPage = currentPage < searchResult.page_count - 1 + } + + return MangasPage(searchResult.mangas.map(::searchMangaToSManga), hasNextPage) } - override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException("Not used.") - override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used.") + override fun latestUpdatesParse(response: Response): MangasPage { + val searchResult = json.decodeFromString<AralosBDSearchResult>(response.body.string()) + + var hasNextPage = false + val pageMatch = PAGE_REGEX.find(response.request.url.toString()) + if (pageMatch != null && pageMatch.groupValues.count() > 1) { + val currentPage = pageMatch.groupValues[1].toInt() + hasNextPage = currentPage < searchResult.page_count - 1 + } + + return MangasPage(searchResult.mangas.map(::searchMangaToSManga), hasNextPage) + } + + override fun latestUpdatesRequest(page: Int): Request { + // That's almost exactly the same stuff that the popular request, simply ordered differently + // A new title will always have a greater ID, so we can sort by ID. Using year would not be + // accurate because it's the release year + // It would be better to sort by last updated, but this is not yet in the API + return GET("$baseUrl/manga/search?s=sort:id;limit:24;-id:3;page:${page - 1};order:desc", headers) + } override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { // For a basic search, we call the appropriate endpoint - return GET("$baseUrl/manga/search?s=text:$query", headers) + return GET("$baseUrl/manga/search?s=page:${page - 1};sort:id;order:desc;text:$query", headers) } override fun searchMangaParse(response: Response): MangasPage { val searchResult = json.decodeFromString<AralosBDSearchResult>(response.body.string()) - return MangasPage(searchResult.mangas.map(::searchMangaToSManga), false) + var hasNextPage = false + val pageMatch = PAGE_REGEX.find(response.request.url.toString()) + if (pageMatch != null && pageMatch.groupValues.count() > 1) { + val currentPage = pageMatch.groupValues[1].toInt() + hasNextPage = currentPage < searchResult.page_count - 1 + } + + return MangasPage(searchResult.mangas.map(::searchMangaToSManga), hasNextPage) } override fun mangaDetailsRequest(manga: SManga): Request { @@ -167,6 +200,8 @@ data class AralosBDSearchManga( @Serializable data class AralosBDSearchResult( val error: Int = 0, + val result_count: Int = 0, + val page_count: Int = 0, val mangas: List<AralosBDSearchManga> = emptyList(), ) diff --git a/src/fr/fmteam/AndroidManifest.xml b/src/fr/fmteam/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/fmteam/AndroidManifest.xml +++ b/src/fr/fmteam/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/fmteam/build.gradle b/src/fr/fmteam/build.gradle index a4f1c51338..2a3de48264 100644 --- a/src/fr/fmteam/build.gradle +++ b/src/fr/fmteam/build.gradle @@ -1,11 +1,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' ext { extName = 'FMTEAM' pkgNameSuffix = 'fr.fmteam' extClass = '.FMTEAM' - extVersionCode = 1 + extVersionCode = 2 } apply from: "$rootDir/common.gradle" diff --git a/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAM.kt b/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAM.kt index f496fc8a2f..bed5cc3783 100644 --- a/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAM.kt +++ b/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAM.kt @@ -6,114 +6,124 @@ import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.ParsedHttpSource -import eu.kanade.tachiyomi.util.asJsoup +import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import okhttp3.Request import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import java.text.ParseException +import uy.kohesive.injekt.injectLazy import java.text.SimpleDateFormat -import java.util.Date import java.util.Locale +import java.util.TimeZone -class FMTEAM : ParsedHttpSource() { +class FMTEAM : HttpSource() { override val name = "FMTEAM" override val baseUrl = "https://fmteam.fr" override val lang = "fr" override val supportsLatest = true + override val versionId = 2 - + private val json: Json by injectLazy() companion object { - private val dateFormat = SimpleDateFormat("yyyy.MM.dd", Locale.FRENCH) - private val allPagesRegex = "\"url\":\"(.*?)\"".toRegex() - private val authorRegex = "Author: *(.*) Synopsis".toRegex() - private val descriptionRegex = "Synopsis: *(.*)".toRegex() + private val DATE_FORMATTER by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + } } // All manga - override fun popularMangaRequest(page: Int) = GET("$baseUrl/directory/") - override fun popularMangaSelector() = "#content .panel .list.series .group" - override fun popularMangaNextPageSelector(): String? = null - override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.selectFirst(".title a")!!.text().trim() - setUrlWithoutDomain(element.selectFirst("a")!!.attr("href")) - thumbnail_url = element.select(".preview").attr("src") + override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/api/comics") + + override fun popularMangaParse(response: Response): MangasPage { + val results = json.decodeFromString<FmteamComicListPage>(response.body.string()) + + return MangasPage(results.comics.sortedByDescending { it.views }.map(::fmTeamComicToSManga), false) } // Latest - override fun latestUpdatesRequest(page: Int) = GET(baseUrl) - override fun latestUpdatesSelector() = ".panel .list .group" - override fun latestUpdatesNextPageSelector() = ".prevnext .next .gbutton.fright:last-child a" - override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { - title = element.selectFirst(".title a")!!.text().trim() - setUrlWithoutDomain(element.select(".title a").attr("href")) - } + override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/api/comics") + override fun latestUpdatesParse(response: Response): MangasPage { - val document = response.asJsoup() - val mangas = document.select(latestUpdatesSelector()).map { element -> - latestUpdatesFromElement(element) - }.distinctBy { it.title } - return MangasPage(mangas, false) + val results = json.decodeFromString<FmteamComicListPage>(response.body.string()) + + return MangasPage(results.comics.sortedByDescending { parseDate(it.last_chapter.published_on) }.map(::fmTeamComicToSManga), false) } // Search + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request = GET("$baseUrl/api/search/$query") + override fun searchMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - var mangas = document.select(popularMangaSelector()).map { popularMangaFromElement(it) } - val query = response.request.headers["query"] - if (query != null) { - mangas = mangas.filter { it.title.contains(query, true) } - } - return MangasPage(mangas, false) - } - override fun searchMangaFromElement(element: Element): SManga = popularMangaFromElement(element) - override fun searchMangaNextPageSelector(): String? = null - override fun searchMangaSelector(): String = popularMangaSelector() - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val headers = headersBuilder() - .add("query", query) - .build() - return GET("$baseUrl/directory/", headers) + val results = json.decodeFromString<FmteamComicListPage>(response.body.string()) + + return MangasPage(results.comics.map(::fmTeamComicToSManga), false) } // Manga details - override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { - thumbnail_url = document.select(".comic.info .thumbnail img").attr("src") - val authorSynopsis = document.select(".large.comic .info").text().trim() - if (authorSynopsis.contains("Author")) { - author = authorRegex.find(authorSynopsis)!!.groups[1]!!.value.trim() - } - description = descriptionRegex.find(authorSynopsis)!!.groups[1]!!.value.trim() + override fun mangaDetailsRequest(manga: SManga): Request = GET("$baseUrl/api${manga.url}") + + override fun mangaDetailsParse(response: Response): SManga { + val results = json.decodeFromString<FmteamComicDetailPage>(response.body.string()) + + return fmTeamComicToSManga(results.comic) + } + + override fun getMangaUrl(manga: SManga): String { + return "$baseUrl${manga.url}" } // Chapter list - override fun chapterListSelector() = ".list .group .element" - override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - setUrlWithoutDomain(element.select(".title a").attr("href")) - name = element.select(".title a").attr("title").trim() - val date = element.select(".meta_r").text().trim().split(", ")[1] - date_upload = if (date === "Aujourd'hui") { - dateFormat.format(Date()).toLong() - } else { - try { - dateFormat.parse(date)!!.time - } catch (e: ParseException) { - 0L - } - } + override fun chapterListRequest(manga: SManga): Request = GET("$baseUrl/api${manga.url}") + + override fun chapterListParse(response: Response): List<SChapter> { + val results = json.decodeFromString<FmteamComicDetailPage>(response.body.string()) + + return results.comic.chapters?.map(::fmTeamChapterToSChapter) ?: emptyList() + } + + override fun getChapterUrl(chapter: SChapter): String { + return "$baseUrl${chapter.url}" } // Pages - override fun pageListParse(document: Document): List<Page> { - val pages = mutableListOf<Page>() - val script = document.selectFirst("script:containsData(pages =)")!!.data() - val allPages = allPagesRegex.findAll(script) - allPages.asIterable().mapIndexed { i, it -> - pages.add(Page(i, "", it.groupValues[1].replace("\\/", "/"))) + override fun pageListRequest(chapter: SChapter): Request = GET("$baseUrl/api${chapter.url}") + + override fun pageListParse(response: Response): List<Page> { + val results = json.decodeFromString<FmteamChapterDetailPage>(response.body.string()) + + return results.chapter.pages.orEmpty() + .mapIndexed { i, page -> Page(i, "${results.chapter.url}#${i + 1}", page) } + } + + override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException("Not used.") + + // Utils + private fun fmTeamComicToSManga(comic: FmteamComic): SManga = SManga.create().apply { + url = comic.url + title = comic.title + artist = comic.artist + author = comic.author + description = comic.description + genre = comic.genres.joinToString { it.name } + status = when (comic.status) { + "En cours" -> SManga.ONGOING + "Termin\u00e9" -> SManga.COMPLETED + else -> SManga.UNKNOWN } - return pages + thumbnail_url = comic.thumbnail + initialized = true + } + + private fun fmTeamChapterToSChapter(chapter: FmteamChapter): SChapter = SChapter.create().apply { + url = chapter.url + name = chapter.full_title + date_upload = parseDate(chapter.published_on) + scanlator = chapter.teams.filterNotNull().joinToString { it.name } + } + + private fun parseDate(dateStr: String): Long { + return runCatching { DATE_FORMATTER.parse(dateStr)?.time } + .getOrNull() ?: 0L } - override fun imageUrlParse(document: Document): String = throw Exception("Not used") } diff --git a/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAMDto.kt b/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAMDto.kt new file mode 100644 index 0000000000..cf6074a767 --- /dev/null +++ b/src/fr/fmteam/src/eu/kanade/tachiyomi/extension/fr/fmteam/FMTEAMDto.kt @@ -0,0 +1,54 @@ +package eu.kanade.tachiyomi.extension.fr.fmteam + +import kotlinx.serialization.Serializable + +@Serializable +data class FmteamComicListPage( + val comics: List<FmteamComic>, +) + +@Serializable +data class FmteamComicDetailPage( + val comic: FmteamComic, +) + +@Serializable +data class FmteamChapterDetailPage( + val chapter: FmteamChapter, +) + +@Serializable +data class FmteamComic( + val title: String, + val thumbnail: String, + val description: String, + val author: String, + val artist: String?, + val genres: List<FmteamTag>, + val status: String, + val views: Int, + val url: String, + val last_chapter: FmteamChapter, + val chapters: List<FmteamChapter>?, +) + +@Serializable +data class FmteamTag( + val name: String, +) + +@Serializable +data class FmteamChapter( + val full_title: String, + val chapter: Int, + val subchapter: Int?, + val teams: List<FmteamTeam?>, + val published_on: String, + val url: String, + val pages: List<String>?, +) + +@Serializable +data class FmteamTeam( + val name: String, +) diff --git a/src/fr/furyosquad/AndroidManifest.xml b/src/fr/furyosquad/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/furyosquad/AndroidManifest.xml +++ b/src/fr/furyosquad/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/furyosquad/build.gradle b/src/fr/furyosquad/build.gradle index 12ac81a466..7b8505eaf2 100644 --- a/src/fr/furyosquad/build.gradle +++ b/src/fr/furyosquad/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'FuryoSquad' pkgNameSuffix = 'fr.furyosquad' extClass = '.FuryoSquad' - extVersionCode = 2 + extVersionCode = 3 } apply from: "$rootDir/common.gradle" diff --git a/src/fr/furyosquad/src/eu/kanade/tachiyomi/extension/fr/furyosquad/FuryoSquad.kt b/src/fr/furyosquad/src/eu/kanade/tachiyomi/extension/fr/furyosquad/FuryoSquad.kt index 358bfdb172..b1ebe54d7d 100644 --- a/src/fr/furyosquad/src/eu/kanade/tachiyomi/extension/fr/furyosquad/FuryoSquad.kt +++ b/src/fr/furyosquad/src/eu/kanade/tachiyomi/extension/fr/furyosquad/FuryoSquad.kt @@ -25,7 +25,7 @@ class FuryoSquad : ParsedHttpSource() { override val name = "FuryoSquad" - override val baseUrl = "https://www.furyosquad.com/" + override val baseUrl = "https://www.furyosociety.com/" override val lang = "fr" diff --git a/src/fr/japanread/AndroidManifest.xml b/src/fr/japanread/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/japanread/AndroidManifest.xml +++ b/src/fr/japanread/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/japscan/AndroidManifest.xml b/src/fr/japscan/AndroidManifest.xml deleted file mode 100644 index 30deb7f797..0000000000 --- a/src/fr/japscan/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> diff --git a/src/fr/japscan/build.gradle b/src/fr/japscan/build.gradle deleted file mode 100644 index 4733ebba08..0000000000 --- a/src/fr/japscan/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlinx-serialization' - -ext { - extName = 'Japscan' - pkgNameSuffix = 'fr.japscan' - extClass = '.Japscan' - extVersionCode = 41 -} - -apply from: "$rootDir/common.gradle" diff --git a/src/fr/japscan/res/mipmap-hdpi/ic_launcher.png b/src/fr/japscan/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 4659fbb8cb..0000000000 Binary files a/src/fr/japscan/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/fr/japscan/res/mipmap-mdpi/ic_launcher.png b/src/fr/japscan/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 19a5531b02..0000000000 Binary files a/src/fr/japscan/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/fr/japscan/res/mipmap-xhdpi/ic_launcher.png b/src/fr/japscan/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 7320f09ebd..0000000000 Binary files a/src/fr/japscan/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/fr/japscan/res/mipmap-xxhdpi/ic_launcher.png b/src/fr/japscan/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 52e248fa63..0000000000 Binary files a/src/fr/japscan/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/fr/japscan/res/mipmap-xxxhdpi/ic_launcher.png b/src/fr/japscan/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 166cd3cfe2..0000000000 Binary files a/src/fr/japscan/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/fr/japscan/res/web_hi_res_512.png b/src/fr/japscan/res/web_hi_res_512.png deleted file mode 100644 index 3af8c2771f..0000000000 Binary files a/src/fr/japscan/res/web_hi_res_512.png and /dev/null differ diff --git a/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt b/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt deleted file mode 100644 index 964daeb89e..0000000000 --- a/src/fr/japscan/src/eu/kanade/tachiyomi/extension/fr/japscan/Japscan.kt +++ /dev/null @@ -1,383 +0,0 @@ -package eu.kanade.tachiyomi.extension.fr.japscan - -import android.app.Application -import android.content.SharedPreferences -import android.net.Uri -import android.util.Base64 -import android.util.Log -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.POST -import eu.kanade.tachiyomi.network.interceptor.rateLimit -import eu.kanade.tachiyomi.source.ConfigurableSource -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.ParsedHttpSource -import eu.kanade.tachiyomi.util.asJsoup -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.jsonArray -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive -import okhttp3.FormBody -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import uy.kohesive.injekt.injectLazy -import java.text.ParseException -import java.text.SimpleDateFormat -import java.util.Locale - -class Japscan : ConfigurableSource, ParsedHttpSource() { - - override val id: Long = 11 - - override val name = "Japscan" - - override val baseUrl = "https://www.japscan.lol" - - override val lang = "fr" - - override val supportsLatest = true - - private val json: Json by injectLazy() - - private val preferences: SharedPreferences by lazy { - Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) - } - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .rateLimit(1, 2) - .build() - - companion object { - val dateFormat by lazy { - SimpleDateFormat("dd MMM yyyy", Locale.US) - } - private const val SHOW_SPOILER_CHAPTERS_Title = "Les chapitres en Anglais ou non traduit sont upload en tant que \" Spoilers \" sur Japscan" - private const val SHOW_SPOILER_CHAPTERS = "JAPSCAN_SPOILER_CHAPTERS" - private val prefsEntries = arrayOf("Montrer uniquement les chapitres traduit en Français", "Montrer les chapitres spoiler") - private val prefsEntryValues = arrayOf("hide", "show") - } - - private fun chapterListPref() = preferences.getString(SHOW_SPOILER_CHAPTERS, "hide") - - override fun headersBuilder() = super.headersBuilder() - .add("referer", "$baseUrl/") - - // Popular - override fun popularMangaRequest(page: Int): Request { - return GET("$baseUrl/mangas/", headers) - } - - override fun popularMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - pageNumberDoc = document - - val mangas = document.select(popularMangaSelector()).map { element -> - popularMangaFromElement(element) - } - val hasNextPage = false - return MangasPage(mangas, hasNextPage) - } - - override fun popularMangaNextPageSelector(): String? = null - - override fun popularMangaSelector() = "#top_mangas_week li" - - override fun popularMangaFromElement(element: Element): SManga { - val manga = SManga.create() - element.select("a").first()!!.let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text() - manga.thumbnail_url = "$baseUrl/imgs/${it.attr("href").replace(Regex("/$"),".jpg").replace("manga","mangas")}".lowercase(Locale.ROOT) - } - return manga - } - - // Latest - override fun latestUpdatesRequest(page: Int): Request { - return GET(baseUrl, headers) - } - - override fun latestUpdatesParse(response: Response): MangasPage { - val document = response.asJsoup() - val mangas = document.select(latestUpdatesSelector()) - .distinctBy { element -> element.select("a").attr("href") } - .map { element -> - latestUpdatesFromElement(element) - } - val hasNextPage = false - return MangasPage(mangas, hasNextPage) - } - - override fun latestUpdatesNextPageSelector(): String? = null - - override fun latestUpdatesSelector() = "#chapters h3.text-truncate, #chapters_list h3.text-truncate" - - override fun latestUpdatesFromElement(element: Element): SManga = popularMangaFromElement(element) - - // Search - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - if (query.isEmpty()) { - val uri = Uri.parse(baseUrl).buildUpon() - .appendPath("mangas") - filters.forEach { filter -> - when (filter) { - is TextField -> uri.appendPath(((page - 1) + filter.state.toInt()).toString()) - is PageList -> uri.appendPath(((page - 1) + filter.values[filter.state]).toString()) - else -> {} - } - } - return GET(uri.toString(), headers) - } else { - val formBody = FormBody.Builder() - .add("search", query) - .build() - val searchHeaders = headers.newBuilder() - .add("X-Requested-With", "XMLHttpRequest") - .build() - - try { - val searchRequest = POST("$baseUrl/live-search/", searchHeaders, formBody) - val searchResponse = client.newCall(searchRequest).execute() - - if (!searchResponse.isSuccessful) { - throw Exception("Code ${searchResponse.code} inattendu") - } - - val jsonResult = json.parseToJsonElement(searchResponse.body.string()).jsonArray - - if (jsonResult.isEmpty()) { - Log.d("japscan", "Search not returning anything, using duckduckgo") - throw Exception("Pas de données") - } - - return searchRequest - } catch (e: Exception) { - // Fallback to duckduckgo if the search does not return any result - val uri = Uri.parse("https://duckduckgo.com/lite/").buildUpon() - .appendQueryParameter("q", "$query site:$baseUrl/manga/") - .appendQueryParameter("kd", "-1") - return GET(uri.toString(), headers) - } - } - } - - override fun searchMangaNextPageSelector(): String = "li.page-item:last-child:not(li.active),.next_form .navbutton" - - override fun searchMangaSelector(): String = "div.card div.p-2, a.result-link" - - override fun searchMangaParse(response: Response): MangasPage { - if ("live-search" in response.request.url.toString()) { - val jsonResult = json.parseToJsonElement(response.body.string()).jsonArray - - val mangaList = jsonResult.map { jsonEl -> searchMangaFromJson(jsonEl.jsonObject) } - - return MangasPage(mangaList, hasNextPage = false) - } - - return super.searchMangaParse(response) - } - - override fun searchMangaFromElement(element: Element): SManga { - return if (element.attr("class") == "result-link") { - SManga.create().apply { - title = element.text().substringAfter(" ").substringBefore(" | JapScan") - setUrlWithoutDomain(element.attr("abs:href")) - } - } else { - SManga.create().apply { - thumbnail_url = element.select("img").attr("abs:src") - element.select("p a").let { - title = it.text() - url = it.attr("href") - } - } - } - } - - private fun searchMangaFromJson(jsonObj: JsonObject): SManga = SManga.create().apply { - title = jsonObj["name"]!!.jsonPrimitive.content - url = jsonObj["url"]!!.jsonPrimitive.content - } - - override fun mangaDetailsParse(document: Document): SManga { - val infoElement = document.selectFirst("#main .card-body")!! - - val manga = SManga.create() - manga.thumbnail_url = infoElement.select("img").attr("abs:src") - - val infoRows = infoElement.select(".row, .d-flex") - infoRows.select("p").forEach { el -> - when (el.select("span").text().trim()) { - "Auteur(s):" -> manga.author = el.text().replace("Auteur(s):", "").trim() - "Artiste(s):" -> manga.artist = el.text().replace("Artiste(s):", "").trim() - "Genre(s):" -> manga.genre = el.text().replace("Genre(s):", "").trim() - "Statut:" -> manga.status = el.text().replace("Statut:", "").trim().let { - parseStatus(it) - } - } - } - manga.description = infoElement.select("div:contains(Synopsis) + p").text().orEmpty() - - return manga - } - - private fun parseStatus(status: String) = when { - status.contains("En Cours") -> SManga.ONGOING - status.contains("Terminé") -> SManga.COMPLETED - else -> SManga.UNKNOWN - } - - override fun chapterListSelector() = "#chapters_list > div.collapse > div.chapters_list" + - if (chapterListPref() == "hide") { ":not(:has(.badge:contains(SPOILER),.badge:contains(RAW),.badge:contains(VUS)))" } else { "" } - // JapScan sometimes uploads some "spoiler preview" chapters, containing 2 or 3 untranslated pictures taken from a raw. Sometimes they also upload full RAWs/US versions and replace them with a translation as soon as available. - // Those have a span.badge "SPOILER" or "RAW". The additional pseudo selector makes sure to exclude these from the chapter list. - - override fun chapterFromElement(element: Element): SChapter { - val urlElement = element.selectFirst("a")!! - - val chapter = SChapter.create() - chapter.setUrlWithoutDomain(urlElement.attr("href")) - chapter.name = urlElement.ownText() - // Using ownText() doesn't include childs' text, like "VUS" or "RAW" badges, in the chapter name. - chapter.date_upload = element.selectFirst("span")!!.text().trim().let { parseChapterDate(it) } - return chapter - } - - private fun parseChapterDate(date: String): Long { - return try { - dateFormat.parse(date)?.time ?: 0 - } catch (e: ParseException) { - 0L - } - } - - private val shortDecodingStringsRe: Regex = Regex("""'([\dA-Z]{2})'""", RegexOption.IGNORE_CASE) - private val longDecodingStringsRe: Regex = Regex("""'([\dA-Z]{20})'""", RegexOption.IGNORE_CASE) - - override fun pageListParse(document: Document): List<Page> { - /* - JapScan stores chapter metadata in a `#data` element, and in the `data-data` attribute. - - This data is scrambled base64, and to unscramble it this code searches in the ZJS for - two strings of length 62 (base64 minus `+` and `/`), creating a character map. - - Since figuring out how to properly map characters would be more effort than I want to - put in, this just flips around the charsets if the first attempt didn't succeed. - */ - val zjsurl = document.getElementsByTag("script").first { - it.attr("src").contains("zjs", ignoreCase = true) - }.attr("src") - Log.d("japscan", "ZJS at $zjsurl") - val zjs = client.newCall(GET(baseUrl + zjsurl, headers)).execute().body.string() - - // Japscan split its lookup table into 1 string of 2 chars + 2 * 3 strings of 20 chars - // Try to find the 2 set of 3 strings of 20 chars first - val rawLongStringLookupTables = longDecodingStringsRe.findAll(zjs).map { - it.groupValues[1] - }.toList() - - if (rawLongStringLookupTables.size != 6) { - throw Exception("Attendait 6 chaînes de recherche dans ZJS, a trouvé ${rawLongStringLookupTables.size}") - } - - // Then try to find the 2 strings of 2 chars (will output 3 values the first is something else) - val rawShortStringLookupTables = shortDecodingStringsRe.findAll(zjs).map { - it.groupValues[1] - }.toList() - - if (rawShortStringLookupTables.size != 3) { - throw Exception("Attendait 3 chaînes de recherche dans ZJS, a trouvé ${rawShortStringLookupTables.size}") - } - - // Once we found the 8 strings, assuming they are always in the same order - // Since Japscan reverse the char order, reverse the strings - val stringLookupTables = listOf( - rawShortStringLookupTables[1].reversed() + rawLongStringLookupTables[5].reversed() + rawLongStringLookupTables[2].reversed() + rawLongStringLookupTables[0].reversed(), - rawShortStringLookupTables[2].reversed() + rawLongStringLookupTables[3].reversed() + rawLongStringLookupTables[4].reversed() + rawLongStringLookupTables[1].reversed(), - ) - - val scrambledData = document.getElementById("data")!!.attr("data-data") - - for (i in 0..1) { - Log.d("japscan", "descramble attempt $i") - val otherIndice = if (i == 0) 1 else 0 - val lookupTable = stringLookupTables[i].zip(stringLookupTables[otherIndice]).toMap() - try { - val unscrambledData = scrambledData.map { lookupTable[it] ?: it }.joinToString("") - if (!unscrambledData.startsWith("ey")) { - // `ey` is the Base64 representation of a curly bracket. Since we're expecting a - // JSON object, we're counting this attempt as failed if it doesn't start with a - // curly bracket. - continue - } - val decoded = Base64.decode(unscrambledData, Base64.DEFAULT).toString(Charsets.UTF_8) - - val data = json.parseToJsonElement(decoded).jsonObject - - return data["imagesLink"]!!.jsonArray.mapIndexed { idx, it -> - Page(idx, imageUrl = it.jsonPrimitive.content) - } - } catch (_: Throwable) {} - } - - throw Exception("Les deux tentatives de désembrouillage ont échoué") - } - - override fun imageUrlParse(document: Document): String = "" - - // Filters - private class TextField(name: String) : Filter.Text(name) - - private class PageList(pages: Array<Int>) : Filter.Select<Int>("Page #", arrayOf(0, *pages)) - - override fun getFilterList(): FilterList { - val totalPages = pageNumberDoc?.select("li.page-item:last-child a")?.text() - val pagelist = mutableListOf<Int>() - return if (!totalPages.isNullOrEmpty()) { - for (i in 0 until totalPages.toInt()) { - pagelist.add(i + 1) - } - FilterList( - Filter.Header("Page alphabétique"), - PageList(pagelist.toTypedArray()), - ) - } else { - FilterList( - Filter.Header("Page alphabétique"), - TextField("Page #"), - Filter.Header("Appuyez sur reset pour la liste"), - ) - } - } - - private var pageNumberDoc: Document? = null - - // Prefs - override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { - val chapterListPref = androidx.preference.ListPreference(screen.context).apply { - key = SHOW_SPOILER_CHAPTERS_Title - title = SHOW_SPOILER_CHAPTERS_Title - entries = prefsEntries - entryValues = prefsEntryValues - summary = "%s" - - setOnPreferenceChangeListener { _, newValue -> - val selected = newValue as String - val index = this.findIndexOfValue(selected) - val entry = entryValues[index] as String - preferences.edit().putString(SHOW_SPOILER_CHAPTERS, entry).commit() - } - } - screen.addPreference(chapterListPref) - } -} diff --git a/src/fr/lirescan/AndroidManifest.xml b/src/fr/lirescan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/lirescan/AndroidManifest.xml +++ b/src/fr/lirescan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/mangakawaii/AndroidManifest.xml b/src/fr/mangakawaii/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/mangakawaii/AndroidManifest.xml +++ b/src/fr/mangakawaii/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/scanmanga/AndroidManifest.xml b/src/fr/scanmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/scanmanga/AndroidManifest.xml +++ b/src/fr/scanmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/fr/scantradunion/AndroidManifest.xml b/src/fr/scantradunion/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/fr/scantradunion/AndroidManifest.xml +++ b/src/fr/scantradunion/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/KomikFan/AndroidManifest.xml b/src/id/KomikFan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/id/KomikFan/AndroidManifest.xml +++ b/src/id/KomikFan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/bacakomik/AndroidManifest.xml b/src/id/bacakomik/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/id/bacakomik/AndroidManifest.xml +++ b/src/id/bacakomik/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/comicfx/AndroidManifest.xml b/src/id/comicfx/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/id/comicfx/AndroidManifest.xml +++ b/src/id/comicfx/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/doujindesu/AndroidManifest.xml b/src/id/doujindesu/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/id/doujindesu/AndroidManifest.xml +++ b/src/id/doujindesu/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/doujindesu/build.gradle b/src/id/doujindesu/build.gradle index 5465b3b504..6cd9db04f1 100644 --- a/src/id/doujindesu/build.gradle +++ b/src/id/doujindesu/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'DoujinDesu' pkgNameSuffix = 'id.doujindesu' extClass = '.DoujinDesu' - extVersionCode = 4 + extVersionCode = 5 isNsfw = true } diff --git a/src/id/doujindesu/src/eu/kanade/tachiyomi/extension/id/doujindesu/DoujinDesu.kt b/src/id/doujindesu/src/eu/kanade/tachiyomi/extension/id/doujindesu/DoujinDesu.kt index e9ed895f6d..e2769f5a2e 100644 --- a/src/id/doujindesu/src/eu/kanade/tachiyomi/extension/id/doujindesu/DoujinDesu.kt +++ b/src/id/doujindesu/src/eu/kanade/tachiyomi/extension/id/doujindesu/DoujinDesu.kt @@ -1,7 +1,14 @@ package eu.kanade.tachiyomi.extension.id.doujindesu +import android.app.Application +import android.content.SharedPreferences +import android.widget.Toast +import androidx.preference.EditTextPreference +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.AppInfo import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.Page @@ -16,19 +23,25 @@ import okhttp3.OkHttpClient import okhttp3.Request import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat import java.util.Locale -class DoujinDesu : ParsedHttpSource() { +class DoujinDesu : ParsedHttpSource(), ConfigurableSource { // Information : DoujinDesu use EastManga WordPress Theme override val name = "Doujindesu" - override val baseUrl = "https://doujindesu.xxx" + override val baseUrl by lazy { preferences.getString(PREF_DOMAIN_KEY, PREF_DOMAIN_DEFAULT)!! } override val lang = "id" override val supportsLatest = true override val client: OkHttpClient = network.cloudflareClient // Private stuff + private val preferences: SharedPreferences by lazy { + Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) + } + private val DATE_FORMAT by lazy { SimpleDateFormat("EEEE, dd MMMM yyyy", Locale("id")) } @@ -258,7 +271,7 @@ class DoujinDesu : ParsedHttpSource() { return manga } - private fun imageFromElement(element: Element): String? { + private fun imageFromElement(element: Element): String { return when { element.hasAttr("data-src") -> element.attr("abs:data-src") element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src") @@ -422,4 +435,27 @@ class DoujinDesu : ParsedHttpSource() { Page(i, "", element.attr("src")) } } + + companion object { + private val PREF_DOMAIN_KEY = "preferred_domain_name_v${AppInfo.getVersionName()}" + private const val PREF_DOMAIN_TITLE = "Override BaseUrl" + private const val PREF_DOMAIN_DEFAULT = "https://doujindesu.tv" + private const val PREF_DOMAIN_SUMMARY = "Override default domain with a different one" + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + EditTextPreference(screen.context).apply { + key = PREF_DOMAIN_KEY + title = PREF_DOMAIN_TITLE + dialogTitle = PREF_DOMAIN_TITLE + summary = PREF_DOMAIN_SUMMARY + dialogMessage = "Default: $PREF_DOMAIN_DEFAULT" + setDefaultValue(PREF_DOMAIN_DEFAULT) + + setOnPreferenceChangeListener { _, newValue -> + Toast.makeText(screen.context, "Restart App to apply new setting.", Toast.LENGTH_LONG).show() + true + } + }.also(screen::addPreference) + } } diff --git a/src/id/komikindoid/AndroidManifest.xml b/src/id/komikindoid/AndroidManifest.xml index a46a5570e2..cd55ad9174 100644 --- a/src/id/komikindoid/AndroidManifest.xml +++ b/src/id/komikindoid/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/id/komiku/AndroidManifest.xml b/src/id/komiku/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/id/komiku/AndroidManifest.xml +++ b/src/id/komiku/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/mangaku/AndroidManifest.xml b/src/id/mangaku/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/id/mangaku/AndroidManifest.xml +++ b/src/id/mangaku/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/id/mangaku/build.gradle b/src/id/mangaku/build.gradle index 6a72c4a58a..0e67983af5 100644 --- a/src/id/mangaku/build.gradle +++ b/src/id/mangaku/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Mangaku' pkgNameSuffix = 'id.mangaku' extClass = '.Mangaku' - extVersionCode = 4 + extVersionCode = 5 } apply from: "$rootDir/common.gradle" diff --git a/src/id/mangaku/src/eu/kanade/tachiyomi/extension/id/mangaku/Mangaku.kt b/src/id/mangaku/src/eu/kanade/tachiyomi/extension/id/mangaku/Mangaku.kt index 271b70389d..12a4df130f 100644 --- a/src/id/mangaku/src/eu/kanade/tachiyomi/extension/id/mangaku/Mangaku.kt +++ b/src/id/mangaku/src/eu/kanade/tachiyomi/extension/id/mangaku/Mangaku.kt @@ -28,7 +28,7 @@ class Mangaku : ParsedHttpSource() { override val name = "Mangaku" - override val baseUrl = "https://mangaku.vip" + override val baseUrl = "https://mangaku.fun" override val lang = "id" diff --git a/src/it/animegdrclub/AndroidManifest.xml b/src/it/animegdrclub/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/it/animegdrclub/AndroidManifest.xml +++ b/src/it/animegdrclub/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/it/demoneceleste/AndroidManifest.xml b/src/it/demoneceleste/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/it/demoneceleste/AndroidManifest.xml +++ b/src/it/demoneceleste/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/it/digitalteam/AndroidManifest.xml b/src/it/digitalteam/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/it/digitalteam/AndroidManifest.xml +++ b/src/it/digitalteam/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/it/hentaifantasy/AndroidManifest.xml b/src/it/hentaifantasy/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/it/hentaifantasy/AndroidManifest.xml +++ b/src/it/hentaifantasy/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/it/novelleleggere/AndroidManifest.xml b/src/it/novelleleggere/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/it/novelleleggere/AndroidManifest.xml +++ b/src/it/novelleleggere/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/it/shingekinoshoujo/AndroidManifest.xml b/src/it/shingekinoshoujo/AndroidManifest.xml deleted file mode 100644 index 30deb7f797..0000000000 --- a/src/it/shingekinoshoujo/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> diff --git a/src/it/shingekinoshoujo/build.gradle b/src/it/shingekinoshoujo/build.gradle deleted file mode 100644 index 4add2bc458..0000000000 --- a/src/it/shingekinoshoujo/build.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' - -ext { - extName = 'Shingeki no Shoujo' - pkgNameSuffix = 'it.shingekinoshoujo' - extClass = '.ShingekiNoShoujo' - extVersionCode = 3 -} - -apply from: "$rootDir/common.gradle" diff --git a/src/it/shingekinoshoujo/res/mipmap-hdpi/ic_launcher.png b/src/it/shingekinoshoujo/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 524250f616..0000000000 Binary files a/src/it/shingekinoshoujo/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/src/it/shingekinoshoujo/res/mipmap-mdpi/ic_launcher.png b/src/it/shingekinoshoujo/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 1a6248fdd6..0000000000 Binary files a/src/it/shingekinoshoujo/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/src/it/shingekinoshoujo/res/mipmap-xhdpi/ic_launcher.png b/src/it/shingekinoshoujo/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 67abda1d2e..0000000000 Binary files a/src/it/shingekinoshoujo/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/src/it/shingekinoshoujo/res/mipmap-xxhdpi/ic_launcher.png b/src/it/shingekinoshoujo/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 00c4a52210..0000000000 Binary files a/src/it/shingekinoshoujo/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/it/shingekinoshoujo/res/mipmap-xxxhdpi/ic_launcher.png b/src/it/shingekinoshoujo/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 529c550ce5..0000000000 Binary files a/src/it/shingekinoshoujo/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/src/it/shingekinoshoujo/res/web_hi_res_512.png b/src/it/shingekinoshoujo/res/web_hi_res_512.png deleted file mode 100644 index 10bd3aab85..0000000000 Binary files a/src/it/shingekinoshoujo/res/web_hi_res_512.png and /dev/null differ diff --git a/src/it/shingekinoshoujo/src/eu/kanade/tachiyomi/extension/it/shingekinoshoujo/ShingekiNoShoujo.kt b/src/it/shingekinoshoujo/src/eu/kanade/tachiyomi/extension/it/shingekinoshoujo/ShingekiNoShoujo.kt deleted file mode 100644 index 37538af913..0000000000 --- a/src/it/shingekinoshoujo/src/eu/kanade/tachiyomi/extension/it/shingekinoshoujo/ShingekiNoShoujo.kt +++ /dev/null @@ -1,213 +0,0 @@ -package eu.kanade.tachiyomi.extension.it.shingekinoshoujo - -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import eu.kanade.tachiyomi.source.online.ParsedHttpSource -import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.Headers -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import org.jsoup.nodes.Document -import org.jsoup.nodes.Element - -class ShingekiNoShoujo : ParsedHttpSource() { - - override val name = "Shingeki no Shoujo" - override val baseUrl = "https://shingekinoshoujo.it" - override val lang = "it" - override val supportsLatest = true - override val client: OkHttpClient = network.cloudflareClient - - //region REQUESTS - override fun popularMangaRequest(page: Int): Request = GET("$baseUrl/", headers) - override fun latestUpdatesRequest(page: Int): Request = GET("$baseUrl/#recent-tab", headers) - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - if (query.isNotEmpty()) { - return GET("$baseUrl/page/$page/?s=$query", headers) - } else { - (if (filters.isEmpty()) getFilterList() else filters).forEach { filter -> - when (filter) { - is GenreSelez -> { - return GET( - "$baseUrl/tag/${getGenreList().filter { - filter.values[filter.state] == it.name - }.map { it.id }[0]}/page/$page", - ) - } - else -> {} - } - } - return GET(baseUrl, headers) - } - } - //endregion - - //region CONTENTS INFO - private fun mangasParse(response: Response, selector: String, num: Int): MangasPage { - val document = response.asJsoup() - if (document.select("#login > .custom-message").size > 0) throw Exception("Devi accedere al sito web con il tuo account!\nPremi WebView (il pulsante con il globo)\ne accedi normalmente") - - val mangas = document.select(selector).map { element -> - when (num) { - 1 -> popularMangaFromElement(element) - 2 -> latestUpdatesFromElement(element) - else -> searchMangaFromElement(element) - } - } - return MangasPage( - mangas, - !document.select(searchMangaNextPageSelector()).isEmpty(), - ) - } - override fun popularMangaParse(response: Response): MangasPage = mangasParse(response, popularMangaSelector(), 1) - override fun latestUpdatesParse(response: Response): MangasPage = mangasParse(response, latestUpdatesSelector(), 2) - override fun searchMangaParse(response: Response): MangasPage = mangasParse(response, searchMangaSelector(), 3) - - override fun popularMangaSelector() = "#content section .single-article:not([aria-hidden]) > figure" - override fun latestUpdatesSelector() = "#recent-tab > div > figure" - override fun searchMangaSelector() = "article.type-post" - - override fun popularMangaFromElement(element: Element) = SManga.create().apply { - thumbnail_url = element.selectFirst("img")!!.attr("src") - element.selectFirst("a")!!.let { - setUrlWithoutDomain(it.attr("href")) - title = it.attr("title").let { l -> - l.ifEmpty { it.text() } - } - } - } - override fun latestUpdatesFromElement(element: Element) = popularMangaFromElement(element) - override fun searchMangaFromElement(element: Element) = popularMangaFromElement(element) - - override fun mangaDetailsParse(document: Document): SManga { - val statusElementText = document.select("blockquote").last()!!.text().lowercase() - return SManga.create().apply { - thumbnail_url = document.select(".wp-post-image").attr("src") - status = when { - statusElementText.contains("in corso") -> SManga.ONGOING - statusElementText.contains("fine") -> SManga.COMPLETED - statusElementText.contains("diritti") -> SManga.LICENSED - statusElementText.contains("sospeso") -> SManga.CANCELLED - statusElementText.contains("hiatus") -> SManga.ON_HIATUS - else -> SManga.UNKNOWN - } - author = document.select(".entry-content span:has(strong:contains(Autore))").text().substringAfter("Autore:").trim() - genre = document.select(".entry-content span:has(strong:contains(Genere))").text().substringAfter("Genere:").replace(".", "").trim() - description = document.select(".entry-content p:has(strong:contains(Trama))").text().substringAfter("Trama:").trim() - } - } - //endregion - - //region NEXT SELECTOR - Not used - - override fun popularMangaNextPageSelector(): String? = null - override fun latestUpdatesNextPageSelector() = searchMangaNextPageSelector() - override fun searchMangaNextPageSelector() = ".next.page-numbers" - //endregion - - //region CHAPTER and PAGES - - override fun chapterListParse(response: Response): List<SChapter> { - val document = response.asJsoup() - if (document.select("#login > .custom-message").size > 0) throw Exception("Devi accedere al sito web con il tuo account!\nPremi WebView (il pulsante con il globo)\ne accedi normalmente") - val chapters = mutableListOf<SChapter>() - document.select(chapterListSelector()).forEachIndexed { i, it -> - chapters.add( - SChapter.create().apply { - setUrlWithoutDomain(it.attr("href")) - it.text().let { n -> - name = n - chapter_number = n.replace(Regex("OneShot|Prologo"), "0").replace("Capitolo", "").let { - if (!it.contains(Regex("[0-9]\\."))) { - it.filter { t -> t.isDigit() }.let { t -> - t.ifEmpty { "$i" } - } - } else { - it - } - }.toFloat() - } - }, - ) - } - - chapters.reverse() - return chapters - } - override fun chapterListSelector() = ".entry-content > blockquote span > strong a, .entry-content > ul > li a" - override fun chapterFromElement(element: Element) = throw Exception("Not used") - - override fun pageListParse(document: Document): List<Page> { - val pages = mutableListOf<Page>() - - document.select("article > .entry-content img").forEachIndexed { i, it -> - pages.add(Page(i, "", it.attr("src"))) - } - if (document.toString().contains("che state leggendo")) { - pages.add(Page(1, "", "https://i.imgur.com/l0eZuoO.png")) - } - - return pages - } - - override fun imageUrlParse(document: Document) = "" - override fun imageRequest(page: Page): Request { - val imgHeader = Headers.Builder().apply { - add("User-Agent", "Mozilla/5.0 (Linux; U; Android 4.1.1; en-gb; Build/KLP) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30") - add("Referer", baseUrl) - }.build() - return GET(page.imageUrl!!, imgHeader) - } - //endregion - - //region FILTERS - private class Genre(name: String, val id: String = name) : Filter.CheckBox(name) - private class GenreSelez(genres: List<Genre>) : Filter.Select<String>( - "Genere", - genres.map { - it.name - }.toTypedArray(), - 0, - ) - - override fun getFilterList() = FilterList( - Filter.Header("La ricerca testuale non accetta i filtri e viceversa"), - GenreSelez(getGenreList()), - ) - - private fun getGenreList() = listOf( - Genre("Josei", "josei"), - Genre("Sportivo", "sportivo"), - Genre("Harlequin", "harlequin"), - Genre("Ufficio", "ufficio"), - Genre("Shoujo", "shoujo"), - Genre("Fujitani Yoko", "fujitani-yoko"), - Genre("Tsukishima Haru", "tsukishima-haru"), - Genre("Scolastico", "scolastico"), - Genre("Viaggio nel Tempo", "viaggio-nel-tempo"), - Genre("Vita Scolastica", "vita-scolastica"), - Genre("Seinen", "seinen"), - Genre("Drammatico", "drammatico"), - Genre("Commedia", "commedia"), - Genre("Drama", "drama"), - Genre("Oneshot", "oneshot"), - Genre("Mistero", "mistero"), - Genre("Saori", "saori"), - Genre("Fuji Momo", "fuji-momo"), - Genre("Azione", "azione"), - Genre("Sovrannaturale", "sovrannaturale"), - Genre("Vita Quotidiana", "vita-quotidiana"), - Genre("Storico", "storico"), - Genre("Shibano Yuka", "shibano-yuka"), - Genre("Fantasy", "fantasy"), - Genre("Smut", "smut"), - Genre("Psicologico", "psicologico"), - ) - //endregion -} diff --git a/src/it/zeurelscan/AndroidManifest.xml b/src/it/zeurelscan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/it/zeurelscan/AndroidManifest.xml +++ b/src/it/zeurelscan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/comicnewtype/AndroidManifest.xml b/src/ja/comicnewtype/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/comicnewtype/AndroidManifest.xml +++ b/src/ja/comicnewtype/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/ganma/AndroidManifest.xml b/src/ja/ganma/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/ganma/AndroidManifest.xml +++ b/src/ja/ganma/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/mangacross/AndroidManifest.xml b/src/ja/mangacross/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/mangacross/AndroidManifest.xml +++ b/src/ja/mangacross/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/nicovideoseiga/AndroidManifest.xml b/src/ja/nicovideoseiga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/nicovideoseiga/AndroidManifest.xml +++ b/src/ja/nicovideoseiga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/nikkangecchan/AndroidManifest.xml b/src/ja/nikkangecchan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/nikkangecchan/AndroidManifest.xml +++ b/src/ja/nikkangecchan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/rawdevart/AndroidManifest.xml b/src/ja/rawdevart/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/rawdevart/AndroidManifest.xml +++ b/src/ja/rawdevart/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/senmanga/AndroidManifest.xml b/src/ja/senmanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ja/senmanga/AndroidManifest.xml +++ b/src/ja/senmanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ja/twi4/AndroidManifest.xml b/src/ja/twi4/AndroidManifest.xml index 6edd79bd5a..1ee0154dd6 100644 --- a/src/ja/twi4/AndroidManifest.xml +++ b/src/ja/twi4/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".ja.twi4.Twi4Activity" diff --git a/src/ko/jmana/AndroidManifest.xml b/src/ko/jmana/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ko/jmana/AndroidManifest.xml +++ b/src/ko/jmana/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ko/navercomic/AndroidManifest.xml b/src/ko/navercomic/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ko/navercomic/AndroidManifest.xml +++ b/src/ko/navercomic/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ko/newtoki/AndroidManifest.xml b/src/ko/newtoki/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ko/newtoki/AndroidManifest.xml +++ b/src/ko/newtoki/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ko/toonkor/AndroidManifest.xml b/src/ko/toonkor/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ko/toonkor/AndroidManifest.xml +++ b/src/ko/toonkor/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/argosscan/AndroidManifest.xml b/src/pt/argosscan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/argosscan/AndroidManifest.xml +++ b/src/pt/argosscan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/bakai/build.gradle b/src/pt/bakai/build.gradle deleted file mode 100644 index 5817d02723..0000000000 --- a/src/pt/bakai/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' - -ext { - extName = 'Bakai' - pkgNameSuffix = 'pt.bakai' - extClass = '.Bakai' - extVersionCode = 3 - isNsfw = true -} - -apply from: "$rootDir/common.gradle" diff --git a/src/pt/brmangas/AndroidManifest.xml b/src/pt/brmangas/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/brmangas/AndroidManifest.xml +++ b/src/pt/brmangas/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/bruttal/AndroidManifest.xml b/src/pt/bruttal/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/bruttal/AndroidManifest.xml +++ b/src/pt/bruttal/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/goldenmangas/AndroidManifest.xml b/src/pt/goldenmangas/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/goldenmangas/AndroidManifest.xml +++ b/src/pt/goldenmangas/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/goldenmangas/build.gradle b/src/pt/goldenmangas/build.gradle index f243aed951..5f01c23f2f 100644 --- a/src/pt/goldenmangas/build.gradle +++ b/src/pt/goldenmangas/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Golden Mangás' pkgNameSuffix = 'pt.goldenmangas' extClass = '.GoldenMangas' - extVersionCode = 18 + extVersionCode = 19 isNsfw = true } diff --git a/src/pt/goldenmangas/src/eu/kanade/tachiyomi/extension/pt/goldenmangas/GoldenMangas.kt b/src/pt/goldenmangas/src/eu/kanade/tachiyomi/extension/pt/goldenmangas/GoldenMangas.kt index 0f8c0695d0..c1ead57894 100644 --- a/src/pt/goldenmangas/src/eu/kanade/tachiyomi/extension/pt/goldenmangas/GoldenMangas.kt +++ b/src/pt/goldenmangas/src/eu/kanade/tachiyomi/extension/pt/goldenmangas/GoldenMangas.kt @@ -7,9 +7,10 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource import okhttp3.Headers -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.OkHttpClient import okhttp3.Request +import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import java.text.SimpleDateFormat @@ -23,7 +24,7 @@ class GoldenMangas : ParsedHttpSource() { override val name = "Golden Mangás" - override val baseUrl = "https://goldenmangas.top" + override val baseUrl = "https://goldenmanga.top" override val lang = "pt-BR" @@ -45,8 +46,8 @@ class GoldenMangas : ParsedHttpSource() { override fun popularMangaSelector(): String = "div#maisLidos div.itemmanga" override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.select("h3").text().withoutLanguage() - thumbnail_url = element.select("img").attr("abs:src") + title = element.selectFirst("h3")!!.text().withoutLanguage() + thumbnail_url = element.selectFirst("img")!!.absUrl("src") url = element.attr("href") } @@ -60,12 +61,12 @@ class GoldenMangas : ParsedHttpSource() { override fun latestUpdatesSelector() = "div.col-sm-12.atualizacao > div.row" override fun latestUpdatesFromElement(element: Element): SManga = SManga.create().apply { - val infoElement = element.select("div.col-sm-10.col-xs-8 h3").first()!! - val thumbElement = element.select("a:first-child div img").first()!! + val infoElement = element.selectFirst("div.col-sm-10.col-xs-8 h3")!! + val thumbElement = element.selectFirst("a:first-child div img")!! title = infoElement.text().withoutLanguage() - thumbnail_url = thumbElement.attr("abs:src") - .replace("w=80&h=120", "w=380&h=600") + thumbnail_url = thumbElement.absUrl("src") + .replace("w=100&h=140", "w=380&h=600") url = element.select("a:first-child").attr("href") } @@ -76,7 +77,7 @@ class GoldenMangas : ParsedHttpSource() { .set("Referer", "$baseUrl/mangas") .build() - val url = "$baseUrl/mangas".toHttpUrlOrNull()!!.newBuilder() + val url = "$baseUrl/mangas".toHttpUrl().newBuilder() .addQueryParameter("busca", query) .toString() @@ -86,17 +87,28 @@ class GoldenMangas : ParsedHttpSource() { override fun searchMangaSelector() = "div.mangas.col-lg-2 a" override fun searchMangaFromElement(element: Element): SManga = SManga.create().apply { - title = element.select("h3").text().withoutLanguage() - thumbnail_url = element.select("img").attr("abs:src") + title = element.selectFirst("h3")!!.text().withoutLanguage() + thumbnail_url = element.selectFirst("img")!!.absUrl("src") url = element.attr("href") } override fun searchMangaNextPageSelector(): String? = null + override fun mangaDetailsParse(response: Response): SManga { + val mangaResult = runCatching { super.mangaDetailsParse(response) } + val manga = mangaResult.getOrNull() + + if (manga?.title.isNullOrEmpty()) { + throw Exception(MIGRATE_WARNING) + } + + return manga!! + } + override fun mangaDetailsParse(document: Document): SManga = SManga.create().apply { - val infoElement = document.select("div.row > div.col-sm-8 > div.row").first()!! - val firstColumn = infoElement.select("div.col-sm-4.text-right > img").first()!! - val secondColumn = infoElement.select("div.col-sm-8").first()!! + val infoElement = document.selectFirst("div.row > div.col-sm-8 > div.row")!! + val firstColumn = infoElement.selectFirst("div.col-sm-4.text-right > img")!! + val secondColumn = infoElement.selectFirst("div.col-sm-8")!! title = secondColumn.select("h2:eq(0)").text().withoutLanguage() author = secondColumn.select("h5:contains(Autor)").text().withoutLabel() @@ -109,14 +121,24 @@ class GoldenMangas : ParsedHttpSource() { thumbnail_url = firstColumn.attr("abs:src") } + override fun chapterListParse(response: Response): List<SChapter> { + val chaptersResult = runCatching { super.chapterListParse(response) } + val chapterList = chaptersResult.getOrNull() + + if (chapterList.isNullOrEmpty()) { + throw Exception(MIGRATE_WARNING) + } + + return chapterList + } + override fun chapterListSelector() = "ul#capitulos li.row" override fun chapterFromElement(element: Element): SChapter = SChapter.create().apply { - val firstColumn = element.select("a > div.col-sm-5") - val secondColumn = element.select("div.col-sm-5.text-right a[href^='http']") + val firstColumn = element.selectFirst("a > div.col-sm-5")!! + val secondColumn = element.select("div.col-sm-5.text-right a:not([href^='/'])") - name = firstColumn.select("div.col-sm-5").first()!!.text() - .substringBefore("(").trim() + name = firstColumn.text().substringBefore("(").trim() scanlator = secondColumn.joinToString { it.text() } date_upload = firstColumn.select("div.col-sm-5 span[style]").text().toDate() url = element.select("a").attr("href") @@ -131,17 +153,19 @@ class GoldenMangas : ParsedHttpSource() { } override fun pageListParse(document: Document): List<Page> { - val chapterImages = document - .select("div.col-sm-12[id^='capitulos_images']:has(img[pag])") - .firstOrNull() + val chapterImages = document.selectFirst("div.col-sm-12[id^='capitulos_images']:has(img[pag])") - val isNovel = document.select(".block_text_border").firstOrNull() !== null + val isNovel = document.selectFirst(".block_text_border") !== null if (chapterImages == null && isNovel) { throw Exception(CHAPTER_IS_NOVEL_ERROR) } - return chapterImages!!.select("img[pag]") + if (chapterImages == null) { + throw Exception(MIGRATE_WARNING) + } + + return chapterImages.select("img[pag]") .mapIndexed { i, element -> Page(i, document.location(), element.attr("abs:src")) } @@ -159,7 +183,7 @@ class GoldenMangas : ParsedHttpSource() { } private fun String.toDate(): Long { - return runCatching { DATE_FORMATTER.parse(trim())?. time } + return runCatching { DATE_FORMATTER.parse(trim())?.time } .getOrNull() ?: 0L } @@ -185,8 +209,10 @@ class GoldenMangas : ParsedHttpSource() { private const val CHAPTER_IS_NOVEL_ERROR = "O capítulo é uma novel em formato de texto e não possui imagens." + private const val MIGRATE_WARNING = "Migre o item da Golden Mangás para Golden Mangás para atualizar a URL." + private val DATE_FORMATTER by lazy { - SimpleDateFormat("(dd/MM/yyyy)", Locale.ENGLISH) + SimpleDateFormat("(dd/MM/yyyy)", Locale("pt", "BR")) } } } diff --git a/src/pt/hqnow/AndroidManifest.xml b/src/pt/hqnow/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/hqnow/AndroidManifest.xml +++ b/src/pt/hqnow/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/mangavibe/AndroidManifest.xml b/src/pt/mangavibe/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/mangavibe/AndroidManifest.xml +++ b/src/pt/mangavibe/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/mizumangas/AndroidManifest.xml b/src/pt/mizumangas/AndroidManifest.xml index 389c51256f..8072ee00db 100644 --- a/src/pt/mizumangas/AndroidManifest.xml +++ b/src/pt/mizumangas/AndroidManifest.xml @@ -1,3 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> - +<manifest /> diff --git a/src/pt/muitohentai/AndroidManifest.xml b/src/pt/muitohentai/AndroidManifest.xml index 389c51256f..8072ee00db 100644 --- a/src/pt/muitohentai/AndroidManifest.xml +++ b/src/pt/muitohentai/AndroidManifest.xml @@ -1,3 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> - +<manifest /> diff --git a/src/pt/mundohentai/AndroidManifest.xml b/src/pt/mundohentai/AndroidManifest.xml index 389c51256f..8072ee00db 100644 --- a/src/pt/mundohentai/AndroidManifest.xml +++ b/src/pt/mundohentai/AndroidManifest.xml @@ -1,3 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> - +<manifest /> diff --git a/src/pt/mundowebtoon/AndroidManifest.xml b/src/pt/mundowebtoon/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/mundowebtoon/AndroidManifest.xml +++ b/src/pt/mundowebtoon/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/nixmangas/AndroidManifest.xml b/src/pt/nixmangas/AndroidManifest.xml index 389c51256f..8072ee00db 100644 --- a/src/pt/nixmangas/AndroidManifest.xml +++ b/src/pt/nixmangas/AndroidManifest.xml @@ -1,3 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> - +<manifest /> diff --git a/src/pt/opex/AndroidManifest.xml b/src/pt/opex/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/opex/AndroidManifest.xml +++ b/src/pt/opex/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/saikaiscan/AndroidManifest.xml b/src/pt/saikaiscan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/pt/saikaiscan/AndroidManifest.xml +++ b/src/pt/saikaiscan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/pt/saikaiscan/build.gradle b/src/pt/saikaiscan/build.gradle index a3fc8f9849..e6e60d779b 100644 --- a/src/pt/saikaiscan/build.gradle +++ b/src/pt/saikaiscan/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Saikai Scan' pkgNameSuffix = 'pt.saikaiscan' extClass = '.SaikaiScan' - extVersionCode = 10 + extVersionCode = 11 } apply from: "$rootDir/common.gradle" diff --git a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt index 893a9e4c8d..3e0dc7760d 100644 --- a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt +++ b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScan.kt @@ -22,7 +22,7 @@ class SaikaiScan : HttpSource() { override val name = SOURCE_NAME - override val baseUrl = "https://saikai.com.br" + override val baseUrl = "https://saikaiscans.net" override val lang = "pt-BR" @@ -51,7 +51,7 @@ class SaikaiScan : HttpSource() { .addQueryParameter("page", page.toString()) .addQueryParameter("per_page", PER_PAGE) .addQueryParameter("relationships", "language,type,format") - .toString() + .build() return GET(apiEndpointUrl, apiHeaders) } @@ -60,9 +60,8 @@ class SaikaiScan : HttpSource() { val result = response.parseAs<SaikaiScanPaginatedStoriesDto>() val mangaList = result.data!!.map(SaikaiScanStoryDto::toSManga) - val hasNextPage = result.meta!!.currentPage < result.meta.lastPage - return MangasPage(mangaList, hasNextPage) + return MangasPage(mangaList, result.hasNextPage) } override fun latestUpdatesRequest(page: Int): Request { @@ -75,7 +74,7 @@ class SaikaiScan : HttpSource() { .addQueryParameter("page", page.toString()) .addQueryParameter("per_page", PER_PAGE) .addQueryParameter("relationships", "language,type,format,latestReleases.separator") - .toString() + .build() return GET(apiEndpointUrl, apiHeaders) } @@ -99,7 +98,7 @@ class SaikaiScan : HttpSource() { filters.filterIsInstance<UrlQueryFilter>() .forEach { it.addQueryParameter(apiEndpointUrl) } - return GET(apiEndpointUrl.toString(), apiHeaders) + return GET(apiEndpointUrl.build(), apiHeaders) } override fun searchMangaParse(response: Response): MangasPage = popularMangaParse(response) @@ -118,7 +117,7 @@ class SaikaiScan : HttpSource() { .addQueryParameter("slug", storySlug) .addQueryParameter("per_page", "1") .addQueryParameter("relationships", "language,type,format,artists,status") - .toString() + .build() return GET(apiEndpointUrl, apiHeaders) } @@ -141,7 +140,7 @@ class SaikaiScan : HttpSource() { .addQueryParameter("slug", storySlug) .addQueryParameter("per_page", "1") .addQueryParameter("relationships", "releases") - .toString() + .build() return GET(apiEndpointUrl, apiHeaders) } @@ -169,7 +168,7 @@ class SaikaiScan : HttpSource() { val apiEndpointUrl = "$API_URL/api/releases/$releaseId".toHttpUrl().newBuilder() .addQueryParameter("relationships", "releaseImages") - .toString() + .build() return GET(apiEndpointUrl, apiHeaders) } @@ -177,7 +176,7 @@ class SaikaiScan : HttpSource() { override fun pageListParse(response: Response): List<Page> { val result = response.parseAs<SaikaiScanReleaseResultDto>() - return result.data!!.releaseImages.mapIndexed { i, obj -> + return result.data?.releaseImages.orEmpty().mapIndexed { i, obj -> Page(i, "", "$IMAGE_SERVER_URL/${obj.image}") } } @@ -295,7 +294,7 @@ class SaikaiScan : HttpSource() { private const val COMIC_FORMAT_ID = "2" private const val PER_PAGE = "12" - private const val API_URL = "https://api.saikai.com.br" - const val IMAGE_SERVER_URL = "https://s3-alpha.saikai.com.br" + private const val API_URL = "https://api.saikaiscans.net" + const val IMAGE_SERVER_URL = "https://s3-alpha.saikaiscans.net" } } diff --git a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt index bb9f25c40f..ab48b86e4a 100644 --- a/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt +++ b/src/pt/saikaiscan/src/eu/kanade/tachiyomi/extension/pt/saikaiscan/SaikaiScanDto.kt @@ -12,7 +12,11 @@ import java.util.Locale data class SaikaiScanResultDto<T>( val data: T? = null, val meta: SaikaiScanMetaDto? = null, -) +) { + + val hasNextPage: Boolean + get() = meta !== null && meta.currentPage < meta.lastPage +} typealias SaikaiScanPaginatedStoriesDto = SaikaiScanResultDto<List<SaikaiScanStoryDto>> typealias SaikaiScanReleaseResultDto = SaikaiScanResultDto<SaikaiScanReleaseDto> diff --git a/src/pt/taosect/AndroidManifest.xml b/src/pt/taosect/AndroidManifest.xml index 77c0c33893..b79ee94936 100644 --- a/src/pt/taosect/AndroidManifest.xml +++ b/src/pt/taosect/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/pt/yomumangas/AndroidManifest.xml b/src/pt/yomumangas/AndroidManifest.xml new file mode 100644 index 0000000000..8072ee00db --- /dev/null +++ b/src/pt/yomumangas/AndroidManifest.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest /> diff --git a/src/pt/yomumangas/build.gradle b/src/pt/yomumangas/build.gradle new file mode 100644 index 0000000000..b79a7a1632 --- /dev/null +++ b/src/pt/yomumangas/build.gradle @@ -0,0 +1,13 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlinx-serialization' + +ext { + extName = 'Yomu Mangás' + pkgNameSuffix = 'pt.yomumangas' + extClass = '.YomuMangas' + extVersionCode = 2 + isNsfw = true +} + +apply from: "$rootDir/common.gradle" diff --git a/src/pt/yomumangas/res/mipmap-hdpi/ic_launcher.png b/src/pt/yomumangas/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..47013f3484 Binary files /dev/null and b/src/pt/yomumangas/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/pt/yomumangas/res/mipmap-mdpi/ic_launcher.png b/src/pt/yomumangas/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..dcd5ab52a7 Binary files /dev/null and b/src/pt/yomumangas/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/pt/yomumangas/res/mipmap-xhdpi/ic_launcher.png b/src/pt/yomumangas/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..1d3ce2077a Binary files /dev/null and b/src/pt/yomumangas/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/pt/yomumangas/res/mipmap-xxhdpi/ic_launcher.png b/src/pt/yomumangas/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..3210653980 Binary files /dev/null and b/src/pt/yomumangas/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/pt/yomumangas/res/mipmap-xxxhdpi/ic_launcher.png b/src/pt/yomumangas/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..91ec7f3a6b Binary files /dev/null and b/src/pt/yomumangas/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/pt/yomumangas/res/web_hi_res_512.png b/src/pt/yomumangas/res/web_hi_res_512.png new file mode 100644 index 0000000000..e59253d0d3 Binary files /dev/null and b/src/pt/yomumangas/res/web_hi_res_512.png differ diff --git a/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangas.kt b/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangas.kt new file mode 100644 index 0000000000..d112f02719 --- /dev/null +++ b/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangas.kt @@ -0,0 +1,208 @@ +package eu.kanade.tachiyomi.extension.pt.yomumangas + +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.interceptor.rateLimitHost +import eu.kanade.tachiyomi.source.model.FilterList +import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import eu.kanade.tachiyomi.source.online.HttpSource +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json +import okhttp3.Headers +import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import rx.Observable +import uy.kohesive.injekt.injectLazy +import java.util.concurrent.TimeUnit + +class YomuMangas : HttpSource() { + + override val name = "Yomu Mangás" + + override val baseUrl = "https://yomumangas.com" + + override val lang = "pt-BR" + + override val supportsLatest = true + + override val client: OkHttpClient = network.cloudflareClient.newBuilder() + .rateLimitHost(baseUrl.toHttpUrl(), 1, 1, TimeUnit.SECONDS) + .rateLimitHost(API_URL.toHttpUrl(), 1, 1, TimeUnit.SECONDS) + .rateLimitHost(CDN_URL.toHttpUrl(), 1, 2, TimeUnit.SECONDS) + .build() + + private val json: Json by injectLazy() + + private val apiHeaders: Headers by lazy { apiHeadersBuilder().build() } + + override fun headersBuilder(): Headers.Builder = Headers.Builder() + .add("Origin", baseUrl) + .add("Referer", baseUrl) + + private fun apiHeadersBuilder(): Headers.Builder = headersBuilder() + .add("Accept", ACCEPT_JSON) + + override fun popularMangaRequest(page: Int): Request { + return GET("$API_URL/mangas/home", apiHeaders) + } + + override fun popularMangaParse(response: Response): MangasPage { + val result = response.parseAs<YomuMangasHomeDto>() + val seriesList = result.votes.map(YomuMangasSeriesDto::toSManga) + + return MangasPage(seriesList, hasNextPage = false) + } + + override fun latestUpdatesRequest(page: Int) = popularMangaRequest(page) + + override fun latestUpdatesParse(response: Response): MangasPage { + val result = response.parseAs<YomuMangasHomeDto>() + val seriesList = result.updates.map(YomuMangasSeriesDto::toSManga) + + return MangasPage(seriesList, hasNextPage = false) + } + + override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { + val apiUrl = "$API_URL/mangas/search".toHttpUrl().newBuilder() + .addQueryParameter("query", query) + .addQueryParameter("page", page.toString()) + + filters.filterIsInstance<UrlQueryFilter>() + .forEach { it.addQueryParameter(apiUrl) } + + return GET(apiUrl.build(), apiHeaders) + } + + override fun searchMangaParse(response: Response): MangasPage { + val result = response.parseAs<YomuMangasSearchDto>() + val seriesList = result.mangas.map(YomuMangasSeriesDto::toSManga) + + return MangasPage(seriesList, result.hasNextPage) + } + + override fun getMangaUrl(manga: SManga): String = baseUrl + manga.url + + override fun mangaDetailsRequest(manga: SManga): Request { + val id = manga.url + .substringAfter("/manga/") + .substringBefore("/") + + return GET("$API_URL/mangas/$id", apiHeaders) + } + + override fun mangaDetailsParse(response: Response): SManga { + return response.parseAs<YomuMangasDetailsDto>().manga.toSManga() + } + + override fun chapterListRequest(manga: SManga) = mangaDetailsRequest(manga) + + private fun chapterListApiRequest(mangaId: Int): Request { + return GET("$API_URL/mangas/$mangaId/chapters", apiHeaders) + } + + override fun chapterListParse(response: Response): List<SChapter> { + val series = response.parseAs<YomuMangasDetailsDto>().manga + + return client.newCall(chapterListApiRequest(series.id)).execute() + .parseAs<YomuMangasChaptersDto>().chapters + .sortedByDescending(YomuMangasChapterDto::chapter) + .map { it.toSChapter(series) } + } + + override fun getChapterUrl(chapter: SChapter): String = baseUrl + chapter.url + + override fun pageListRequest(chapter: SChapter): Request { + val urlParts = chapter.url.split("/", "#") + val seriesId = urlParts[2] + val chapterNumber = urlParts[6] + + return GET("$API_URL/mangas/$seriesId/chapters/$chapterNumber", apiHeaders) + } + + override fun pageListParse(response: Response): List<Page> { + return response.parseAs<YomuMangasChapterDetailsDto>() + .chapter.images.orEmpty() + .mapIndexed { i, image -> Page(i, "", "$CDN_URL/${image.uri}") } + } + + override fun fetchImageUrl(page: Page): Observable<String> = Observable.just(page.imageUrl!!) + + override fun imageUrlParse(response: Response): String = "" + + override fun imageRequest(page: Page): Request { + val newHeaders = headersBuilder() + .add("Accept", ACCEPT_IMAGE) + .build() + + return GET(page.imageUrl!!, newHeaders) + } + + override fun getFilterList(): FilterList = FilterList( + StatusFilter(getStatusList()), + TypeFilter(getTypesList()), + NsfwContentFilter(), + AdultContentFilter(), + GenreFilter(getGenresList()), + ) + + private fun getStatusList(): List<Status> = listOf( + Status("Todos", ""), + Status("Lançando", "RELEASING"), + Status("Finalizado", "FINISHED"), + Status("Cancelado", "CANCELLED"), + Status("Hiato", "HIATUS"), + Status("Não lançado", "NOT_YET_RELEASED"), + Status("Traduzindo", "TRANSLATING"), + Status("Desconhecido", "UNKNOWN"), + ) + + private fun getTypesList(): List<Type> = listOf( + Type("Todos", ""), + Type("Mangá", "MANGA"), + Type("Manhwa", "MANHWA"), + Type("Mangá em hiato", "MANGA_HIATUS"), + Type("Webcomic", "WEBCOMIC"), + Type("Webtoon", "WEBTOON"), + Type("Hentai", "HENTAI"), + Type("Doujinshi", "DOUJIN"), + Type("One-shot", "ONESHOT"), + ) + + private fun getGenresList(): List<Genre> = listOf( + Genre("Ação", "1"), + Genre("Aventura", "8"), + Genre("Comédia", "2"), + Genre("Drama", "3"), + Genre("Ecchi", "15"), + Genre("Esportes", "14"), + Genre("Fantasia", "6"), + Genre("Hentai", "19"), + Genre("Horror", "4"), + Genre("Mahou shoujo", "18"), + Genre("Mecha", "17"), + Genre("Mistério", "7"), + Genre("Música", "16"), + Genre("Psicológico", "9"), + Genre("Romance", "13"), + Genre("Sci-fi", "11"), + Genre("Slice of life", "10"), + Genre("Sobrenatural", "5"), + Genre("Suspense", "12"), + ) + + private inline fun <reified T> Response.parseAs(): T = use { + json.decodeFromString(it.body.string()) + } + + companion object { + private const val ACCEPT_IMAGE = "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" + private const val ACCEPT_JSON = "application/json" + + private const val API_URL = "https://api.yomumangas.com" + const val CDN_URL = "https://images.yomumangas.com" + } +} diff --git a/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangasDto.kt b/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangasDto.kt new file mode 100644 index 0000000000..1035ff92e1 --- /dev/null +++ b/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangasDto.kt @@ -0,0 +1,96 @@ +package eu.kanade.tachiyomi.extension.pt.yomumangas + +import eu.kanade.tachiyomi.source.model.SChapter +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import java.text.SimpleDateFormat +import java.util.Locale + +@Serializable +data class YomuMangasHomeDto( + val updates: List<YomuMangasSeriesDto> = emptyList(), + val votes: List<YomuMangasSeriesDto> = emptyList(), +) + +@Serializable +data class YomuMangasSearchDto( + val mangas: List<YomuMangasSeriesDto> = emptyList(), + val page: Int, + val pages: Int, +) { + + val hasNextPage: Boolean + get() = page < pages +} + +@Serializable +data class YomuMangasDetailsDto(val manga: YomuMangasSeriesDto) + +@Serializable +data class YomuMangasSeriesDto( + val id: Int, + val slug: String, + val title: String, + val cover: String, + val status: String, + val authors: List<String>? = emptyList(), + val artists: List<String>? = emptyList(), + val genres: List<YomuMangasGenreDto>? = emptyList(), + val description: String? = null, +) { + + fun toSManga(): SManga = SManga.create().apply { + title = this@YomuMangasSeriesDto.title + author = authors.orEmpty().joinToString { it.trim() } + artist = artists.orEmpty().joinToString { it.trim() } + genre = genres.orEmpty() + .sortedBy { it.name } + .joinToString { it.name.trim() } + description = this@YomuMangasSeriesDto.description?.trim() + status = when (this@YomuMangasSeriesDto.status) { + "RELEASING" -> SManga.ONGOING + "FINISHED" -> SManga.COMPLETED + "HIATUS" -> SManga.ON_HIATUS + "CANCELLED" -> SManga.CANCELLED + "TRANSLATING" -> SManga.PUBLISHING_FINISHED + else -> SManga.UNKNOWN + } + thumbnail_url = "${YomuMangas.CDN_URL}/$cover" + url = "/manga/$id/$slug" + } +} + +@Serializable +data class YomuMangasGenreDto(val name: String) + +@Serializable +data class YomuMangasChaptersDto(val chapters: List<YomuMangasChapterDto> = emptyList()) + +@Serializable +data class YomuMangasChapterDto( + val id: Int, + val chapter: Float, + @SerialName("uploaded_at") val uploadedAt: String, + val images: List<YomuMangasImageDto>? = emptyList(), +) { + + fun toSChapter(series: YomuMangasSeriesDto): SChapter = SChapter.create().apply { + name = "Capítulo ${chapter.toString().removeSuffix(".0")}" + date_upload = runCatching { DATE_FORMATTER.parse(uploadedAt)?.time } + .getOrNull() ?: 0L + url = "/manga/${series.id}/${series.slug}/chapter/$id#$chapter" + } + + companion object { + private val DATE_FORMATTER by lazy { + SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ", Locale.US) + } + } +} + +@Serializable +data class YomuMangasChapterDetailsDto(val chapter: YomuMangasChapterDto) + +@Serializable +data class YomuMangasImageDto(val uri: String) diff --git a/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangasFilters.kt b/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangasFilters.kt new file mode 100644 index 0000000000..2bd95683b5 --- /dev/null +++ b/src/pt/yomumangas/src/eu/kanade/tachiyomi/extension/pt/yomumangas/YomuMangasFilters.kt @@ -0,0 +1,73 @@ +package eu.kanade.tachiyomi.extension.pt.yomumangas + +import eu.kanade.tachiyomi.source.model.Filter +import okhttp3.HttpUrl + +interface UrlQueryFilter { + fun addQueryParameter(url: HttpUrl.Builder) +} + +class NsfwContentFilter : Filter.CheckBox("Conteúdo NSFW"), UrlQueryFilter { + override fun addQueryParameter(url: HttpUrl.Builder) { + if (state) { + url.addQueryParameter("nsfw", "true") + } + } +} + +class AdultContentFilter : Filter.CheckBox("Conteúdo adulto"), UrlQueryFilter { + override fun addQueryParameter(url: HttpUrl.Builder) { + if (state) { + url.addQueryParameter("hentai", "true") + } + } +} + +open class EnhancedSelect<T>(name: String, values: Array<T>) : Filter.Select<T>(name, values) { + val selected: T + get() = values[state] +} + +data class Status(val name: String, val value: String) { + override fun toString() = name +} + +class StatusFilter(statusList: List<Status>) : + EnhancedSelect<Status>("Status", statusList.toTypedArray()), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + if (state > 0) { + url.addQueryParameter("status", selected.value) + } + } +} + +data class Type(val name: String, val value: String) { + override fun toString() = name +} + +class TypeFilter(typesList: List<Type>) : + EnhancedSelect<Type>("Tipo", typesList.toTypedArray()), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + if (state > 0) { + url.addQueryParameter("type", selected.value) + } + } +} + +class Genre(name: String, val id: String) : Filter.CheckBox(name) { + override fun toString() = name +} + +class GenreFilter(genres: List<Genre>) : + Filter.Group<Genre>("Gêneros", genres), + UrlQueryFilter { + + override fun addQueryParameter(url: HttpUrl.Builder) { + state.filter(Genre::state) + .forEach { url.addQueryParameter("genres[]", it.id) } + } +} diff --git a/src/ru/acomics/AndroidManifest.xml b/src/ru/acomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/acomics/AndroidManifest.xml +++ b/src/ru/acomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/comx/AndroidManifest.xml b/src/ru/comx/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/comx/AndroidManifest.xml +++ b/src/ru/comx/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/desu/AndroidManifest.xml b/src/ru/desu/AndroidManifest.xml index 880af3062a..ea6f95a563 100644 --- a/src/ru/desu/AndroidManifest.xml +++ b/src/ru/desu/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/ru/mangabook/AndroidManifest.xml b/src/ru/mangabook/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/mangabook/AndroidManifest.xml +++ b/src/ru/mangabook/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/mangaclub/AndroidManifest.xml b/src/ru/mangaclub/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/mangaclub/AndroidManifest.xml +++ b/src/ru/mangaclub/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/mangahub/AndroidManifest.xml b/src/ru/mangahub/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/mangahub/AndroidManifest.xml +++ b/src/ru/mangahub/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/mangapoisk/AndroidManifest.xml b/src/ru/mangapoisk/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/mangapoisk/AndroidManifest.xml +++ b/src/ru/mangapoisk/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/mangapoisk/build.gradle b/src/ru/mangapoisk/build.gradle index 10f1a4974b..33dbad7b69 100644 --- a/src/ru/mangapoisk/build.gradle +++ b/src/ru/mangapoisk/build.gradle @@ -5,7 +5,8 @@ ext { extName = 'MangaPoisk' pkgNameSuffix = 'ru.mangapoisk' extClass = '.MangaPoisk' - extVersionCode = 5 + extVersionCode = 6 + isNsfw = true } apply from: "$rootDir/common.gradle" diff --git a/src/ru/mangapoisk/src/eu/kanade/tachiyomi/extension/ru/mangapoisk/MangaPoisk.kt b/src/ru/mangapoisk/src/eu/kanade/tachiyomi/extension/ru/mangapoisk/MangaPoisk.kt index 0ff87c873e..44d509b726 100644 --- a/src/ru/mangapoisk/src/eu/kanade/tachiyomi/extension/ru/mangapoisk/MangaPoisk.kt +++ b/src/ru/mangapoisk/src/eu/kanade/tachiyomi/extension/ru/mangapoisk/MangaPoisk.kt @@ -23,7 +23,7 @@ import java.util.Locale class MangaPoisk : ParsedHttpSource() { override val name = "MangaPoisk" - override val baseUrl = "https://mangapoisk.com" + override val baseUrl = "https://mangapoisk.org" override val lang = "ru" diff --git a/src/ru/newbie/AndroidManifest.xml b/src/ru/newbie/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/newbie/AndroidManifest.xml +++ b/src/ru/newbie/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/nudemoon/AndroidManifest.xml b/src/ru/nudemoon/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/nudemoon/AndroidManifest.xml +++ b/src/ru/nudemoon/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/remanga/AndroidManifest.xml b/src/ru/remanga/AndroidManifest.xml index c268a5b208..1cb45bc3c5 100644 --- a/src/ru/remanga/AndroidManifest.xml +++ b/src/ru/remanga/AndroidManifest.xml @@ -1,7 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" -package="eu.kanade.tachiyomi.extension"> - +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".ru.remanga.RemangaActivity" diff --git a/src/ru/remanga/build.gradle b/src/ru/remanga/build.gradle index c8af60b3a5..87be783f02 100644 --- a/src/ru/remanga/build.gradle +++ b/src/ru/remanga/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Remanga' pkgNameSuffix = 'ru.remanga' extClass = '.Remanga' - extVersionCode = 75 + extVersionCode = 78 } dependencies { diff --git a/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/Remanga.kt b/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/Remanga.kt index 3a4c72da7a..222eacefb1 100644 --- a/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/Remanga.kt +++ b/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/Remanga.kt @@ -10,13 +10,14 @@ import eu.kanade.tachiyomi.extension.ru.remanga.dto.BookDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.BranchesDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.ChunksPageDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.ExBookDto +import eu.kanade.tachiyomi.extension.ru.remanga.dto.ExLibraryDto +import eu.kanade.tachiyomi.extension.ru.remanga.dto.ExWrapperDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.LibraryDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.MangaDetDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.MyLibraryDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.PageDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.PageWrapperDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.PagesDto -import eu.kanade.tachiyomi.extension.ru.remanga.dto.SeriesExWrapperDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.SeriesWrapperDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.TagsDto import eu.kanade.tachiyomi.extension.ru.remanga.dto.UserDto @@ -212,7 +213,14 @@ class Remanga : ConfigurableSource, HttpSource() { override fun latestUpdatesParse(response: Response): MangasPage = searchMangaParse(response) override fun searchMangaParse(response: Response): MangasPage { - if (response.request.url.toString().contains("/bookmarks/")) { + if (response.request.url.toString().contains(exManga)) { + val page = json.decodeFromString<ExWrapperDto<List<ExLibraryDto>>>(response.body.string()) + val mangas = page.data.map { + it.toSManga() + } + + return MangasPage(mangas, true) + } else if (response.request.url.toString().contains("/bookmarks/")) { val page = json.decodeFromString<PageWrapperDto<MyLibraryDto>>(response.body.string()) val mangas = page.content.map { it.title.toSManga() @@ -229,6 +237,13 @@ class Remanga : ConfigurableSource, HttpSource() { return MangasPage(mangas, page.props.page < page.props.total_pages!!) } } + private fun ExLibraryDto.toSManga(): SManga = + SManga.create().apply { + // Do not change the title name to ensure work with a multilingual catalog! + title = name + url = "/api/titles/$dir/" + thumbnail_url = baseUrl + img + } private fun LibraryDto.toSManga(): SManga = SManga.create().apply { @@ -307,6 +322,11 @@ class Remanga : ConfigurableSource, HttpSource() { url.setQueryParameter("count_chapters_gte", "0") } } + is RequireEX -> { + if (filter.state == 1) { + return GET("$exManga/manga?take=20&skip=${10 * (page - 1)}&name=$query", exHeaders()) + } + } else -> {} } } @@ -370,7 +390,11 @@ class Remanga : ConfigurableSource, HttpSource() { altName = "Альтернативные названия:\n" + another_name + "\n\n" } val mediaNameLanguage = if (isEng.equals("rus")) en_name else rus_name - this.description = mediaNameLanguage + "\n" + ratingStar + " " + ratingValue + " (голосов: " + count_rating + ")\n" + altName + Jsoup.parse(o.description.replace("<p>", "").replace("</p>", "REPLACbR")).text().replace("REPLACbR", "\n") + this.description = "$mediaNameLanguage\n$ratingStar $ratingValue (голосов: $count_rating)\n$altName" + + o.description?.let { Jsoup.parseBodyFragment(it) } + ?.select("p") + ?.joinToString("\n") { it.text() } + .orEmpty() genre = (parseType(type) + ", " + parseAge(age_limit) + ", " + (genres + categories).joinToString { it.name }).split(", ").filter { it.isNotEmpty() }.joinToString { it.trim() } status = parseStatus(o.status.id) } @@ -461,13 +485,13 @@ class Remanga : ConfigurableSource, HttpSource() { else -> { val mangaID = mangaIDs[manga.url.substringAfter("/api/titles/").substringBefore("/").substringBefore("?")] val exChapters = if (preferences.getBoolean(exPAID_PREF, true)) { - json.decodeFromString<SeriesExWrapperDto<List<ExBookDto>>>(client.newCall(GET("$exManga/chapter/history/$mangaID", exHeaders())).execute().body.string()).data + json.decodeFromString<ExWrapperDto<List<ExBookDto>>>(client.newCall(GET("$exManga/chapter/history/$mangaID", exHeaders())).execute().body.string()).data } else { emptyList() } val selectedBranch = branch.maxByOrNull { selector(it) }!! val tempChaptersList = mutableListOf<SChapter>() - (1..(selectedBranch.count_chapters / 100 + 1)).map { + (1..(selectedBranch.count_chapters / 300 + 1)).map { val response = chapterListRequest(selectedBranch.id, it) chapterListParse(response, manga, exChapters) }.let { tempChaptersList.addAll(it.flatten()) } @@ -481,7 +505,7 @@ class Remanga : ConfigurableSource, HttpSource() { ).content.firstOrNull()?.chapter?.toFloatOrNull() ?: -2F ) ) { - (1..(selectedBranch2.count_chapters / 100 + 1)).map { + (1..(selectedBranch2.count_chapters / 300 + 1)).map { val response = chapterListRequest(selectedBranch2.id, it) chapterListParse(response, manga, exChapters) }.let { tempChaptersList.addAll(0, it.flatten()) } @@ -498,7 +522,7 @@ class Remanga : ConfigurableSource, HttpSource() { private fun chapterListRequest(branch: Long, page: Number): Response = client.newCall( GET( - "$baseUrl/api/titles/chapters/?branch_id=$branch&page=$page&count=100", + "$baseUrl/api/titles/chapters/?branch_id=$branch&page=$page&count=300", headers, ), ).execute().run { @@ -570,7 +594,7 @@ class Remanga : ConfigurableSource, HttpSource() { val heightEmptyChunks = 10 if (chapter.scanlator.equals("exmanga")) { try { - val exPage = json.decodeFromString<SeriesExWrapperDto<List<List<PagesDto>>>>(body) + val exPage = json.decodeFromString<ExWrapperDto<List<List<PagesDto>>>>(body) val result = mutableListOf<Page>() exPage.data.forEach { it.filter { page -> page.height > heightEmptyChunks }.forEach { page -> @@ -688,6 +712,7 @@ class Remanga : ConfigurableSource, HttpSource() { private class AgeList(ages: List<CheckFilter>) : Filter.Group<CheckFilter>("Возрастное ограничение", ages) override fun getFilterList() = FilterList( + RequireEX(), OrderBy(), GenreList(getGenreList()), CategoryList(getCategoryList()), @@ -894,6 +919,11 @@ class Remanga : ConfigurableSource, HttpSource() { "Только проекты с главами", arrayOf("Да", "Все"), ) + + private class RequireEX : Filter.Select<String>( + "Использовать поиск", + arrayOf("Remanga", "ExManga(без фильтров)"), + ) private var isEng: String? = preferences.getString(LANGUAGE_PREF, "eng") override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { val userAgentSystem = androidx.preference.CheckBoxPreference(screen.context).apply { diff --git a/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/dto/Dto.kt b/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/dto/Dto.kt index 751ad8d314..7764a33a1b 100644 --- a/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/dto/Dto.kt +++ b/src/ru/remanga/src/eu/kanade/tachiyomi/extension/ru/remanga/dto/Dto.kt @@ -49,7 +49,7 @@ data class MangaDetDto( val rus_name: String, val another_name: String, val dir: String, - val description: String, + val description: String?, val issue_year: Int?, val img: ImgDto, val type: TagsDto, @@ -97,7 +97,7 @@ data class BookDto( ) @Serializable -data class SeriesExWrapperDto<T>( +data class ExWrapperDto<T>( val data: T, ) @@ -108,6 +108,14 @@ data class ExBookDto( val chapter: String, ) +@Serializable +data class ExLibraryDto( + val id: Long, + val dir: String, + val name: String = "Без названия", + val img: String?, +) + @Serializable data class PagesDto( val id: Int, diff --git a/src/ru/unicomics/AndroidManifest.xml b/src/ru/unicomics/AndroidManifest.xml index 9745510248..ec2ffbaf03 100644 --- a/src/ru/unicomics/AndroidManifest.xml +++ b/src/ru/unicomics/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/ru/waymanga/AndroidManifest.xml b/src/ru/waymanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/waymanga/AndroidManifest.xml +++ b/src/ru/waymanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/webofcomics/AndroidManifest.xml b/src/ru/webofcomics/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/webofcomics/AndroidManifest.xml +++ b/src/ru/webofcomics/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/ru/yagamiproject/AndroidManifest.xml b/src/ru/yagamiproject/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/ru/yagamiproject/AndroidManifest.xml +++ b/src/ru/yagamiproject/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/th/mikudoujin/AndroidManifest.xml b/src/th/mikudoujin/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/th/mikudoujin/AndroidManifest.xml +++ b/src/th/mikudoujin/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/th/nekopost/AndroidManifest.xml b/src/th/nekopost/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/th/nekopost/AndroidManifest.xml +++ b/src/th/nekopost/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/th/niceoppai/AndroidManifest.xml b/src/th/niceoppai/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/th/niceoppai/AndroidManifest.xml +++ b/src/th/niceoppai/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/th/niceoppai/build.gradle b/src/th/niceoppai/build.gradle index 5b58e4467d..1c36205175 100644 --- a/src/th/niceoppai/build.gradle +++ b/src/th/niceoppai/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Niceoppai' pkgNameSuffix = 'th.niceoppai' extClass = '.Niceoppai' - extVersionCode = 27 + extVersionCode = 28 isNsfw = true } diff --git a/src/th/niceoppai/src/eu/kanade/tachiyomi/extension/th/niceoppai/Niceoppai.kt b/src/th/niceoppai/src/eu/kanade/tachiyomi/extension/th/niceoppai/Niceoppai.kt index 3e905ef945..f98692c924 100644 --- a/src/th/niceoppai/src/eu/kanade/tachiyomi/extension/th/niceoppai/Niceoppai.kt +++ b/src/th/niceoppai/src/eu/kanade/tachiyomi/extension/th/niceoppai/Niceoppai.kt @@ -43,16 +43,12 @@ class Niceoppai : ParsedHttpSource() { return GET("$baseUrl/manga_list/all/any/most-popular-monthly/$page", headers) } override fun popularMangaSelector() = "div.nde" - override fun popularMangaFromElement(element: Element): SManga { - val manga = SManga.create() - manga.title = element.select("div.det a").text() + override fun popularMangaFromElement(element: Element): SManga = SManga.create().apply { + title = element.selectFirst("div.det a")!!.text() element.select("div.cvr").let { - manga.setUrlWithoutDomain(it.select("div.img_wrp a").attr("href")) - manga.thumbnail_url = it.select("img").attr("abs:src") - manga.initialized = false + setUrlWithoutDomain(it.select("a").attr("href")) + thumbnail_url = it.select("img").attr("abs:src") } - - return manga } override fun popularMangaNextPageSelector() = "ul.pgg li a" diff --git a/src/tr/MangaDenizi/AndroidManifest.xml b/src/tr/MangaDenizi/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/tr/MangaDenizi/AndroidManifest.xml +++ b/src/tr/MangaDenizi/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/tr/mangaship/AndroidManifest.xml b/src/tr/mangaship/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/tr/mangaship/AndroidManifest.xml +++ b/src/tr/mangaship/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/tr/serimanga/AndroidManifest.xml b/src/tr/serimanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/tr/serimanga/AndroidManifest.xml +++ b/src/tr/serimanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/uk/honeymanga/AndroidManifest.xml b/src/uk/honeymanga/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/uk/honeymanga/AndroidManifest.xml +++ b/src/uk/honeymanga/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/uk/mangainua/AndroidManifest.xml b/src/uk/mangainua/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/uk/mangainua/AndroidManifest.xml +++ b/src/uk/mangainua/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/uk/mangainua/build.gradle b/src/uk/mangainua/build.gradle index ff5ee90499..5760a59064 100644 --- a/src/uk/mangainua/build.gradle +++ b/src/uk/mangainua/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'MangaInUa' pkgNameSuffix = 'uk.mangainua' extClass = '.Mangainua' - extVersionCode = 3 + extVersionCode = 4 isNsfw = true } diff --git a/src/uk/mangainua/res/mipmap-hdpi/ic_launcher.png b/src/uk/mangainua/res/mipmap-hdpi/ic_launcher.png index d546cd8979..c479bc1db1 100644 Binary files a/src/uk/mangainua/res/mipmap-hdpi/ic_launcher.png and b/src/uk/mangainua/res/mipmap-hdpi/ic_launcher.png differ diff --git a/src/uk/mangainua/res/mipmap-mdpi/ic_launcher.png b/src/uk/mangainua/res/mipmap-mdpi/ic_launcher.png index 3b63958da4..ced96f77da 100644 Binary files a/src/uk/mangainua/res/mipmap-mdpi/ic_launcher.png and b/src/uk/mangainua/res/mipmap-mdpi/ic_launcher.png differ diff --git a/src/uk/mangainua/res/mipmap-xhdpi/ic_launcher.png b/src/uk/mangainua/res/mipmap-xhdpi/ic_launcher.png index 812eba1141..f37eb78021 100644 Binary files a/src/uk/mangainua/res/mipmap-xhdpi/ic_launcher.png and b/src/uk/mangainua/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/src/uk/mangainua/res/mipmap-xxhdpi/ic_launcher.png b/src/uk/mangainua/res/mipmap-xxhdpi/ic_launcher.png index 5d90bc81db..3961213bc4 100644 Binary files a/src/uk/mangainua/res/mipmap-xxhdpi/ic_launcher.png and b/src/uk/mangainua/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/src/uk/mangainua/res/mipmap-xxxhdpi/ic_launcher.png b/src/uk/mangainua/res/mipmap-xxxhdpi/ic_launcher.png index 43900ba24d..99ef7402b5 100644 Binary files a/src/uk/mangainua/res/mipmap-xxxhdpi/ic_launcher.png and b/src/uk/mangainua/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/src/uk/mangainua/src/eu/kanade/tachiyomi/extension/uk/mangainua/Mangainua.kt b/src/uk/mangainua/src/eu/kanade/tachiyomi/extension/uk/mangainua/Mangainua.kt index 66f2d6d599..685059d11a 100644 --- a/src/uk/mangainua/src/eu/kanade/tachiyomi/extension/uk/mangainua/Mangainua.kt +++ b/src/uk/mangainua/src/eu/kanade/tachiyomi/extension/uk/mangainua/Mangainua.kt @@ -7,13 +7,17 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource +import eu.kanade.tachiyomi.util.asJsoup import okhttp3.FormBody import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response +import org.jsoup.Jsoup import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import org.jsoup.select.Elements +import org.jsoup.select.Evaluator import java.text.SimpleDateFormat import java.util.Locale @@ -27,7 +31,7 @@ class Mangainua : ParsedHttpSource() { override val client: OkHttpClient = network.cloudflareClient - override fun headersBuilder(): Headers.Builder = Headers.Builder() + override fun headersBuilder(): Headers.Builder = super.headersBuilder() .add("Referer", baseUrl) // Popular @@ -37,11 +41,11 @@ class Mangainua : ParsedHttpSource() { override fun popularMangaSelector() = "div.owl-carousel div.card--big" override fun popularMangaFromElement(element: Element): SManga { return SManga.create().apply { - element.select("h3.card__title a").first()!!.let { + element.selectFirst("h3.card__title a")!!.let { setUrlWithoutDomain(it.attr("href")) title = it.text() } - thumbnail_url = element.select("img").attr("abs:src") + thumbnail_url = element.selectFirst("img")?.absUrl("src") } } override fun popularMangaNextPageSelector() = "not used" @@ -53,11 +57,11 @@ class Mangainua : ParsedHttpSource() { override fun latestUpdatesSelector() = "main.main article.item" override fun latestUpdatesFromElement(element: Element): SManga { return SManga.create().apply { - element.select("h3.card__title a").first()!!.let { + element.selectFirst("h3.card__title a")!!.let { setUrlWithoutDomain(it.attr("href")) title = it.text() } - thumbnail_url = element.select("div.card--big img").attr("abs:data-src") + thumbnail_url = element.selectFirst("div.card--big img")?.absUrl("data-src") } } override fun latestUpdatesNextPageSelector() = "a:contains(Наступна)" @@ -76,18 +80,18 @@ class Mangainua : ParsedHttpSource() { headers = headers, ) } else { - throw UnsupportedOperationException("Запит має містити щонайменше 3 символи / The query must contain at least 3 characters") + throw Exception("Запит має містити щонайменше 3 символи / The query must contain at least 3 characters") } } override fun searchMangaSelector() = latestUpdatesSelector() override fun searchMangaFromElement(element: Element): SManga { return SManga.create().apply { - element.select("h3.card__title a").first()!!.let { + element.selectFirst("h3.card__title a")!!.let { setUrlWithoutDomain(it.attr("href")) title = it.text() } - thumbnail_url = element.select("div.card--big img").attr("abs:src") + thumbnail_url = element.selectFirst("div.card--big img")?.absUrl("src") } } override fun searchMangaNextPageSelector() = latestUpdatesNextPageSelector() @@ -95,35 +99,40 @@ class Mangainua : ParsedHttpSource() { // Manga Details override fun mangaDetailsParse(document: Document): SManga { return SManga.create().apply { - title = document.select("span.UAname").text() - description = document.select("div.item__full-description").text() - thumbnail_url = document.select("div.item__full-sidebar--poster img").first()!!.attr("abs:src") - status = when (document.select("div.item__full-sideba--header:has(div:containsOwn(Статус перекладу:))").first()?.select("span.item__full-sidebar--description")?.first()?.text()) { + title = document.selectFirst("span.UAname")!!.text() + description = document.selectFirst("div.item__full-description")!!.text() + thumbnail_url = document.selectFirst("div.item__full-sidebar--poster img")!!.absUrl("src") + status = when (document.selectFirst("div.item__full-sideba--header:has(div:containsOwn(Статус перекладу:))")?.selectFirst("span.item__full-sidebar--description")?.text()) { "Триває" -> SManga.ONGOING "Покинуто" -> SManga.CANCELLED "Закінчений" -> SManga.COMPLETED else -> SManga.UNKNOWN } - val type = when (document.select("div.item__full-sideba--header:has(div:containsOwn(Тип:))").first()?.select("span.item__full-sidebar--description")?.first()!!.text()) { + val type = when (document.selectFirst("div.item__full-sideba--header:has(div:containsOwn(Тип:))")?.selectFirst("span.item__full-sidebar--description")!!.text()) { "ВЕБМАНХВА" -> "Manhwa" "МАНХВА" -> "Manhwa" "МАНЬХВА" -> "Manhua" "ВЕБМАНЬХВА" -> "Manhua" else -> "Manga" } - genre = document.select("div.item__full-sideba--header:has(div:containsOwn(Жанри:))").first()?.select("span.item__full-sidebar--description")?.first()!!.select("a").joinToString { it.text() } + ", " + type + genre = document.selectFirst("div.item__full-sideba--header:has(div:containsOwn(Жанри:))")?.selectFirst("span.item__full-sidebar--description")!!.select("a").joinToString { it.text() } + ", " + type } } // Chapters - override fun chapterListSelector() = "div.ltcitems" - - private var previousChapterName: String? = null - private var previousChapterNumber: Float = 0.0f + override fun chapterListSelector() = throw UnsupportedOperationException() override fun chapterFromElement(element: Element): SChapter { - return SChapter.create().apply { - element.select("a").let { urlElement -> + throw UnsupportedOperationException() + } + + private fun parseChapterElements(elements: Elements): List<SChapter> { + var previousChapterName: String? = null + var previousChapterNumber: Float = 0.0f + val dateFormat = DATE_FORMATTER + return elements.map { element -> + SChapter.create().apply { + val urlElement = element.selectFirst("a")!! setUrlWithoutDomain(urlElement.attr("href")) val chapterName = urlElement.text().substringAfter("НОВЕ").trim() val chapterNumber = urlElement.text().substringAfter("Розділ").substringBefore("-").trim() @@ -137,31 +146,58 @@ class Mangainua : ParsedHttpSource() { chapter_number = chapterNumber.toFloat() previousChapterNumber = chapterNumber.toFloat() } + date_upload = dateFormat.parse(element.child(0).ownText())?.time!! } - date_upload = parseDate(element.select("div.ltcright:containsOwn(.)").text()) } } override fun chapterListParse(response: Response): List<SChapter> { - return super.chapterListParse(response).reversed() + val document = response.asJsoup() + val userHash = document.parseUserHash() + val metaElement = document.selectFirst(Evaluator.Id("linkstocomics"))!! + val body = FormBody.Builder() + .addEncoded("action", "show") + .addEncoded("news_id", metaElement.attr("data-news_id")) + .addEncoded("news_category", metaElement.attr("data-news_category")) + .addEncoded("this_link", metaElement.attr("data-this_link")) + .addEncoded("user_hash", userHash) + .build() + val request = POST("$baseUrl/engine/ajax/controller.php?mod=load_chapters", headers, body) + val chaptersHtml = client.newCall(request).execute().body.string() + val chaptersDocument = Jsoup.parseBodyFragment(chaptersHtml) + return parseChapterElements(chaptersDocument.body().children()).asReversed() } // Pages override fun pageListParse(document: Document): List<Page> { - return document.select("ul.loadcomicsimages img").mapIndexed { i, element -> - Page(i, "", element.attr("abs:data-src")) + val userHash = document.parseUserHash() + val newsId = document.selectFirst(Evaluator.Id("comics"))!!.attr("data-news_id") + val url = "$baseUrl/engine/ajax/controller.php?mod=load_chapters_image&news_id=$newsId&action=show&user_hash=$userHash" + val pagesHtml = client.newCall(GET(url, headers)).execute().body.string() + val pagesDocument = Jsoup.parseBodyFragment(pagesHtml) + return pagesDocument.getElementsByTag("img").mapIndexed { index, img -> + Page(index, imageUrl = img.attr("data-src")) } } - override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not used") - - private fun parseDate(dateStr: String): Long { - return runCatching { DATE_FORMATTER.parse(dateStr)?.time } - .getOrNull() ?: 0L - } + override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException() companion object { private val DATE_FORMATTER by lazy { SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH) } + + private fun Document.parseUserHash(): String { + val start = "site_login_hash = '" + for (element in body().children()) { + if (element.tagName() != "script") continue + val data = element.data() + val leftIndex = data.indexOf(start) + if (leftIndex == -1) continue + val startIndex = leftIndex + start.length + val endIndex = data.indexOf('\'', startIndex) + return data.substring(startIndex, endIndex) + } + throw Exception("Couldn't find user hash") + } } } diff --git a/src/vi/blogtruyen/AndroidManifest.xml b/src/vi/blogtruyen/AndroidManifest.xml index f1474fbb26..d17382d140 100644 --- a/src/vi/blogtruyen/AndroidManifest.xml +++ b/src/vi/blogtruyen/AndroidManifest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".vi.blogtruyen.BlogTruyenUrlActivity" android:excludeFromRecents="true" diff --git a/src/vi/hentaivn/AndroidManifest.xml b/src/vi/hentaivn/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/vi/hentaivn/AndroidManifest.xml +++ b/src/vi/hentaivn/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/vi/hentaivn/build.gradle b/src/vi/hentaivn/build.gradle index 79437661fd..61c702506c 100644 --- a/src/vi/hentaivn/build.gradle +++ b/src/vi/hentaivn/build.gradle @@ -5,8 +5,12 @@ ext { extName = 'HentaiVN' pkgNameSuffix = 'vi.hentaivn' extClass = '.HentaiVN' - extVersionCode = 25 + extVersionCode = 30 isNsfw = true } +dependencies { + implementation(project(":lib-randomua")) +} + apply from: "$rootDir/common.gradle" diff --git a/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/CookieInterceptor.kt b/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/CookieInterceptor.kt new file mode 100644 index 0000000000..ce312b3211 --- /dev/null +++ b/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/CookieInterceptor.kt @@ -0,0 +1,46 @@ +package eu.kanade.tachiyomi.extension.vi.hentaivn + +import android.util.Log +import android.webkit.CookieManager +import okhttp3.Interceptor +import okhttp3.Response + +class CookieInterceptor( + private val domain: String, + private val key: String, + private val value: String, +) : Interceptor { + + init { + val url = "https://$domain/" + val cookie = "$key=$value; Domain=$domain; Path=/" + setCookie(url, cookie) + } + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + if (!request.url.host.endsWith(domain)) return chain.proceed(request) + + val cookie = "$key=$value" + val cookieList = request.header("Cookie")?.split("; ") ?: emptyList() + if (cookie in cookieList) return chain.proceed(request) + + setCookie("https://$domain/", "$cookie; Domain=$domain; Path=/") + val prefix = "$key=" + val newCookie = buildList(cookieList.size + 1) { + cookieList.filterNotTo(this) { it.startsWith(prefix) } + add(cookie) + }.joinToString("; ") + val newRequest = request.newBuilder().header("Cookie", newCookie).build() + return chain.proceed(newRequest) + } + + private fun setCookie(url: String, value: String) { + try { + CookieManager.getInstance().setCookie(url, value) + } catch (e: Exception) { + // Probably running on Tachidesk + Log.e("HentaiVN", "failed to set cookie", e) + } + } +} diff --git a/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/HentaiVN.kt b/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/HentaiVN.kt index d6835debb0..3c57e89004 100644 --- a/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/HentaiVN.kt +++ b/src/vi/hentaivn/src/eu/kanade/tachiyomi/extension/vi/hentaivn/HentaiVN.kt @@ -1,8 +1,20 @@ package eu.kanade.tachiyomi.extension.vi.hentaivn +import android.app.Application +import android.content.SharedPreferences +import android.widget.Toast +import androidx.preference.EditTextPreference +import androidx.preference.ListPreference +import androidx.preference.PreferenceScreen +import androidx.preference.SwitchPreferenceCompat +import eu.kanade.tachiyomi.extension.BuildConfig +import eu.kanade.tachiyomi.lib.randomua.getPrefCustomUA +import eu.kanade.tachiyomi.lib.randomua.getPrefUAType +import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.interceptor.rateLimit +import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage @@ -11,174 +23,91 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup -import okhttp3.CookieJar import okhttp3.Headers import okhttp3.HttpUrl.Companion.toHttpUrl -import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable -import java.text.ParseException +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import java.text.SimpleDateFormat import java.util.Locale -class HentaiVN : ParsedHttpSource() { +class HentaiVN : ParsedHttpSource(), ConfigurableSource { + + private val preferences: SharedPreferences by lazy { + Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) + } - override val baseUrl = "https://hentaivn.site" - override val lang = "vi" override val name = "HentaiVN" - override val supportsLatest = true + private val defaultBaseUrl = "https://hentaivn.tv" + override val baseUrl = preferences.getString(PREF_KEY_BASE_URL, defaultBaseUrl)!! + + private val domain = baseUrl.toHttpUrl().host private val searchUrl = "$baseUrl/forum/search-plus.php" private val searchByAuthorUrl = "$baseUrl/tim-kiem-tac-gia.html" private val searchAllURL = "$baseUrl/tim-kiem-truyen.html" - private val searchClient = network.cloudflareClient - - override val client: OkHttpClient = network.cloudflareClient.newBuilder() - .cookieJar(CookieJar.NO_COOKIES) - .addInterceptor { chain -> - val originalRequest = chain.request() - when { - originalRequest.url.toString().startsWith(searchUrl) -> { - searchClient.newCall(originalRequest).execute() - } - else -> chain.proceed(originalRequest) - } + + override val lang = "vi" + + override val supportsLatest = true + + override val client: OkHttpClient by lazy { + val baseClient = if (preferences.getBoolean(PREF_KEY_ENABLE_CLOUDFLARE_BYPASS, true)) { + network.cloudflareClient + } else { + network.client } - .rateLimit(3) - .build() - override fun headersBuilder(): Headers.Builder = super.headersBuilder() - .add("Referer", baseUrl) - .add("Cookie", "view1=1; view4=1") // bypass "captcha" and get popular manga + baseClient.newBuilder() + .addNetworkInterceptor(CookieInterceptor(domain, "view1", "1")) + .addNetworkInterceptor(CookieInterceptor(domain, "view4", "1")) + .setRandomUserAgent( + preferences.getPrefUAType(), + preferences.getPrefCustomUA(), + ) + .rateLimit(1) + .build() + } - private val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) + override fun headersBuilder(): Headers.Builder = super.headersBuilder() + .add("Referer", "$baseUrl/") // latestUpdates override fun latestUpdatesRequest(page: Int): Request { return GET("$baseUrl/chap-moi.html?page=$page", headers) } - override fun latestUpdatesSelector() = ".main > .block-left > .block-item > ul > li.item" - override fun latestUpdatesNextPageSelector() = "ul.pagination > li:contains(Next)" + override fun latestUpdatesSelector() = ".block-item ul li.item" + override fun latestUpdatesFromElement(element: Element): SManga { val manga = SManga.create() - element.select(".box-description a").first()!!.let { + element.select(".box-description a, .box-description-2 a").first()!!.let { manga.setUrlWithoutDomain(it.attr("href")) manga.title = it.text().trim() } - manga.thumbnail_url = element.select(".box-cover a img").attr("data-src") + manga.thumbnail_url = imageFromElement(element.selectFirst(".box-cover a img, .box-cover-2 a img")) return manga } + override fun latestUpdatesNextPageSelector() = ".pagination *:contains(Next)" + // Popular override fun popularMangaRequest(page: Int): Request { return GET("$baseUrl/danh-sach.html?page=$page", headers) } - override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element) - override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector() override fun popularMangaSelector() = latestUpdatesSelector() - // Chapter - override fun chapterListSelector() = "table.listing > tbody > tr" - override fun chapterFromElement(element: Element): SChapter { - if (element.select("a").isEmpty()) throw Exception(element.select("h2").html()) - val chapter = SChapter.create() - element.select("a").first()!!.let { - chapter.name = it.select("h2").text() - chapter.setUrlWithoutDomain(it.attr("href")) - } - chapter.date_upload = parseDate(element.select("td:nth-child(2)").text().trim()) - return chapter - } - - override fun chapterListRequest(manga: SManga): Request { - val mangaId = manga.url.substringAfterLast("/").substringBefore('-') - return GET("$baseUrl/list-showchapter.php?idchapshow=$mangaId", headers) - } - - private fun parseDate(dateString: String): Long { - return try { - dateFormat.parse(dateString)?.time ?: 0L - } catch (e: ParseException) { - return 0L - } - } - - override fun imageUrlParse(document: Document) = "" - - // Detail - override fun mangaDetailsParse(document: Document): SManga { - val infoElement = document.select(".main > .page-left > .left-info > .page-info") - val manga = SManga.create() - manga.title = document.selectFirst(".breadcrumb2 li:last-child span")!!.text() - manga.author = infoElement.select("p:contains(Tác giả:) a").text() - manga.description = infoElement.select(":root > p:contains(Nội dung:) + p").text() - manga.genre = infoElement.select("p:contains(Thể loại:) a").joinToString { it.text() } - manga.thumbnail_url = - document.select(".main > .page-right > .right-info > .page-ava > img").attr("src") - manga.status = - parseStatus(infoElement.select("p:contains(Tình Trạng:) a").firstOrNull()?.text()) - return manga - } - - private fun parseStatus(status: String?) = when { - status == null -> SManga.UNKNOWN - status.contains("Đang tiến hành") -> SManga.ONGOING - status.contains("Đã hoàn thành") -> SManga.COMPLETED - else -> SManga.UNKNOWN - } + override fun popularMangaFromElement(element: Element) = latestUpdatesFromElement(element) - // Pages - override fun pageListParse(document: Document): List<Page> { - return document.select("#image > img").mapIndexed { i, e -> - Page(i, imageUrl = e.attr("abs:src")) - } - } + override fun popularMangaNextPageSelector() = latestUpdatesNextPageSelector() // Search - override fun searchMangaParse(response: Response): MangasPage { - val document = response.asJsoup() - if (document.select("p").toString() - .contains("Bạn chỉ có thể sử dụng chức năng này khi đã đăng ký thành viên") - ) { - throw Exception("Đăng nhập qua WebView để kích hoạt tìm kiếm") - } - - val mangas = document.select(searchMangaSelector()).map { element -> - searchMangaFromElement(element) - } - - val hasNextPage = searchMangaNextPageSelector().let { selector -> - document.select(selector).first() - } != null - - return MangasPage(mangas, hasNextPage) - } - - override fun searchMangaFromElement(element: Element): SManga { - val manga = SManga.create() - element.select(".search-des > a, .box-description a").first()!!.let { - manga.setUrlWithoutDomain(it.attr("href")) - manga.title = it.text().trim() - } - manga.thumbnail_url = element.select("div.search-img img, .box-cover a img").attr("abs:src") - return manga - } - - override fun searchMangaNextPageSelector() = "ul.pagination > li:contains(Cuối)" - - private fun searchMangaByIdRequest(id: String) = GET("$searchAllURL?key=$id", headers) - private fun searchMangaByIdParse(response: Response, ids: String): MangasPage { - val details = mangaDetailsParse(response) - details.url = "/$ids-doc-truyen-id.html" - return MangasPage(listOf(details), false) - } - override fun fetchSearchManga( page: Int, query: String, @@ -229,34 +158,242 @@ class HentaiVN : ParsedHttpSource() { } } - companion object { - const val PREFIX_ID_SEARCH = "id:" - } - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val url = "$searchUrl?name=$query&page=$page&dou=&char=&group=0&search=".toHttpUrlOrNull()!! - .newBuilder() - (if (filters.isEmpty()) getFilterList() else filters).forEach { filter -> - when (filter) { - is TextField -> url.addQueryParameter(filter.key, filter.state) - is GenreList -> - filter.state - .filter { it.state } - .map { it.id } - .forEach { url.addQueryParameter("tag[]", it) } - is GroupList -> { - val group = getGroupList()[filter.state] - url.addQueryParameter("group", group.id) + val url = searchUrl.toHttpUrl().newBuilder().apply { + addQueryParameter("name", query) + addQueryParameter("dou", "") + addQueryParameter("char", "") + addQueryParameter("search", "") + + if (page > 1) { + addQueryParameter("page", page.toString()) + } + + (if (filters.isEmpty()) getFilterList() else filters).forEach { filter -> + when (filter) { + is TextField -> setQueryParameter(filter.key, filter.state) + is GenreList -> + filter.state + .filter { it.state } + .map { it.id } + .forEach { addQueryParameter("tag[]", it) } + is GroupList -> { + val group = getGroupList()[filter.state] + addQueryParameter("group", group.id) + } + else -> {} } - else -> {} } + }.build() + + return GET(url, headers) + } + + override fun searchMangaParse(response: Response): MangasPage { + val document = response.asJsoup() + if (document.select("p").toString() + .contains("Bạn chỉ có thể sử dụng chức năng này khi đã đăng ký thành viên") + ) { + throw Exception("Đăng nhập qua WebView để kích hoạt tìm kiếm") } - return GET(url.toString(), headers) + val mangas = document.select(searchMangaSelector()).map { element -> + searchMangaFromElement(element) + } + + val hasNextPage = searchMangaNextPageSelector().let { selector -> + document.select(selector).first() + } != null + + return MangasPage(mangas, hasNextPage) } override fun searchMangaSelector() = - ".search-ul .search-li, .main > .block-left > .block-item > ul > li.item" + ".search-ul .search-li, ${latestUpdatesSelector()}" + + override fun searchMangaFromElement(element: Element): SManga { + val manga = SManga.create() + element.select(".search-des a, .box-description a, .box-description-2 a").first()!!.let { + manga.setUrlWithoutDomain(it.attr("href")) + manga.title = it.text().trim() + } + manga.thumbnail_url = imageFromElement(element.selectFirst("div.search-img img, .box-cover a img, .box-cover-2 a img")) + return manga + } + + override fun searchMangaNextPageSelector() = ".pagination *:contains(Cuối), .pagination *:contains(Next)" + + private fun searchMangaByIdRequest(id: String) = GET("$searchAllURL?key=$id", headers) + private fun searchMangaByIdParse(response: Response, ids: String): MangasPage { + val details = mangaDetailsParse(response) + details.url = "/$ids-doc-truyen-id.html" + return MangasPage(listOf(details), false) + } + + // Detail + private val genreUrlRegex = Regex("""\"(list-info-theloai-mobile\.php?.+)\"""") + + override fun mangaDetailsParse(document: Document): SManga { + if (document.toString().contains("document.cookie = \"mobile=1")) { // Desktop version + val infoElement = document.select(".main > .page-left > .left-info > .page-info") + return SManga.create().apply { + title = document.selectFirst(".breadcrumb2 li:last-child span")!!.text() + author = infoElement.select("p:contains(Tác giả:) a").text() + description = infoElement.select(":root > p:contains(Nội dung:) + p").text() + genre = infoElement.select("p:contains(Thể loại:) a").joinToString { it.text() } + thumbnail_url = + imageFromElement(document.selectFirst(".main > .page-right > .right-info > .page-ava > img")) + status = + parseStatus(infoElement.select("p:contains(Tình Trạng:) a").firstOrNull()?.text()) + } + } else { // Mobile version + val id = document.location().substringAfterLast("/").substringBefore("-") + val documentText = document.toString() + + return SManga.create().apply { + val thumbnailElem = document.selectFirst(".content-images-1 img.cover-1") + thumbnail_url = imageFromElement(thumbnailElem) + + title = thumbnailElem?.attr("alt")?.substringBeforeLast(" Cover")?.trim() ?: client + .newCall(GET("$baseUrl/list-info-ten-mobile.php?id_anime=$id")) + .execute() + .asJsoup() + .select("h3") + .text() + + val genreUrl = genreUrlRegex.find(documentText)?.groupValues?.get(1) + genre = client + .newCall(GET("$baseUrl/$genreUrl")) + .execute() + .asJsoup() + .select("a.tag") + .joinToString { it.text() } + + val infoElement = client + .newCall(GET("$baseUrl/list-info-all-mobile.php?id_anime=$id")) + .execute() + .asJsoup() + author = infoElement.select("p:contains(Tác giả:) a").text() + status = parseStatus(infoElement.select("p:contains(Tình Trạng:) a").firstOrNull()?.text()) + description = infoElement.select("p:contains(Nội dung:) + p").text() + } + } + } + + // Chapter + override fun chapterListRequest(manga: SManga): Request { + val mangaId = manga.url.substringAfterLast("/").substringBefore('-') + return GET("$baseUrl/list-showchapter.php?idchapshow=$mangaId", headers) + } + + override fun chapterListSelector() = "table.listing > tbody > tr" + + override fun chapterFromElement(element: Element): SChapter { + if (element.select("a").isEmpty()) throw Exception(element.select("h2").html()) + val chapter = SChapter.create() + element.select("a").first()!!.let { + chapter.name = it.select("h2").text() + chapter.setUrlWithoutDomain(it.attr("href")) + } + chapter.date_upload = parseDate(element.select("td:nth-child(2)").text().trim()) + return chapter + } + + // Pages + override fun pageListParse(document: Document): List<Page> { + return document.select("#image > img").mapIndexed { i, e -> + Page(i, imageUrl = imageFromElement(e)) + } + } + + override fun imageUrlParse(document: Document) = "" + + private val dateFormat = SimpleDateFormat("dd/MM/yyyy", Locale.ENGLISH) + + private fun parseDate(dateString: String): Long { + return kotlin.runCatching { + dateFormat.parse(dateString)?.time + }.getOrNull() ?: 0L + } + + private fun parseStatus(status: String?) = when { + status == null -> SManga.UNKNOWN + status.contains("Đang tiến hành") -> SManga.ONGOING + status.contains("Đã hoàn thành") -> SManga.COMPLETED + status.contains("Tạm ngưng") -> SManga.ON_HIATUS + else -> SManga.UNKNOWN + } + + private fun imageFromElement(element: Element?): String? { + if (element == null) return null + + return when { + element.hasAttr("data-src") -> element.attr("abs:data-src") + element.hasAttr("data-lazy-src") -> element.attr("abs:data-lazy-src") + element.hasAttr("data-cfsrc") -> element.attr("abs:data-cfsrc") + element.hasAttr("srcset") -> element.attr("abs:srcset").substringBefore(" ") + else -> element.attr("abs:src") + } + } + + override fun setupPreferenceScreen(screen: PreferenceScreen) { + SwitchPreferenceCompat(screen.context).apply { + key = PREF_KEY_ENABLE_CLOUDFLARE_BYPASS + title = TITLE_ENABLE_CLOUDFLARE_BYPASS + summary = SUMMARY_ENABLE_CLOUDFLARE_BYPASS + + setDefaultValue(true) + + setOnPreferenceChangeListener { _, _ -> + Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show() + true + } + }.also(screen::addPreference) + + EditTextPreference(screen.context).apply { + key = PREF_KEY_BASE_URL + title = TITLE_BASE_URL + summary = SUMMARY_BASE_URL + + setDefaultValue(defaultBaseUrl) + dialogTitle = TITLE_BASE_URL + + setOnPreferenceChangeListener { _, _ -> + Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show() + true + } + }.also(screen::addPreference) + + ListPreference(screen.context).apply { + key = PREF_KEY_RANDOM_UA + title = TITLE_RANDOM_UA + entries = ENTRIES_RANDOM_UA + entryValues = VALUES_RANDOM_UA + summary = "%s" + setDefaultValue("off") + + setOnPreferenceChangeListener { _, _ -> + Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show() + true + } + }.also(screen::addPreference) + + EditTextPreference(screen.context).apply { + key = PREF_KEY_CUSTOM_UA + title = TITLE_CUSTOM_UA + summary = SUMMARY_CUSTOM_UA + setOnPreferenceChangeListener { _, newValue -> + try { + Headers.Builder().add("User-Agent", newValue as String).build() + Toast.makeText(screen.context, RESTART_TACHIYOMI, Toast.LENGTH_LONG).show() + true + } catch (e: IllegalArgumentException) { + Toast.makeText(screen.context, "Chuỗi đại diện người dùng không hợp lệ: ${e.message}", Toast.LENGTH_LONG).show() + false + } + } + }.also(screen::addPreference) + } private class Alls : Filter.Text("Tìm tất cả") private class Author : Filter.Text("Tác giả") @@ -279,12 +416,12 @@ class HentaiVN : ParsedHttpSource() { Author(), TextField("Doujinshi", "dou"), TextField("Nhân vật", "char"), - GenreList(getGenreList()), GroupList(getGroupList()), + GenreList(getGenreList()), ) - // jQuery.makeArray($('#container > div > div > div.box-box.textbox > form > ul:nth-child(7) > li').map((i, e) => `Genre("${e.textContent}", "${e.children[0].value}")`)).join(',\n') - // https://hentaivn.net/forum/search-plus.php + // console.log(jQuery.makeArray($('ul.ul-search > li').map((i, e) => `Genre("${e.textContent}", "${e.children[0].value}")`)).join(',\n')) + // https://hentaivn.autos/forum/search-plus.php private fun getGenreList() = listOf( Genre("3D Hentai", "3"), Genre("Action", "5"), @@ -459,7 +596,7 @@ class HentaiVN : ParsedHttpSource() { ) // jQuery.makeArray($('#container > div > div > div.box-box.textbox > form > ul:nth-child(8) > li').map((i, e) => `TransGroup("${e.textContent}", "${e.children[0].value}")`)).join(',\n') - // https://hentaivn.net/forum/search-plus.php + // https://hentaivn.autos/forum/search-plus.php private fun getGroupList() = arrayOf( TransGroup("Tất cả", "0"), TransGroup("Đang cập nhật", "1"), @@ -513,4 +650,27 @@ class HentaiVN : ParsedHttpSource() { TransGroup("Depressed Lolicons Squad - DLS", "52"), TransGroup("Heaven Of The Fuck", "53"), ) + + companion object { + const val PREFIX_ID_SEARCH = "id:" + + const val RESTART_TACHIYOMI = "Khởi động lại Tachiyomi để áp dụng thay đổi." + + const val PREF_KEY_ENABLE_CLOUDFLARE_BYPASS = "enable_cloudflare" + const val TITLE_ENABLE_CLOUDFLARE_BYPASS = "Kích hoạt bỏ qua Cloudflare" + const val SUMMARY_ENABLE_CLOUDFLARE_BYPASS = "Nếu bật khi không cần thiết, có thể gây lỗi \"Bỏ qua Cloudflare thất bại\" giả." + + const val PREF_KEY_BASE_URL = "override_base_url_${BuildConfig.VERSION_CODE}" + const val TITLE_BASE_URL = "Thay đổi tên miền" + const val SUMMARY_BASE_URL = "Thay đổi này là tạm thời và sẽ bị xoá khi cập nhật tiện ích mở rộng." + + const val PREF_KEY_RANDOM_UA = "pref_key_random_ua_" + const val TITLE_RANDOM_UA = "Chuỗi đại diện người dùng ngẫu nhiên" + val ENTRIES_RANDOM_UA = arrayOf("Tắt", "Máy tính", "Di động") + val VALUES_RANDOM_UA = arrayOf("off", "desktop", "mobile") + + const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua_" + const val TITLE_CUSTOM_UA = "Chuỗi đại diện người dùng tuỳ chỉnh" + const val SUMMARY_CUSTOM_UA = "Để trống để dùng chuỗi đại diện người dùng mặc định của ứng dụng. Cài đặt này bị vô hiệu nếu chuỗi đại diện người dùng ngẫu nhiên được bật." + } } diff --git a/src/vi/lxhentai/AndroidManifest.xml b/src/vi/lxhentai/AndroidManifest.xml index 14f6600880..c64269cfc1 100644 --- a/src/vi/lxhentai/AndroidManifest.xml +++ b/src/vi/lxhentai/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -21,4 +20,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/vi/mangaxy/AndroidManifest.xml b/src/vi/mangaxy/AndroidManifest.xml index b4571bfa87..8072ee00db 100644 --- a/src/vi/mangaxy/AndroidManifest.xml +++ b/src/vi/mangaxy/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/vi/medoctruyentranh/AndroidManifest.xml b/src/vi/medoctruyentranh/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/vi/medoctruyentranh/AndroidManifest.xml +++ b/src/vi/medoctruyentranh/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/vi/qmanga/AndroidManifest.xml b/src/vi/qmanga/AndroidManifest.xml index f059b93aff..8d2245f4ae 100644 --- a/src/vi/qmanga/AndroidManifest.xml +++ b/src/vi/qmanga/AndroidManifest.xml @@ -1,8 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> - <activity + <activity android:name="eu.kanade.tachiyomi.extension.vi.qmanga.QMangaUrlActivity" android:excludeFromRecents="true" android:exported="true" diff --git a/src/vi/truyengihot/AndroidManifest.xml b/src/vi/truyengihot/AndroidManifest.xml index 7436ac50e9..5d1d4d1c90 100644 --- a/src/vi/truyengihot/AndroidManifest.xml +++ b/src/vi/truyengihot/AndroidManifest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".vi.truyengihot.TruyenGiHotUrlActivity" android:excludeFromRecents="true" diff --git a/src/vi/truyenqq/AndroidManifest.xml b/src/vi/truyenqq/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/vi/truyenqq/AndroidManifest.xml +++ b/src/vi/truyenqq/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/vi/truyentranh8/AndroidManifest.xml b/src/vi/truyentranh8/AndroidManifest.xml index aeb21ac92b..5783cbd15c 100644 --- a/src/vi/truyentranh8/AndroidManifest.xml +++ b/src/vi/truyentranh8/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -21,4 +20,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/vi/yurineko/AndroidManifest.xml b/src/vi/yurineko/AndroidManifest.xml index 2c0409d963..37590f6ca4 100644 --- a/src/vi/yurineko/AndroidManifest.xml +++ b/src/vi/yurineko/AndroidManifest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".vi.yurineko.YuriNekoUrlActivity" diff --git a/src/zh/baimangu/AndroidManifest.xml b/src/zh/baimangu/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/baimangu/AndroidManifest.xml +++ b/src/zh/baimangu/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/baozimanhua/AndroidManifest.xml b/src/zh/baozimanhua/AndroidManifest.xml index d4f0e92296..e838905602 100644 --- a/src/zh/baozimanhua/AndroidManifest.xml +++ b/src/zh/baozimanhua/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".zh.baozimanhua.BaoziUrlActivity" @@ -29,4 +28,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/zh/baozimanhua/CHANGELOG.md b/src/zh/baozimanhua/CHANGELOG.md index 7d0a406cac..75714349fc 100644 --- a/src/zh/baozimanhua/CHANGELOG.md +++ b/src/zh/baozimanhua/CHANGELOG.md @@ -1,3 +1,18 @@ +## 1.3.22 (2023-08-09) + +- 设置里新增一些镜像网址 +- 解析章节时确保镜像网址生效 + +## 1.3.21 (2023-08-08) + +- 设置里新增一些镜像网址 +- 修复部分情况下无法解析章节的问题 +- 修复所有图片无法加载的问题 + +## 1.3.20 (2023-07-29) + +- 修复需要验证时图片无法加载的问题 + ## 1.3.19 (2023-04-03) - 设置章节日期时不再错误地检查应用版本 diff --git a/src/zh/baozimanhua/build.gradle b/src/zh/baozimanhua/build.gradle index ebf6dbcb74..313485175c 100644 --- a/src/zh/baozimanhua/build.gradle +++ b/src/zh/baozimanhua/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Baozi Manhua' pkgNameSuffix = 'zh.baozimanhua' extClass = '.Baozi' - extVersionCode = 19 + extVersionCode = 22 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/Baozi.kt b/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/Baozi.kt index ac27cb5d8e..3c3ca6a766 100644 --- a/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/Baozi.kt +++ b/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/Baozi.kt @@ -37,7 +37,15 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { private val preferences: SharedPreferences = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) - override val baseUrl = "https://${preferences.getString(MIRROR_PREF, MIRRORS[0])}" + private val domain: String = run { + val mirrors = MIRRORS + val domain = preferences.getString(MIRROR_PREF, null) ?: return@run mirrors[0] + if (domain in mirrors) return@run domain + preferences.edit().remove(MIRROR_PREF).apply() + mirrors[0] + } + + override val baseUrl = "https://$domain" override val lang = "zh" @@ -51,10 +59,11 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { .rateLimit(2) .addInterceptor(bannerInterceptor) .addNetworkInterceptor(MissingImageInterceptor) + .addNetworkInterceptor(RedirectDomainInterceptor(domain)) .build() override fun headersBuilder() = super.headersBuilder() - .add("Referer", "$baseUrl/") + .add("Referer", "https://$domain/") override fun chapterListSelector() = throw UnsupportedOperationException("Not used.") @@ -135,26 +144,31 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { } override fun fetchPageList(chapter: SChapter): Observable<List<Page>> = Observable.fromCallable { - val pageNumberSelector = Evaluator.Class("comic-text__amp") - val pageList = ArrayList<Page>(0) - var url = baseUrl + chapter.url - var i = 0 + val pathToUrl = LinkedHashMap<String, String>() + var request = GET(baseUrl + chapter.url, headers).newBuilder() + .tag(RedirectDomainInterceptor.Tag::class, RedirectDomainInterceptor.Tag()).build() while (true) { - val document = client.newCall(GET(url, headers)).execute().asJsoup() - document.select(".comic-contain amp-img").dropWhile { element -> - element.selectFirst(pageNumberSelector)!!.text().substringBefore('/').toInt() <= i - }.mapTo(pageList) { element -> - Page(i++, imageUrl = element.attr("src")) + val document = client.newCall(request).execute().asJsoup() + for (element in document.select(".comic-contain amp-img")) { + val imageUrl = element.attr("data-src") + val path = imageUrl.substring(imageUrl.indexOf('/', startIndex = 8)) // Skip "https://" + pathToUrl[path] = imageUrl } - url = document.selectFirst(Evaluator.Id("next-chapter")) + val url = document.selectFirst(Evaluator.Id("next-chapter")) ?.takeIf { val text = it.text() text == "下一页" || text == "下一頁" } ?.attr("href") ?: break + request = GET(url, headers) } - pageList + pathToUrl.values.mapIndexed { index, imageUrl -> Page(index, imageUrl = imageUrl) } + } + + override fun imageRequest(page: Page): Request { + val url = page.imageUrl!!.replace(".baozicdn.com", ".baozimh.com") + return GET(url, headers) } override fun pageListParse(document: Document) = throw UnsupportedOperationException("Not used.") @@ -189,7 +203,7 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { // impossible to search a manga and use the filters return if (query.isNotEmpty()) { - val baseUrl = baseUrl.replace("webmota.com", "baozimh.com") + val baseUrl = baseUrl.replace(".dinnerku.com", ".baozimh.com") val url = baseUrl.toHttpUrl().newBuilder() .addEncodedPathSegment("search") .addQueryParameter("q", query) @@ -222,14 +236,15 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { override fun setupPreferenceScreen(screen: PreferenceScreen) { ListPreference(screen.context).apply { + val mirrors = MIRRORS + key = MIRROR_PREF title = "使用镜像网址" - entries = MIRRORS - entryValues = MIRRORS + entries = mirrors + entryValues = mirrors summary = "已选择:%s\n" + - "重启生效,切换简繁体后需要迁移才能刷新漫画标题。\n" + - "搜索漫画时自动使用 baozimh.com 域名以避免出错。" - setDefaultValue(MIRRORS[0]) + "重启生效,切换简繁体后需要迁移才能刷新漫画标题。" + setDefaultValue(mirrors[0]) }.let { screen.addPreference(it) } ListPreference(screen.context).apply { @@ -251,8 +266,7 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { summary = "已选择:%s\n" + "部分作品的章节顺序错误,最新章节总是显示为一个旧章节,导致检查更新时新章节被错标为已读。" + "开启后,将会正确判断新章节和已读情况,但是错误的章节顺序不会改变。" + - "如果作品有章节标号重复,开启或关闭后第一次刷新会导致它们的阅读状态同步。" + - "开启或关闭强力模式后第一次刷新会将所有未标号的章节标记为未读。" + "警告:修改此设置后第一次刷新可能会导致已读状态出现错乱,请谨慎使用。" entries = arrayOf("关闭", "开启 (对有标号的章节有效)", "强力模式 (对所有章节有效)") entryValues = arrayOf(CHAPTER_ORDER_DISABLED, CHAPTER_ORDER_ENABLED, CHAPTER_ORDER_AGGRESSIVE) setDefaultValue(CHAPTER_ORDER_DISABLED) @@ -263,13 +277,22 @@ class Baozi : ParsedHttpSource(), ConfigurableSource { const val ID_SEARCH_PREFIX = "id:" private const val MIRROR_PREF = "MIRROR" - private val MIRRORS = arrayOf( + private val MIRRORS get() = arrayOf( "cn.baozimh.com", - "cn.webmota.com", "tw.baozimh.com", - "tw.webmota.com", "www.baozimh.com", + "cn.webmota.com", + "tw.webmota.com", "www.webmota.com", + "cn.kukuc.co", + "tw.kukuc.co", + "www.kukuc.co", + "cn.czmanga.com", + "tw.czmanga.com", + "www.czmanga.com", + "cn.dinnerku.com", + "tw.dinnerku.com", + "www.dinnerku.com", ) private const val DEFAULT_LEVEL = BaoziBanner.NORMAL.toString() diff --git a/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/RedirectDomainInterceptor.kt b/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/RedirectDomainInterceptor.kt new file mode 100644 index 0000000000..c39564b4df --- /dev/null +++ b/src/zh/baozimanhua/src/eu/kanade/tachiyomi/extension/zh/baozimanhua/RedirectDomainInterceptor.kt @@ -0,0 +1,19 @@ +package eu.kanade.tachiyomi.extension.zh.baozimanhua + +import okhttp3.Interceptor +import okhttp3.Response + +class RedirectDomainInterceptor(private val domain: String) : Interceptor { + + class Tag + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + val response = chain.proceed(request) + if (!response.isRedirect || request.tag(Tag::class) == null) return response + + val location = response.header("Location")!! + val newLocation = request.url.resolve(location)!!.newBuilder().host(domain).build() + return response.newBuilder().header("Location", newLocation.toString()).build() + } +} diff --git a/src/zh/baozimhorg/AndroidManifest.xml b/src/zh/baozimhorg/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/baozimhorg/AndroidManifest.xml +++ b/src/zh/baozimhorg/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/bh3/AndroidManifest.xml b/src/zh/bh3/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/bh3/AndroidManifest.xml +++ b/src/zh/bh3/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/boylove/AndroidManifest.xml b/src/zh/boylove/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/boylove/AndroidManifest.xml +++ b/src/zh/boylove/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/comicabc/AndroidManifest.xml b/src/zh/comicabc/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/comicabc/AndroidManifest.xml +++ b/src/zh/comicabc/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/dmzj/AndroidManifest.xml b/src/zh/dmzj/AndroidManifest.xml index 7121a9eb6d..dfea2cf99a 100644 --- a/src/zh/dmzj/AndroidManifest.xml +++ b/src/zh/dmzj/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/zh/happymh/AndroidManifest.xml b/src/zh/happymh/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/happymh/AndroidManifest.xml +++ b/src/zh/happymh/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/iqiyi/AndroidManifest.xml b/src/zh/iqiyi/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/iqiyi/AndroidManifest.xml +++ b/src/zh/iqiyi/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/jinmantiantang/AndroidManifest.xml b/src/zh/jinmantiantang/AndroidManifest.xml index 1d597445ea..7a74de2235 100644 --- a/src/zh/jinmantiantang/AndroidManifest.xml +++ b/src/zh/jinmantiantang/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity @@ -33,4 +32,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/zh/jinmantiantang/build.gradle b/src/zh/jinmantiantang/build.gradle index 69c77936df..4437de56ac 100644 --- a/src/zh/jinmantiantang/build.gradle +++ b/src/zh/jinmantiantang/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Jinman Tiantang' pkgNameSuffix = 'zh.jinmantiantang' extClass = '.Jinmantiantang' - extVersionCode = 37 + extVersionCode = 38 isNsfw = true } diff --git a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt index 5cfab811cf..f187d0a6ac 100644 --- a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt +++ b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/Jinmantiantang.kt @@ -48,7 +48,7 @@ class Jinmantiantang : ParsedHttpSource(), ConfigurableSource { preferences.getString(MAINSITE_RATELIMIT_PREF, MAINSITE_RATELIMIT_PREF_DEFAULT)!!.toInt(), preferences.getString(MAINSITE_RATELIMIT_PERIOD, MAINSITE_RATELIMIT_PERIOD_DEFAULT)!!.toLong(), ) - .addInterceptor(updateUrlInterceptor) + .apply { interceptors().add(0, updateUrlInterceptor) } .addInterceptor(ScrambledImageInterceptor).build() // 点击量排序(人气) diff --git a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangPreferences.kt b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangPreferences.kt index 7c17a1aab8..d981225361 100644 --- a/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangPreferences.kt +++ b/src/zh/jinmantiantang/src/eu/kanade/tachiyomi/extension/zh/jinmantiantang/JinmantiantangPreferences.kt @@ -93,7 +93,7 @@ private val SITE_ENTRIES_ARRAY get() = arrayOf( "jmcomic1.me", ) -private const val DEFAULT_LIST = "jm-comic2.org,jm-comic3.org,jm-comic1.org" +private const val DEFAULT_LIST = "jm-comic3.art,jm-comic1.art,jm-comic2.ark" private const val DEFAULT_LIST_PREF = "defaultBaseUrlList" private const val URL_LIST_PREF = "baseUrlList" @@ -132,12 +132,12 @@ class UpdateUrlInterceptor(private val preferences: SharedPreferences) : Interce response.close() Result.success(response) } catch (e: Throwable) { - if (chain.call().isCanceled()) throw e + if (chain.call().isCanceled() || e.message?.contains("Cloudflare") == true) throw e Result.failure(e) } if (isUpdated || updateUrl(chain)) { - throw IOException("镜像网址已自动更新,请在插件设置中选择合适的镜像网址并重启应用") + throw IOException("镜像网址已自动更新,请在插件设置中选择合适的镜像网址并重启应用(如果反复提示,可能是服务器故障)") } return failedResponse.getOrThrow() } diff --git a/src/zh/kuaikanmanhua/AndroidManifest.xml b/src/zh/kuaikanmanhua/AndroidManifest.xml index 66ea74250d..c88d319665 100644 --- a/src/zh/kuaikanmanhua/AndroidManifest.xml +++ b/src/zh/kuaikanmanhua/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".zh.kuaikanmanhua.KuaikanmanhuaUrlActivity" @@ -27,4 +26,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/zh/mangabz/AndroidManifest.xml b/src/zh/mangabz/AndroidManifest.xml index d4f898a9ea..3e4f471f91 100644 --- a/src/zh/mangabz/AndroidManifest.xml +++ b/src/zh/mangabz/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/zh/manhuadb/AndroidManifest.xml b/src/zh/manhuadb/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/manhuadb/AndroidManifest.xml +++ b/src/zh/manhuadb/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/manhuagui/AndroidManifest.xml b/src/zh/manhuagui/AndroidManifest.xml index 1fb6b00657..5724a8a1b0 100644 --- a/src/zh/manhuagui/AndroidManifest.xml +++ b/src/zh/manhuagui/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity diff --git a/src/zh/manhuagui/build.gradle b/src/zh/manhuagui/build.gradle index fe81edcc42..e94c073bce 100644 --- a/src/zh/manhuagui/build.gradle +++ b/src/zh/manhuagui/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'ManHuaGui' pkgNameSuffix = 'zh.manhuagui' extClass = '.Manhuagui' - extVersionCode = 16 + extVersionCode = 17 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/manhuagui/src/eu/kanade/tachiyomi/extension/zh/manhuagui/Manhuagui.kt b/src/zh/manhuagui/src/eu/kanade/tachiyomi/extension/zh/manhuagui/Manhuagui.kt index 2432b822e9..b988ae0510 100644 --- a/src/zh/manhuagui/src/eu/kanade/tachiyomi/extension/zh/manhuagui/Manhuagui.kt +++ b/src/zh/manhuagui/src/eu/kanade/tachiyomi/extension/zh/manhuagui/Manhuagui.kt @@ -43,14 +43,15 @@ import java.io.IOException import java.text.SimpleDateFormat import java.util.Locale -class Manhuagui : ConfigurableSource, ParsedHttpSource() { +class Manhuagui( + override val name: String = "漫画柜", + override val lang: String = "zh", +) : ConfigurableSource, ParsedHttpSource() { private val preferences: SharedPreferences by lazy { Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) } - override val name = "漫画柜" - private val baseHost = if (preferences.getBoolean(USE_MIRROR_URL_PREF, false)) { "mhgui.com" } else { @@ -63,7 +64,6 @@ class Manhuagui : ConfigurableSource, ParsedHttpSource() { } else { "https://www.$baseHost" } - override val lang = "zh" override val supportsLatest = true private val imageServer = arrayOf("https://i.hamreus.com", "https://cf.hamreus.com") diff --git a/src/zh/manhuaren/AndroidManifest.xml b/src/zh/manhuaren/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/manhuaren/AndroidManifest.xml +++ b/src/zh/manhuaren/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/manhuaren/build.gradle b/src/zh/manhuaren/build.gradle index 118de011f6..3a4f53fdbc 100644 --- a/src/zh/manhuaren/build.gradle +++ b/src/zh/manhuaren/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Manhuaren' pkgNameSuffix = 'zh.manhuaren' extClass = '.Manhuaren' - extVersionCode = 11 + extVersionCode = 13 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/manhuaren/src/eu/kanade/tachiyomi/extension/zh/manhuaren/Manhuaren.kt b/src/zh/manhuaren/src/eu/kanade/tachiyomi/extension/zh/manhuaren/Manhuaren.kt index c78ef819a9..3340d7431f 100644 --- a/src/zh/manhuaren/src/eu/kanade/tachiyomi/extension/zh/manhuaren/Manhuaren.kt +++ b/src/zh/manhuaren/src/eu/kanade/tachiyomi/extension/zh/manhuaren/Manhuaren.kt @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.extension.zh.manhuaren import android.text.format.DateFormat +import android.util.Base64 import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage @@ -12,18 +13,26 @@ import okhttp3.CacheControl import okhttp3.Headers import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.Request +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response +import okio.Buffer import org.json.JSONArray import org.json.JSONObject import java.net.URLEncoder +import java.security.KeyFactory import java.security.MessageDigest +import java.security.spec.X509EncodedKeySpec import java.text.SimpleDateFormat -import java.util.ArrayList import java.util.Date import java.util.Locale import java.util.UUID import java.util.concurrent.TimeUnit.MINUTES +import javax.crypto.Cipher +import kotlin.random.Random +import kotlin.random.nextUBytes class Manhuaren : HttpSource() { override val lang = "zh" @@ -34,45 +43,188 @@ class Manhuaren : HttpSource() { private val pageSize = 20 private val baseHttpUrl = baseUrl.toHttpUrlOrNull()!! - private val c = "4e0a48e1c0b54041bce9c8f0e036124d" - private val cacheControl: CacheControl by lazy { CacheControl.Builder().maxAge(10, MINUTES).build() } - private val userId = (100000000..4294967295).random().toString() + private val gsnSalt = "4e0a48e1c0b54041bce9c8f0e036124d" + private val encodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmFCg289dTws27v8GtqIffkP4zgFR+MYIuUIeVO5AGiBV0rfpRh5gg7i8RrT12E9j6XwKoe3xJz1khDnPc65P5f7CJcNJ9A8bj7Al5K4jYGxz+4Q+n0YzSllXPit/Vz/iW5jFdlP6CTIgUVwvIoGEL2sS4cqqqSpCDKHSeiXh9CtMsktc6YyrSN+8mQbBvoSSew18r/vC07iQiaYkClcs7jIPq9tuilL//2uR9kWn5jsp8zHKVjmXuLtHDhM9lObZGCVJwdlN2KDKTh276u/pzQ1s5u8z/ARtK26N8e5w8mNlGcHcHfwyhjfEQurvrnkqYH37+12U3jGk5YNHGyOPcwIDAQAB" + private val imei: String by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { generateIMEI() } + private val token: String by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { fetchToken() } + private var userId = "-1" + private var lastUsedTime = "" + + private fun randomNumber(length: Int): String { + var str = "" + for (i in 1..length) { + str += (0..9).random().toString() + } + return str + } + + private fun addLuhnCheckDigit(str: String): String { + var sum = 0 + str.toCharArray().forEachIndexed { i, it -> + var v = Character.getNumericValue(it) + sum += if (i % 2 == 0) { + v + } else { + v *= 2 + if (v < 10) { + v + } else { + v - 9 + } + } + } + var checkDigit = sum % 10 + if (checkDigit != 0) { + checkDigit = 10 - checkDigit + } + + return "$str$checkDigit" + } + + private fun generateIMEI(): String { + return addLuhnCheckDigit(randomNumber(14)) + } + + private fun generateSimSerialNumber(): String { + return addLuhnCheckDigit("891253${randomNumber(12)}") + } + + private fun fetchToken(): String { + val res = client.newCall(getAnonyUser()).execute() + val body = JSONObject(res.body.string()) + val response = body.getJSONObject("response") + val tokenResult = response.getJSONObject("tokenResult") + val scheme = tokenResult.getString("scheme") + val parameter = tokenResult.getString("parameter") + + userId = response.getString("userId") + lastUsedTime = generateLastUsedTime() + return "$scheme $parameter" + } + + private fun generateLastUsedTime(): String { + return ((Date().time / 1000).toInt() * 1000).toString() + } + + private fun encrypt(message: String): String { + val x509EncodedKeySpec = X509EncodedKeySpec(Base64.decode(encodedPublicKey, Base64.DEFAULT)) + val publicKey = KeyFactory.getInstance("RSA").generatePublic(x509EncodedKeySpec) + val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding") + cipher.init(Cipher.ENCRYPT_MODE, publicKey) + + return Base64.encodeToString(cipher.doFinal(message.toByteArray()), Base64.NO_WRAP) + } + + @OptIn(ExperimentalUnsignedTypes::class) + private fun getAnonyUser(): Request { + val url = baseHttpUrl.newBuilder() + .addPathSegments("v1/user/createAnonyUser2") + .build() + + val simSerialNumber = generateSimSerialNumber() + val mac = Random.nextUBytes(6) + .joinToString(":") { it.toString(16).padStart(2, '0') } + val androidId = Random.nextUBytes(8) + .joinToString("") { it.toString(16).padStart(2, '0') } + .replaceFirst("^0+".toRegex(), "") + .uppercase() + + val keysMap = ArrayList<HashMap<String, Any?>>().apply { + add( + HashMap<String, Any?>().apply { + put("key", encrypt(imei)) + put("keyType", "0") + }, + ) + add( + HashMap<String, Any?>().apply { + put("key", encrypt("mac: $mac")) + put("keyType", "1") + }, + ) + add( + HashMap<String, Any?>().apply { + put("key", encrypt(androidId)) // https://developer.android.com/reference/android/provider/Settings.Secure#ANDROID_ID + put("keyType", "2") + }, + ) + add( + HashMap<String, Any?>().apply { + put("key", encrypt(simSerialNumber)) // https://developer.android.com/reference/android/telephony/TelephonyManager#getSimSerialNumber() + put("keyType", "3") + }, + ) + add( + HashMap<String, Any?>().apply { + put("key", encrypt(UUID.randomUUID().toString())) + put("keyType", "-1") + }, + ) + } + val bodyMap = HashMap<String, Any?>().apply { + put("keys", keysMap) + } - private fun generateGSNHash(url: HttpUrl): String { - var s = c + "GET" - url.queryParameterNames.toSortedSet().forEach { + return myPost( + url, + JSONObject(bodyMap).toString() + .replaceFirst("^/+".toRegex(), "") + .toRequestBody("application/json".toMediaTypeOrNull()), + ) + } + + private fun addGsnHash(request: Request): Request { + val isPost = request.method == "POST" + + val params = request.url.queryParameterNames.toMutableSet() + val bodyBuffer = Buffer() + if (isPost) { + params.add("body") + request.body?.writeTo(bodyBuffer) + } + + var str = gsnSalt + request.method + params.toSortedSet().forEach { if (it != "gsn") { - s += it - s += urlEncode(url.queryParameterValues(it)[0]) + val value = if (isPost && it == "body") bodyBuffer.readUtf8() else request.url.queryParameter(it) + str += "$it${urlEncode(value)}" } } - s += c - return hashString("MD5", s) + str += gsnSalt + + val gsn = hashString("MD5", str) + val newUrl = request.url.newBuilder() + .addQueryParameter("gsn", gsn) + .build() + + return request.newBuilder() + .url(newUrl) + .build() } - private fun myGet(url: HttpUrl): Request { + private fun myRequest(url: HttpUrl, method: String, body: RequestBody?): Request { val now = DateFormat.format("yyyy-MM-dd+HH:mm:ss", Date()).toString() - val realUrl = url.newBuilder() + val newUrl = url.newBuilder() .setQueryParameter("gsm", "md5") .setQueryParameter("gft", "json") .setQueryParameter("gak", "android_manhuaren2") .setQueryParameter("gat", "") .setQueryParameter("gui", userId) - .setQueryParameter("gts", now) // timestamp yyyy-MM-dd+HH:mm:ss + .setQueryParameter("gts", now) .setQueryParameter("gut", "0") // user type .setQueryParameter("gem", "1") - .setQueryParameter("gaui", "1") + .setQueryParameter("gaui", userId) .setQueryParameter("gln", "") // location .setQueryParameter("gcy", "US") // country .setQueryParameter("gle", "zh") // language - .setQueryParameter("gcl", "dm5") // umeng channel + .setQueryParameter("gcl", "dm5") // Umeng channel .setQueryParameter("gos", "1") // OS (int) - .setQueryParameter("gov", "22_5.1.1") // "{Build.VERSION.SDK_INT}_{Build.VERSION.RELEASE}" + .setQueryParameter("gov", "33_13") // "{Build.VERSION.SDK_INT}_{Build.VERSION.RELEASE}" .setQueryParameter("gav", "7.0.1") // app version - .setQueryParameter("gdi", "358240051111110") // device info - .setQueryParameter("gfcl", "dm5") // umeng channel config - .setQueryParameter("gfut", "1688140800000") // first used time - .setQueryParameter("glut", "1688140800000") // last used time + .setQueryParameter("gdi", imei) + .setQueryParameter("gfcl", "dm5") // Umeng channel config + .setQueryParameter("gfut", lastUsedTime) // first used time + .setQueryParameter("glut", lastUsedTime) // last used time .setQueryParameter("gpt", "com.mhr.mangamini") // package name .setQueryParameter("gciso", "us") // https://developer.android.com/reference/android/telephony/TelephonyManager#getSimCountryIso() .setQueryParameter("glot", "") // longitude @@ -87,11 +239,28 @@ class Manhuaren : HttpSource() { .setQueryParameter("glcn", "") // country name .setQueryParameter("glcc", "") // country code .setQueryParameter("gflcc", "") // first location country code + .build() + + return addGsnHash( + Request.Builder() + .method(method, body) + .url(newUrl) + .headers(headers) + .build(), + ) + } + + private fun myPost(url: HttpUrl, body: RequestBody?): Request { + return myRequest(url, "POST", body).newBuilder() + .cacheControl(CacheControl.Builder().noCache().noStore().build()) + .build() + } - return Request.Builder() - .url(realUrl.setQueryParameter("gsn", generateGSNHash(realUrl.build())).build()) - .headers(headers) - .cacheControl(cacheControl) + private fun myGet(url: HttpUrl): Request { + val authorization = token + return myRequest(url, "GET", null).newBuilder() + .addHeader("Authorization", authorization) + .cacheControl(CacheControl.Builder().maxAge(10, MINUTES).build()) .build() } @@ -100,27 +269,27 @@ class Manhuaren : HttpSource() { put("at", -1) put("av", "7.0.1") // app version put("ciso", "us") // https://developer.android.com/reference/android/telephony/TelephonyManager#getSimCountryIso() - put("cl", "dm5") // umeng channel + put("cl", "dm5") // Umeng channel put("cy", "US") // country - put("di", "358240051111110") // device info - put("dm", "Android SDK built for x86") // Build.MODEL - put("fcl", "dm5") // umeng channel config + put("di", imei) + put("dm", "Pixel 6 Pro") // https://developer.android.com/reference/android/os/Build#MODEL + put("fcl", "dm5") // Umeng channel config put("ft", "mhr") // from type - put("fut", "1688140800000") // first used time + put("fut", lastUsedTime) // first used time put("installation", "dm5") put("le", "zh") // language put("ln", "") // location - put("lut", "1688140800000") // last used time - put("nt", 4) + put("lut", lastUsedTime) // last used time + put("nt", 3) put("os", 1) // OS (int) - put("ov", "22_5.1.1") // "{Build.VERSION.SDK_INT}_{Build.VERSION.RELEASE}" + put("ov", "33_13") // "{Build.VERSION.SDK_INT}_{Build.VERSION.RELEASE}" put("pt", "com.mhr.mangamini") // package name - put("rn", "1440x2952") // screen "{width}x{height}" + put("rn", "1400x3120") // screen "{width}x{height}" put("st", 0) } val yqppMap = HashMap<String, Any?>().apply { put("ciso", "us") // https://developer.android.com/reference/android/telephony/TelephonyManager#getSimCountryIso() - put("laut", "0") // is allow location (0 or 1) + put("laut", "0") // is allow location ("0" or "1") put("lot", "") // longitude put("lat", "") // latitude put("cut", "GMT+8") // time zone @@ -140,7 +309,7 @@ class Manhuaren : HttpSource() { add("yq_is_anonymous", "1") add("x-request-id", UUID.randomUUID().toString()) add("X-Yq-Yqpp", JSONObject(yqppMap).toString()) - add("User-Agent", "Mozilla/5.0 (Linux; Android 5.1.1; Android SDK built for x86 Build/LMY48X) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/39.0.0.0 Mobile Safari/537.36") + add("User-Agent", "Dalvik/2.1.0 (Linux; U; Android 13; Pixel 6 Pro Build/TQ3A.230705.001)") } } @@ -161,7 +330,7 @@ class Manhuaren : HttpSource() { } private fun urlEncode(str: String?): String { - return URLEncoder.encode(str, "UTF-8") + return URLEncoder.encode(str ?: "", "UTF-8") .replace("+", "%20") .replace("%7E", "~") .replace("*", "%2A") @@ -202,7 +371,7 @@ class Manhuaren : HttpSource() { .addQueryParameter("start", (pageSize * (page - 1)).toString()) .addQueryParameter("limit", pageSize.toString()) .addQueryParameter("sort", "0") - .addPathSegments("/v2/manga/getCategoryMangas") + .addPathSegments("v2/manga/getCategoryMangas") .build() return myGet(url) } @@ -214,7 +383,7 @@ class Manhuaren : HttpSource() { .addQueryParameter("start", (pageSize * (page - 1)).toString()) .addQueryParameter("limit", pageSize.toString()) .addQueryParameter("sort", "1") - .addPathSegments("/v2/manga/getCategoryMangas") + .addPathSegments("v2/manga/getCategoryMangas") .build() return myGet(url) } @@ -233,30 +402,33 @@ class Manhuaren : HttpSource() { .addQueryParameter("limit", pageSize.toString()) if (query != "") { url = url.addQueryParameter("keywords", query) - .addPathSegments("/v1/search/getSearchManga") - return myGet(url.build()) - } - filters.forEach { filter -> - when (filter) { - is SortFilter -> url = url.setQueryParameter("sort", filter.getId()) - is CategoryFilter -> { - url = url.setQueryParameter("subCategoryId", filter.getId()) - .setQueryParameter("subCategoryType", filter.getType()) + .addPathSegments("v1/search/getSearchManga") + } else { + filters.forEach { filter -> + when (filter) { + is SortFilter -> { + url = url.setQueryParameter("sort", filter.getId()) + } + is CategoryFilter -> { + url = url.setQueryParameter("subCategoryId", filter.getId()) + .setQueryParameter("subCategoryType", filter.getType()) + } + else -> {} } - else -> {} } + url = url.addPathSegments("v2/manga/getCategoryMangas") } - url = url.addPathSegments("/v2/manga/getCategoryMangas") return myGet(url.build()) } override fun searchMangaParse(response: Response): MangasPage { val res = response.body.string() val obj = JSONObject(res).getJSONObject("response") - if (obj.has("result")) { - return mangasFromJSONArray(obj.getJSONArray("result")) - } - return mangasFromJSONArray(obj.getJSONArray("mangas")) + return mangasFromJSONArray( + obj.getJSONArray( + if (obj.has("result")) "result" else "mangas", + ), + ) } override fun mangaDetailsParse(response: Response) = SManga.create().apply { diff --git a/src/zh/manwa/AndroidManifest.xml b/src/zh/manwa/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/manwa/AndroidManifest.xml +++ b/src/zh/manwa/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/manwa/build.gradle b/src/zh/manwa/build.gradle index 97fcbfb328..7545cb3526 100644 --- a/src/zh/manwa/build.gradle +++ b/src/zh/manwa/build.gradle @@ -5,7 +5,7 @@ ext { extName = 'Manwa' pkgNameSuffix = 'zh.manwa' extClass = '.Manwa' - extVersionCode = 2 + extVersionCode = 3 isNsfw = true } diff --git a/src/zh/manwa/src/eu/kanade/tachiyomi/extension/zh/manwa/Manwa.kt b/src/zh/manwa/src/eu/kanade/tachiyomi/extension/zh/manwa/Manwa.kt index c88511d185..90b6e7b47e 100644 --- a/src/zh/manwa/src/eu/kanade/tachiyomi/extension/zh/manwa/Manwa.kt +++ b/src/zh/manwa/src/eu/kanade/tachiyomi/extension/zh/manwa/Manwa.kt @@ -27,6 +27,7 @@ import okhttp3.Response import okhttp3.ResponseBody.Companion.toResponseBody import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import rx.Observable import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy @@ -61,12 +62,12 @@ class Manwa : ParsedHttpSource(), ConfigurableSource { originalResponse } } + override val client: OkHttpClient = network.client.newBuilder() .addNetworkInterceptor(rewriteOctetStream) .build() // Popular - override fun popularMangaRequest(page: Int) = GET("$baseUrl/rank", headers) override fun popularMangaNextPageSelector(): String? = null override fun popularMangaSelector(): String = "#rankList_2 > a" @@ -77,7 +78,6 @@ class Manwa : ParsedHttpSource(), ConfigurableSource { } // Latest - override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/getUpdate?page=${page * 15 - 15}&date=", headers) override fun latestUpdatesParse(response: Response): MangasPage { // Get image host @@ -142,14 +142,18 @@ class Manwa : ParsedHttpSource(), ConfigurableSource { return super.chapterListParse(response).reversed() } - // Pages + override fun fetchPageList(chapter: SChapter): Observable<List<Page>> { + client.newCall(GET("$baseUrl/static/images/pv.gif")).execute() + return super.fetchPageList(chapter) + } + // Pages override fun pageListRequest(chapter: SChapter): Request { return GET("$baseUrl${chapter.url}?img_host=${preferences.getString(IMAGE_HOST_KEY, IMAGE_HOST_ENTRY_VALUES[0])}", headers) } override fun pageListParse(document: Document): List<Page> = mutableListOf<Page>().apply { - document.select("#cp_img > img[data-r-src]").forEachIndexed { index, it -> + document.select("#cp_img > .img-content > img[data-r-src]").forEachIndexed { index, it -> add(Page(index, "", it.attr("data-r-src"))) } } diff --git a/src/zh/noyacg/AndroidManifest.xml b/src/zh/noyacg/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/noyacg/AndroidManifest.xml +++ b/src/zh/noyacg/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/picacomic/AndroidManifest.xml b/src/zh/picacomic/AndroidManifest.xml index 55dea899bb..8072ee00db 100644 --- a/src/zh/picacomic/AndroidManifest.xml +++ b/src/zh/picacomic/AndroidManifest.xml @@ -1,3 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension" /> \ No newline at end of file +<manifest /> diff --git a/src/zh/roumanwu/AndroidManifest.xml b/src/zh/roumanwu/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/roumanwu/AndroidManifest.xml +++ b/src/zh/roumanwu/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/roumanwu/build.gradle b/src/zh/roumanwu/build.gradle index 2fe3e0a41f..0e889e7475 100644 --- a/src/zh/roumanwu/build.gradle +++ b/src/zh/roumanwu/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Roumanwu' pkgNameSuffix = 'zh.roumanwu' extClass = '.Roumanwu' - extVersionCode = 4 + extVersionCode = 5 isNsfw = true } diff --git a/src/zh/roumanwu/src/eu/kanade/tachiyomi/extension/zh/roumanwu/Roumanwu.kt b/src/zh/roumanwu/src/eu/kanade/tachiyomi/extension/zh/roumanwu/Roumanwu.kt index 61db16dd34..15b8046969 100644 --- a/src/zh/roumanwu/src/eu/kanade/tachiyomi/extension/zh/roumanwu/Roumanwu.kt +++ b/src/zh/roumanwu/src/eu/kanade/tachiyomi/extension/zh/roumanwu/Roumanwu.kt @@ -116,7 +116,7 @@ class Roumanwu : HttpSource(), ConfigurableSource { private const val MIRROR_PREF_SUMMARY = "使用镜像网址。重启软件生效。" // 地址: https://rou.pub/dizhi - private val MIRRORS = arrayOf("https://rouman5.com", "https://rm01.xyz") + private val MIRRORS = arrayOf("https://rouman5.com", "https://roum1.xyz") private val MIRRORS_DESC = arrayOf("主站", "镜像") private const val MIRROR_DEFAULT = 1.toString() // use mirror diff --git a/src/zh/sixmh/AndroidManifest.xml b/src/zh/sixmh/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/sixmh/AndroidManifest.xml +++ b/src/zh/sixmh/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/tencentcomics/AndroidManifest.xml b/src/zh/tencentcomics/AndroidManifest.xml index a06be62c15..bb75b7458e 100644 --- a/src/zh/tencentcomics/AndroidManifest.xml +++ b/src/zh/tencentcomics/AndroidManifest.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="eu.kanade.tachiyomi.extension"> +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <application> <activity android:name=".zh.tencentcomics.TencentComicsUrlActivity" @@ -27,4 +26,4 @@ </intent-filter> </activity> </application> -</manifest> \ No newline at end of file +</manifest> diff --git a/src/zh/terrahistoricus/AndroidManifest.xml b/src/zh/terrahistoricus/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/terrahistoricus/AndroidManifest.xml +++ b/src/zh/terrahistoricus/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/vomic/AndroidManifest.xml b/src/zh/vomic/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/vomic/AndroidManifest.xml +++ b/src/zh/vomic/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/vomic/build.gradle b/src/zh/vomic/build.gradle index e52e592d3f..4ba950d954 100644 --- a/src/zh/vomic/build.gradle +++ b/src/zh/vomic/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'vomic' pkgNameSuffix = 'zh.vomic' extClass = '.Vomic' - extVersionCode = 2 + extVersionCode = 4 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/vomic/src/eu/kanade/tachiyomi/extension/zh/vomic/Vomic.kt b/src/zh/vomic/src/eu/kanade/tachiyomi/extension/zh/vomic/Vomic.kt index 240760b363..a1bb24a2d1 100644 --- a/src/zh/vomic/src/eu/kanade/tachiyomi/extension/zh/vomic/Vomic.kt +++ b/src/zh/vomic/src/eu/kanade/tachiyomi/extension/zh/vomic/Vomic.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.extension.zh.vomic import android.app.Application import android.util.Base64 -import androidx.preference.ListPreference +import androidx.preference.EditTextPreference import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.source.ConfigurableSource @@ -21,6 +21,7 @@ import okhttp3.Response import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy +import java.io.IOException import java.text.SimpleDateFormat import java.util.Locale import javax.crypto.Cipher @@ -40,13 +41,30 @@ class Vomic : HttpSource(), ConfigurableSource { private val apiUrl: String init { - val mirrors = MIRRORS - val mirrorIndex = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) - .getString(MIRROR_PREF, "0")!!.toInt().coerceAtMost(mirrors.size - 1) - baseUrl = "http://" + mirrors[mirrorIndex] - apiUrl = "http://" + mirrors[mirrorIndex].replace("www.", "api.") + val domain = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000).getString(DOMAIN_PREF, DEFAULT_DOMAIN)!! + if (domain.startsWith("www.") || domain.startsWith("api.")) { + val tld = domain.substring(4) + baseUrl = "http://www.$tld" + apiUrl = "http://api.$tld" + } else { + val url = "http://$domain" + baseUrl = url + apiUrl = url + } } + override val client = network.client.newBuilder().addInterceptor { chain -> + try { + val response = chain.proceed(chain.request()) + if (response.isSuccessful) { + return@addInterceptor response + } + response.close() + } catch (_: Throwable) { + } + throw IOException("请在插件设置中修改网址") + }.build() + override fun headersBuilder() = Headers.Builder().add("User-Agent", System.getProperty("http.agent")!!) override fun popularMangaRequest(page: Int) = GET("$apiUrl/api/v1/rank/rank-data?rank_id=1&page=$page", headers) @@ -150,14 +168,11 @@ class Vomic : HttpSource(), ConfigurableSource { } override fun setupPreferenceScreen(screen: PreferenceScreen) { - ListPreference(screen.context).apply { - val mirrors = MIRRORS - key = MIRROR_PREF - title = "镜像网址" - summary = "%s\n重启生效" - entries = mirrors - entryValues = Array(mirrors.size) { it.toString() } - setDefaultValue("0") + EditTextPreference(screen.context).apply { + key = DOMAIN_PREF + title = "网址" + summary = "不带 http:// 前缀,重启生效\n备选网址:$DEFAULT_DOMAIN 或 119.23.243.52" + setDefaultValue(DEFAULT_DOMAIN) }.let(screen::addPreference) } @@ -167,8 +182,8 @@ class Vomic : HttpSource(), ConfigurableSource { json.decodeFromString<ResponseDto<T>>(body.string()).data companion object { - private const val MIRROR_PREF = "MIRROR" - private val MIRRORS get() = arrayOf("www.vomicmh.com", "www.iewoai.com") + private const val DOMAIN_PREF = "DOMAIN" + private const val DEFAULT_DOMAIN = "www.vomicmh.com" private val dateFormat by lazy { SimpleDateFormat("yyyy-MM-dd hh:mm:ss", Locale.ENGLISH) } } diff --git a/src/zh/wnacg/AndroidManifest.xml b/src/zh/wnacg/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/wnacg/AndroidManifest.xml +++ b/src/zh/wnacg/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/yidan/AndroidManifest.xml b/src/zh/yidan/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/yidan/AndroidManifest.xml +++ b/src/zh/yidan/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest /> diff --git a/src/zh/yidan/build.gradle b/src/zh/yidan/build.gradle index f4b77e6992..34440c22d0 100644 --- a/src/zh/yidan/build.gradle +++ b/src/zh/yidan/build.gradle @@ -6,7 +6,7 @@ ext { extName = 'Yidan Girl' pkgNameSuffix = 'zh.yidan' extClass = '.Yidan' - extVersionCode = 1 + extVersionCode = 2 isNsfw = true } diff --git a/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Dto.kt b/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Dto.kt index 73c90434e7..ad7a944648 100644 --- a/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Dto.kt +++ b/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Dto.kt @@ -15,7 +15,7 @@ class MangaDto( private val coverPic: String?, private val id: Int, ) { - fun toSManga() = SManga.create().apply { + fun toSManga(baseUrl: String) = SManga.create().apply { url = id.toString() title = this@MangaDto.title author = this@MangaDto.author @@ -29,7 +29,7 @@ class MangaDto( "5" in mhcate.split(",") -> SManga.COMPLETED else -> SManga.ONGOING } - thumbnail_url = coverPic + thumbnail_url = if (coverPic?.startsWith("http") == true) coverPic else baseUrl + coverPic initialized = true } } diff --git a/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Yidan.kt b/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Yidan.kt index 085fdb7895..65b3e5c73a 100644 --- a/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Yidan.kt +++ b/src/zh/yidan/src/eu/kanade/tachiyomi/extension/zh/yidan/Yidan.kt @@ -47,7 +47,7 @@ class Yidan : HttpSource(), ConfigurableSource { override fun popularMangaParse(response: Response): MangasPage { val listing: ListingDto = response.parseAs() - val mangas = listing.list.map { it.toSManga() } + val mangas = listing.list.map { it.toSManga(baseUrl) } val hasNextPage = run { val url = response.request.url val pageSize = url.queryParameter("pageSize")!!.toInt() @@ -84,7 +84,7 @@ class Yidan : HttpSource(), ConfigurableSource { } override fun mangaDetailsParse(response: Response) = - response.parseAs<MangaDto>().toSManga() + response.parseAs<MangaDto>().toSManga(baseUrl) override fun chapterListRequest(manga: SManga) = GET("$baseUrl/prod-api/app-api/vv/mh-episodes/list?mhid=${manga.url}", headers) @@ -132,6 +132,6 @@ class Yidan : HttpSource(), ConfigurableSource { companion object { private const val MIRROR_PREF = "MIRROR" - private val MIRRORS get() = arrayOf("ydan.cc", "ydan.vip", "dans.cc") + private val MIRRORS get() = arrayOf("ydan.cc", "yidan.one", "yidan.in", "yidan.info") } } diff --git a/src/zh/zerobyw/AndroidManifest.xml b/src/zh/zerobyw/AndroidManifest.xml index 30deb7f797..8072ee00db 100644 --- a/src/zh/zerobyw/AndroidManifest.xml +++ b/src/zh/zerobyw/AndroidManifest.xml @@ -1,2 +1,2 @@ <?xml version="1.0" encoding="utf-8"?> -<manifest package="eu.kanade.tachiyomi.extension" /> +<manifest />