Compare commits

..

No commits in common. '7f98a7fb5c2f2178b2d6d9d246985b0ce208459f' and 'fe5534b006322188080f6a8fa1d3f04bddb3b6c1' have entirely different histories.

@ -1 +1 @@
total: 1255 total: 1251

@ -13,13 +13,15 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up enviroment 🔧 - name: Set up enviroment 🔧
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with: with:
java-version: '21' java-version: '17'
distribution: 'temurin' distribution: 'temurin'
- name: Set up Gradle 📦 - name: Set up Gradle 📦
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 uses: gradle/actions/setup-gradle@017a9effdb900e5b5b2fddfb590a105619dca3c3 # v4.4.2
with:
cache-read-only: true
- name: Compile parsers 🚀 - name: Compile parsers 🚀
run: ./gradlew compileKotlin run: ./gradlew compileKotlin

@ -17,13 +17,15 @@ jobs:
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Set up enviroment 🔧 - name: Set up enviroment 🔧
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0 uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with: with:
java-version: '21' java-version: '17'
distribution: 'temurin' distribution: 'temurin'
- name: Set up Gradle 📦 - name: Set up Gradle 📦
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3 uses: gradle/actions/setup-gradle@017a9effdb900e5b5b2fddfb590a105619dca3c3 # v4.4.2
with:
cache-read-only: true
- name: Compile parsers 🚀 - name: Compile parsers 🚀
run: ./gradlew compileKotlin run: ./gradlew compileKotlin

4
.gitignore vendored

@ -92,7 +92,3 @@ local.properties
.idea/**/runConfigurations.xml .idea/**/runConfigurations.xml
.idea/**/AndroidProjectSystem.xml .idea/**/AndroidProjectSystem.xml
.idea/caches/deviceStreaming.xml .idea/caches/deviceStreaming.xml
/.idea/copilot.data.migration.agent.xml
/.idea/copilot.data.migration.ask.xml
/.idea/copilot.data.migration.ask2agent.xml
/.idea/copilot.data.migration.edit.xml

@ -56,7 +56,6 @@ dependencies {
testImplementation(libs.junit.api) testImplementation(libs.junit.api)
testImplementation(libs.junit.engine) testImplementation(libs.junit.engine)
testImplementation(libs.junit.params) testImplementation(libs.junit.params)
testRuntimeOnly(libs.junit.launcher)
testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.quickjs) testImplementation(libs.quickjs)
} }

@ -1,5 +1,5 @@
plugins { plugins {
`kotlin-dsl` kotlin("jvm") version "2.2.10"
} }
repositories { repositories {
@ -11,7 +11,8 @@ kotlin {
} }
dependencies { dependencies {
implementation(libs.korte) implementation(gradleApi())
implementation(libs.simplexml) implementation("org.simpleframework:simple-xml:2.7.1")
implementation(libs.kotlinx.coroutines.core) implementation("com.soywiz.korlibs.korte:korte-jvm:4.0.10")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
} }

Binary file not shown.

@ -1,7 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
networkTimeout=10000 distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

47
buildSrc/gradlew vendored

@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -82,11 +80,13 @@ do
esac esac
done done
# This is normally unused APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH="\\\"\\\"" CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@ -133,29 +133,22 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -200,28 +193,18 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# Collect all arguments for the java command: # * put everything else in single quotes, so that it's not re-expanded.
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 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. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

@ -13,10 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@ -27,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -43,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
@ -59,34 +56,32 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH= set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

@ -1,7 +0,0 @@
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}

@ -1,15 +1,9 @@
## Following this blog: # Following this blog:
# https://proandroiddev.com/how-we-reduced-our-gradle-build-times-by-over-80-51f2b6d6b05b # https://proandroiddev.com/how-we-reduced-our-gradle-build-times-by-over-80-51f2b6d6b05b
kotlin.code.style=official kotlin.code.style=official
systemProp.org.gradle.unsafe.configuration-cache=false
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=4096m -XX:+UseParallelGC org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=4096m -XX:+UseParallelGC
org.gradle.vfs.watch=true
org.gradle.configureondemand=true org.gradle.configureondemand=true
org.gradle.configuration-cache.problems=warn org.gradle.parallel=true
org.gradle.caching=true
## Use these flags on local machine for faster build time org.gradle.unsafe.configuration-cache=true
# org.gradle.caching=true
# org.gradle.configuration-cache=true
# org.gradle.vfs.watch=true
# org.gradle.parallel=true
# org.gradle.workers.max=8
# org.gradle.configuration-cache.max-problems=8

@ -9,8 +9,6 @@ json = "20240303"
androidx-collection = "1.5.0" androidx-collection = "1.5.0"
jsoup = "1.21.2" jsoup = "1.21.2"
quickjs = "1.1.0" quickjs = "1.1.0"
korte = "4.0.10"
simplexml = "2.7.1"
[plugins] [plugins]
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
@ -20,7 +18,6 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
ksp-symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } ksp-symbol-processing-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" } kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
junit-launcher = { group = "org.junit.platform", name = "junit-platform-launcher" }
junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" } junit-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" } junit-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }
junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit" } junit-params = { group = "org.junit.jupiter", name = "junit-jupiter-params", version.ref = "junit" }
@ -30,5 +27,3 @@ json = { module = "org.json:json", version.ref = "json" }
androidx-collection = { module = "androidx.collection:collection", version.ref = "androidx-collection" } androidx-collection = { module = "androidx.collection:collection", version.ref = "androidx-collection" }
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
quickjs = { module = "io.webfolder:quickjs", version.ref = "quickjs" } quickjs = { module = "io.webfolder:quickjs", version.ref = "quickjs" }
korte = { module = "com.soywiz.korlibs.korte:korte-jvm", version.ref = "korte" }
simplexml = { module = "org.simpleframework:simple-xml", version.ref = "simplexml" }

Binary file not shown.

@ -1,7 +1,7 @@
#Wed Aug 27 01:56:37 ICT 2025
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip distributionSha256Sum=bd71102213493060956ec229d946beee57158dbd89d0e62b91bca0fa2c5f3531
networkTimeout=10000 distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

49
gradlew vendored

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015 the original authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -82,11 +80,13 @@ do
esac esac
done done
# This is normally unused APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH="\\\"\\\"" CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@ -133,29 +133,22 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@ -200,28 +193,18 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# Collect all arguments for the java command: # * put everything else in single quotes, so that it's not re-expanded.
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 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. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

41
gradlew.bat vendored

@ -13,10 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@ -27,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@ -43,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
@ -59,34 +56,32 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. 1>&2 echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo. 1>&2 echo.
echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation. 1>&2 echo location of your Java installation.
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH= set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

@ -1,2 +0,0 @@
jdk:
- openjdk17

@ -5,7 +5,6 @@ import androidx.collection.SparseArrayCompat
import okhttp3.HttpUrl import okhttp3.HttpUrl
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
@ -19,7 +18,6 @@ import java.util.*
private const val CHAPTERS_LIMIT = 99999 private const val CHAPTERS_LIMIT = 99999
@Broken("Original site closed")
@MangaSourceParser("COMICK_FUN", "ComicK") @MangaSourceParser("COMICK_FUN", "ComicK")
internal class ComickFunParser(context: MangaLoaderContext) : internal class ComickFunParser(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.COMICK_FUN, 20) { PagedMangaParser(context, MangaParserSource.COMICK_FUN, 20) {
@ -147,13 +145,8 @@ internal class ComickFunParser(context: MangaLoaderContext) :
) )
} }
val ja = try { val ja = webClient.httpGet(url.build()).parseJsonArray()
webClient.httpGet(url.build()).parseJsonArray() val tagsMap = tagsArray.get()
} catch (_: Exception) {
throw IllegalArgumentException("ComicK is down!")
}
val tagsMap = tagsArray.get()
return ja.mapJSON { jo -> return ja.mapJSON { jo ->
val slug = jo.getString("slug") val slug = jo.getString("slug")
Manga( Manga(

@ -19,7 +19,9 @@ import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import org.koitharu.kotatsu.parsers.Broken
@Broken("Need to fix getPages, most manga don't have chapter images due to faulty fetch logic")
@MangaSourceParser("KOHARU", "Schale.network", type = ContentType.HENTAI) @MangaSourceParser("KOHARU", "Schale.network", type = ContentType.HENTAI)
internal class Koharu(context: MangaLoaderContext) : internal class Koharu(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.KOHARU, 24) { PagedMangaParser(context, MangaParserSource.KOHARU, 24) {

@ -14,40 +14,11 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.bitmap.Rect import org.koitharu.kotatsu.parsers.bitmap.Rect
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.PagedMangaParser import org.koitharu.kotatsu.parsers.core.PagedMangaParser
import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.attrAsAbsoluteUrl
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.getCookies
import org.koitharu.kotatsu.parsers.util.mapChapters
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import org.koitharu.kotatsu.parsers.util.ownTextOrNull
import org.koitharu.kotatsu.parsers.util.parseFailed
import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.parseJson
import org.koitharu.kotatsu.parsers.util.parseSafe
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
import org.koitharu.kotatsu.parsers.util.splitByWhitespace
import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy import org.koitharu.kotatsu.parsers.util.suspendlazy.suspendLazy
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.parsers.util.urlEncoded
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Base64 import java.util.*
import java.util.EnumSet
import java.util.Locale
import kotlin.math.min import kotlin.math.min
private const val PIECE_SIZE = 200 private const val PIECE_SIZE = 200
@ -70,10 +41,6 @@ internal abstract class MangaFireParser(
SortOrder.RELEVANCE, SortOrder.RELEVANCE,
) )
override fun getRequestHeaders() = super.getRequestHeaders().newBuilder()
.add("Referer", "https://$domain/")
.build()
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) { override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys) super.onCreateConfig(keys)
keys.add(userAgentKey) keys.add(userAgentKey)
@ -128,30 +95,25 @@ internal abstract class MangaFireParser(
addQueryParameter("page", page.toString()) addQueryParameter("page", page.toString())
addQueryParameter("language[]", siteLang) addQueryParameter("language[]", siteLang)
when { when {
!filter.query.isNullOrEmpty() -> { !filter.query.isNullOrEmpty() -> {
val encodedQuery = filter.query.splitByWhitespace().joinToString(separator = "+") { part -> val encodedQuery = filter.query.splitByWhitespace().joinToString(separator = "+") { part ->
part.urlEncoded() part.urlEncoded()
} }
addEncodedQueryParameter("keyword", encodedQuery) addEncodedQueryParameter("keyword", encodedQuery)
addQueryParameter(
// Generate VRF for search query name = "sort",
val searchVrf = VrfGenerator.generate(filter.query.trim()) value = when (order) {
addQueryParameter("vrf", searchVrf) SortOrder.UPDATED -> "recently_updated"
SortOrder.POPULARITY -> "most_viewed"
addQueryParameter( SortOrder.RATING -> "scores"
name = "sort", SortOrder.NEWEST -> "release_date"
value = when (order) { SortOrder.ALPHABETICAL -> "title_az"
SortOrder.UPDATED -> "recently_updated" SortOrder.RELEVANCE -> "most_relevance"
SortOrder.POPULARITY -> "most_viewed" else -> ""
SortOrder.RATING -> "scores" },
SortOrder.NEWEST -> "release_date" )
SortOrder.ALPHABETICAL -> "title_az" }
SortOrder.RELEVANCE -> "most_relevance"
else -> ""
},
)
}
else -> { else -> {
filter.tagsExclude.forEach { tag -> filter.tagsExclude.forEach { tag ->
@ -298,18 +260,14 @@ internal abstract class MangaFireParser(
} }
} }
private suspend fun getChaptersBranch(mangaId: String, branch: ChapterBranch): List<MangaChapter> { private suspend fun getChaptersBranch(mangaId: String, branch: ChapterBranch): List<MangaChapter> {
val readVrfInput = "$mangaId@${branch.type}@${branch.langCode}" val chapterElements = webClient
val readVrf = VrfGenerator.generate(readVrfInput) .httpGet("https://$domain/ajax/read/$mangaId/${branch.type}/${branch.langCode}")
.parseJson()
val response = webClient .getJSONObject("result")
.httpGet("https://$domain/ajax/read/$mangaId/${branch.type}/${branch.langCode}?vrf=$readVrf") .getString("html")
.let(Jsoup::parseBodyFragment)
val chapterElements = response.parseJson() .select("ul li a")
.getJSONObject("result")
.getString("html")
.let(Jsoup::parseBodyFragment)
.select("ul li a")
if (branch.type == "chapter") { if (branch.type == "chapter") {
val doc = webClient val doc = webClient
@ -318,32 +276,31 @@ internal abstract class MangaFireParser(
.getString("result") .getString("result")
.let(Jsoup::parseBodyFragment) .let(Jsoup::parseBodyFragment)
doc.select("ul li a").withIndex().forEach { (i, it) -> doc.select("ul li a").withIndex().forEach { (i, it) ->
val date = it.select("span").getOrNull(1)?.ownText() ?: "" val date = it.select("span")[1].ownText()
chapterElements[i].attr("upload-date", date) chapterElements[i].attr("upload-date", date)
chapterElements[i].attr("other-title", it.attr("title")) chapterElements[i].attr("other-title", it.attr("title"))
} }
} }
return chapterElements.mapChapters(reversed = true) { _, it -> return chapterElements.mapChapters(reversed = true) { _, it ->
val chapterId = it.attr("data-id") MangaChapter(
MangaChapter( id = generateUid(it.attr("href")),
id = generateUid(it.attr("href")), title = it.attr("title").ifBlank {
title = it.attr("title").ifBlank { "${branch.type.toTitleCase()} ${it.attr("data-number")}"
"${branch.type.toTitleCase()} ${it.attr("data-number")}" },
}, number = it.attr("data-number").toFloat(),
number = it.attr("data-number").toFloatOrNull() ?: -1f, volume = it.attr("other-title").let {
volume = it.attr("other-title").let { title -> volumeNumRegex.find(it)?.groupValues?.getOrNull(2)?.toInt() ?: 0
volumeNumRegex.find(title)?.groupValues?.getOrNull(2)?.toInt() ?: 0 },
}, url = "${branch.type}/${it.attr("data-id")}",
url = "$mangaId/${branch.type}/${branch.langCode}/$chapterId", scanlator = null,
scanlator = null, uploadDate = dateFormat.parseSafe(it.attr("upload-date")),
uploadDate = dateFormat.parseSafe(it.attr("upload-date")), branch = "${branch.langTitle} ${branch.type.toTitleCase()}",
branch = "${branch.langTitle} ${branch.type.toTitleCase()}", source = source,
source = source, )
) }
} }
}
private val dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH) private val dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale.ENGLISH)
private val volumeNumRegex = Regex("""vol(ume)?\s*(\d+)""", RegexOption.IGNORE_CASE) private val volumeNumRegex = Regex("""vol(ume)?\s*(\d+)""", RegexOption.IGNORE_CASE)
@ -430,15 +387,12 @@ internal abstract class MangaFireParser(
} }
} }
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val chapterId = chapter.url.substringAfterLast('/') val images = webClient
val vrf = VrfGenerator.generate("chapter@$chapterId") .httpGet("https://$domain/ajax/read/${chapter.url}")
.parseJson()
val images = webClient .getJSONObject("result")
.httpGet("https://$domain/ajax/read/chapter/$chapterId?vrf=$vrf") .getJSONArray("images")
.parseJson()
.getJSONObject("result")
.getJSONArray("images")
val pages = ArrayList<MangaPage>(images.length()) val pages = ArrayList<MangaPage>(images.length())
@ -537,186 +491,3 @@ internal abstract class MangaFireParser(
class PortugueseBR(context: MangaLoaderContext) : class PortugueseBR(context: MangaLoaderContext) :
MangaFireParser(context, MangaParserSource.MANGAFIRE_PTBR, "pt-br") MangaFireParser(context, MangaParserSource.MANGAFIRE_PTBR, "pt-br")
} }
private object VrfGenerator {
private fun atob(data: String): ByteArray = Base64.getDecoder().decode(data)
private fun btoa(data: ByteArray): String = Base64.getEncoder().encodeToString(data)
private fun rc4(key: ByteArray, input: ByteArray): ByteArray {
val s = IntArray(256) { it }
var j = 0
// KSA
for (i in 0..255) {
j = (j + s[i] + key[i % key.size].toInt().and(0xFF)) and 0xFF
val temp = s[i]
s[i] = s[j]
s[j] = temp
}
// PRGA
val output = ByteArray(input.size)
var i = 0
j = 0
for (y in input.indices) {
i = (i + 1) and 0xFF
j = (j + s[i]) and 0xFF
val temp = s[i]
s[i] = s[j]
s[j] = temp
val k = s[(s[i] + s[j]) and 0xFF]
output[y] = (input[y].toInt() xor k).toByte()
}
return output
}
private fun transform(
input: ByteArray,
initSeedBytes: ByteArray,
prefixKeyBytes: ByteArray,
prefixLen: Int,
schedule: List<(Int) -> Int>,
): ByteArray {
val out = mutableListOf<Byte>()
for (i in input.indices) {
if (i < prefixLen) {
out.add(prefixKeyBytes[i])
}
val transformed = schedule[i % 10](
(input[i].toInt() xor initSeedBytes[i % 32].toInt()) and 0xFF,
) and 0xFF
out.add(transformed.toByte())
}
return out.toByteArray()
}
private val scheduleC = listOf<(Int) -> Int>(
{ c -> (c - 48 + 256) and 0xFF },
{ c -> (c - 19 + 256) and 0xFF },
{ c -> (c xor 241) and 0xFF },
{ c -> (c - 19 + 256) and 0xFF },
{ c -> (c + 223) and 0xFF },
{ c -> (c - 19 + 256) and 0xFF },
{ c -> (c - 170 + 256) and 0xFF },
{ c -> (c - 19 + 256) and 0xFF },
{ c -> (c - 48 + 256) and 0xFF },
{ c -> (c xor 8) and 0xFF },
)
private val scheduleY = listOf<(Int) -> Int>(
{ c -> ((c shl 4) or (c ushr 4)) and 0xFF },
{ c -> (c + 223) and 0xFF },
{ c -> ((c shl 4) or (c ushr 4)) and 0xFF },
{ c -> (c xor 163) and 0xFF },
{ c -> (c - 48 + 256) and 0xFF },
{ c -> (c + 82) and 0xFF },
{ c -> (c + 223) and 0xFF },
{ c -> (c - 48 + 256) and 0xFF },
{ c -> (c xor 83) and 0xFF },
{ c -> ((c shl 4) or (c ushr 4)) and 0xFF },
)
private val scheduleB = listOf<(Int) -> Int>(
{ c -> (c - 19 + 256) and 0xFF },
{ c -> (c + 82) and 0xFF },
{ c -> (c - 48 + 256) and 0xFF },
{ c -> (c - 170 + 256) and 0xFF },
{ c -> ((c shl 4) or (c ushr 4)) and 0xFF },
{ c -> (c - 48 + 256) and 0xFF },
{ c -> (c - 170 + 256) and 0xFF },
{ c -> (c xor 8) and 0xFF },
{ c -> (c + 82) and 0xFF },
{ c -> (c xor 163) and 0xFF },
)
private val scheduleJ = listOf<(Int) -> Int>(
{ c -> (c + 223) and 0xFF },
{ c -> ((c shl 4) or (c ushr 4)) and 0xFF },
{ c -> (c + 223) and 0xFF },
{ c -> (c xor 83) and 0xFF },
{ c -> (c - 19 + 256) and 0xFF },
{ c -> (c + 223) and 0xFF },
{ c -> (c - 170 + 256) and 0xFF },
{ c -> (c + 223) and 0xFF },
{ c -> (c - 170 + 256) and 0xFF },
{ c -> (c xor 83) and 0xFF },
)
private val scheduleE = listOf<(Int) -> Int>(
{ c -> (c + 82) and 0xFF },
{ c -> (c xor 83) and 0xFF },
{ c -> (c xor 163) and 0xFF },
{ c -> (c + 82) and 0xFF },
{ c -> (c - 170 + 256) and 0xFF },
{ c -> (c xor 8) and 0xFF },
{ c -> (c xor 241) and 0xFF },
{ c -> (c + 82) and 0xFF },
{ c -> (c + 176) and 0xFF },
{ c -> ((c shl 4) or (c ushr 4)) and 0xFF },
)
private val rc4Keys = mapOf(
"l" to "u8cBwTi1CM4XE3BkwG5Ble3AxWgnhKiXD9Cr279yNW0=",
"g" to "t00NOJ/Fl3wZtez1xU6/YvcWDoXzjrDHJLL2r/IWgcY=",
"B" to "S7I+968ZY4Fo3sLVNH/ExCNq7gjuOHjSRgSqh6SsPJc=",
"m" to "7D4Q8i8dApRj6UWxXbIBEa1UqvjI+8W0UvPH9talJK8=",
"F" to "0JsmfWZA1kwZeWLk5gfV5g41lwLL72wHbam5ZPfnOVE=",
)
private val seeds32 = mapOf(
"A" to "pGjzSCtS4izckNAOhrY5unJnO2E1VbrU+tXRYG24vTo=",
"V" to "dFcKX9Qpu7mt/AD6mb1QF4w+KqHTKmdiqp7penubAKI=",
"N" to "owp1QIY/kBiRWrRn9TLN2CdZsLeejzHhfJwdiQMjg3w=",
"P" to "H1XbRvXOvZAhyyPaO68vgIUgdAHn68Y6mrwkpIpEue8=",
"k" to "2Nmobf/mpQ7+Dxq1/olPSDj3xV8PZkPbKaucJvVckL0=",
)
private val prefixKeys = mapOf(
"O" to "Rowe+rg/0g==",
"v" to "8cULcnOMJVY8AA==",
"L" to "n2+Og2Gth8Hh",
"p" to "aRpvzH+yoA==",
"W" to "ZB4oBi0=",
)
fun generate(input: String): String {
var bytes = input.toByteArray()
// RC4 1
bytes = rc4(atob(rc4Keys["l"]!!), bytes)
// Step C1
bytes = transform(bytes, atob(seeds32["A"]!!), atob(prefixKeys["O"]!!), 7, scheduleC)
// RC4 2
bytes = rc4(atob(rc4Keys["g"]!!), bytes)
// Step Y
bytes = transform(bytes, atob(seeds32["V"]!!), atob(prefixKeys["v"]!!), 10, scheduleY)
// RC4 3
bytes = rc4(atob(rc4Keys["B"]!!), bytes)
// Step B
bytes = transform(bytes, atob(seeds32["N"]!!), atob(prefixKeys["L"]!!), 9, scheduleB)
// RC4 4
bytes = rc4(atob(rc4Keys["m"]!!), bytes)
// Step J
bytes = transform(bytes, atob(seeds32["P"]!!), atob(prefixKeys["p"]!!), 7, scheduleJ)
// RC4 5
bytes = rc4(atob(rc4Keys["F"]!!), bytes)
// Step E
bytes = transform(bytes, atob(seeds32["k"]!!), atob(prefixKeys["W"]!!), 5, scheduleE)
// Base64URL encode
return btoa(bytes)
.replace("+", "-")
.replace("/", "_")
.replace("=", "")
}
}

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.all package org.koitharu.kotatsu.parsers.site.all
import okhttp3.Headers
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
@ -23,15 +22,13 @@ import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl import org.koitharu.kotatsu.parsers.util.attrAsRelativeUrl
import org.koitharu.kotatsu.parsers.util.generateUid import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.mapToSet import org.koitharu.kotatsu.parsers.util.mapToSet
import org.koitharu.kotatsu.parsers.util.oneOrThrowIfMany
import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.splitByWhitespace
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.util.urlEncoded
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.EnumSet import java.util.EnumSet
import java.util.Locale import java.util.Locale
import java.util.regex.Pattern import java.util.regex.Pattern
import kotlin.collections.joinToString
@MangaSourceParser("MYREADINGMANGA", "MyReadingManga", type = ContentType.HENTAI) @MangaSourceParser("MYREADINGMANGA", "MyReadingManga", type = ContentType.HENTAI)
internal class MyReadingManga(context: MangaLoaderContext) : internal class MyReadingManga(context: MangaLoaderContext) :
@ -39,22 +36,19 @@ internal class MyReadingManga(context: MangaLoaderContext) :
override val configKeyDomain = ConfigKey.Domain("myreadingmanga.info") override val configKeyDomain = ConfigKey.Domain("myreadingmanga.info")
override fun getRequestHeaders(): Headers = Headers.Builder() override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
.add("User-Agent", "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Mobile Safari/537.36") super.onCreateConfig(keys)
.add("X-Requested-With", randomString((1..20).random())) keys.add(userAgentKey)
.build() }
override val filterCapabilities: MangaListFilterCapabilities override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities( get() = MangaListFilterCapabilities(
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true, isOriginalLocaleSupported = true,
isMultipleTagsSupported = true,
) )
override val availableSortOrders: Set<SortOrder> = EnumSet.of( override val availableSortOrders: Set<SortOrder> = EnumSet.of(
SortOrder.RELEVANCE, SortOrder.UPDATED,
SortOrder.NEWEST,
SortOrder.NEWEST_ASC,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -62,8 +56,6 @@ internal class MyReadingManga(context: MangaLoaderContext) :
availableStates = EnumSet.of( availableStates = EnumSet.of(
MangaState.ONGOING, MangaState.ONGOING,
MangaState.FINISHED, MangaState.FINISHED,
MangaState.PAUSED,
MangaState.ABANDONED,
), ),
availableContentRating = EnumSet.of(ContentRating.ADULT), availableContentRating = EnumSet.of(ContentRating.ADULT),
availableLocales = setOf( availableLocales = setOf(
@ -108,8 +100,6 @@ internal class MyReadingManga(context: MangaLoaderContext) :
private fun getLanguageSlug(locale: Locale?): String? { private fun getLanguageSlug(locale: Locale?): String? {
return when { return when {
locale?.language == "en" -> "english"
locale?.language == "ja" -> "japanese"
locale?.language == "fr" -> "french" locale?.language == "fr" -> "french"
locale?.language == "ja" -> "jp" locale?.language == "ja" -> "jp"
locale?.language == "zh" && locale.country == "TW" -> "traditional-chinese" locale?.language == "zh" && locale.country == "TW" -> "traditional-chinese"
@ -149,56 +139,75 @@ internal class MyReadingManga(context: MangaLoaderContext) :
} }
} }
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = buildString { val url = buildString {
append("https://") append("https://")
append(domain) append(domain)
if (page > 1) { // Add language path if specified
append("/page/") val langSlug = getLanguageSlug(filter.locale)
append(page) if (langSlug != null) {
} append("/lang/")
append(langSlug)
// order }
append("/?ep_sort=")
when (order) { when {
SortOrder.NEWEST -> append("date") !filter.query.isNullOrEmpty() -> {
SortOrder.NEWEST_ASC -> append("date_asc") // Search with language: /lang/french/page/2/?s=example
else -> append("") if (page > 1) {
} append("/page/")
append(page)
}
// fix order append("/?s=")
append("&s=") append(filter.query.urlEncoded())
if (!filter.query.isNullOrEmpty()) { }
append(filter.query.splitByWhitespace().joinToString(separator = "+"))
} filter.tags.isNotEmpty() -> {
// Genre filtering doesn't work with language, so we ignore language for genre
val langSlug = getLanguageSlug(filter.locale) if (langSlug == null) {
if (langSlug != null) { append("/genre/")
append("&ep_filter_lang=") append(filter.tags.first().key)
append(langSlug) append("/page/")
} append(page)
append("/")
if (filter.states.isNotEmpty()) { } else {
append("&ep_filter_status=") // If both language and genre are selected, just use language
filter.states.oneOrThrowIfMany()?.let { append("/page/")
append( append(page)
when (it) { append("/")
MangaState.ONGOING -> "ongoing" }
MangaState.FINISHED -> "completed" }
MangaState.PAUSED -> "hiatus"
MangaState.ABANDONED -> "dropped" filter.states.isNotEmpty() -> {
else -> "" // Status filtering doesn't work with language either
} if (langSlug == null) {
) append("/status/")
} append(
} when (filter.states.first()) {
MangaState.ONGOING -> "ongoing"
if (filter.tags.isNotEmpty()) { MangaState.FINISHED -> "completed"
append("&ep_filter_genre=") else -> "ongoing"
append(filter.tags.joinToString(",") { it.key }) },
} )
append("/page/")
append(page)
append("/")
} else {
// If both language and status are selected, just use language
append("/page/")
append(page)
append("/")
}
}
else -> {
// Regular browsing with or without language
append("/page/")
append(page)
append("/")
}
}
} }
val doc = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
@ -278,10 +287,10 @@ internal class MyReadingManga(context: MangaLoaderContext) :
val genres = mutableSetOf<MangaTag>() val genres = mutableSetOf<MangaTag>()
doc.select("span.entry-terms:has(span:contains(Genre:)) a").forEach { doc.select("span.entry-terms:has(span:contains(Genres)) a").forEach {
genres.add( genres.add(
MangaTag( MangaTag(
title = it.text().removeSuffix(",").trim(), title = it.text(),
key = it.attr("href").substringAfterLast("/genre/").substringBefore("/"), key = it.attr("href").substringAfterLast("/genre/").substringBefore("/"),
source = source, source = source,
), ),
@ -291,8 +300,6 @@ internal class MyReadingManga(context: MangaLoaderContext) :
val state = when (doc.select("a[href*=status]").firstOrNull()?.text()) { val state = when (doc.select("a[href*=status]").firstOrNull()?.text()) {
"Ongoing" -> MangaState.ONGOING "Ongoing" -> MangaState.ONGOING
"Completed" -> MangaState.FINISHED "Completed" -> MangaState.FINISHED
"Hiatus" -> MangaState.PAUSED
"Dropped" -> MangaState.ABANDONED
else -> null else -> null
} }
@ -440,10 +447,5 @@ internal class MyReadingManga(context: MangaLoaderContext) :
0L 0L
} }
} }
private fun randomString(length: Int): String {
val charPool = ('a'..'z') + ('A'..'Z')
return List(length) { charPool.random() }.joinToString("")
}
} }

@ -21,10 +21,6 @@ internal class BatCave(context: MangaLoaderContext) :
override val configKeyDomain = ConfigKey.Domain("batcave.biz") override val configKeyDomain = ConfigKey.Domain("batcave.biz")
override fun getRequestHeaders() = super.getRequestHeaders().newBuilder()
.add("Referer", "https://$domain/")
.build()
private val availableTags = suspendLazy(initializer = ::fetchTags) private val availableTags = suspendLazy(initializer = ::fetchTags)
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) { override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {

@ -11,7 +11,7 @@ import org.koitharu.kotatsu.parsers.Broken
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@Broken("Original site closed") @Broken // The website is broken, it seems to be closed already.
@MangaSourceParser("COMICEXTRA", "ComicExtra", "en", ContentType.COMICS) @MangaSourceParser("COMICEXTRA", "ComicExtra", "en", ContentType.COMICS)
internal class ComicExtra(context: MangaLoaderContext) : internal class ComicExtra(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.COMICEXTRA, 36) { PagedMangaParser(context, MangaParserSource.COMICEXTRA, 36) {

@ -1,10 +1,15 @@
package org.koitharu.kotatsu.parsers.site.en.mtl package org.koitharu.kotatsu.parsers.site.en.MTL
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQueryCapabilities
import org.koitharu.kotatsu.parsers.model.search.SearchCapability
import org.koitharu.kotatsu.parsers.model.search.SearchableField
import org.koitharu.kotatsu.parsers.model.search.QueryCriteria.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import org.koitharu.kotatsu.parsers.core.FlexiblePagedMangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.PagedMangaParser
import org.koitharu.kotatsu.parsers.exception.ParseException import org.koitharu.kotatsu.parsers.exception.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -13,7 +18,7 @@ internal abstract class MTLParser(
context: MangaLoaderContext, context: MangaLoaderContext,
source: MangaParserSource, source: MangaParserSource,
domain: String, domain: String,
) : PagedMangaParser(context, source, 24) { ) : FlexiblePagedMangaParser(context, source, 24) {
override val configKeyDomain = ConfigKey.Domain(domain) override val configKeyDomain = ConfigKey.Domain(domain)
@ -25,18 +30,21 @@ internal abstract class MTLParser(
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions() override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions()
override val filterCapabilities: MangaListFilterCapabilities override val searchQueryCapabilities = MangaSearchQueryCapabilities(
get() = MangaListFilterCapabilities( SearchCapability(
isSearchSupported = true, field = SearchableField.TITLE_NAME,
) criteriaTypes = setOf(Match::class),
isMultiple = false,
),
)
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(query: MangaSearchQuery, page: Int): List<Manga> {
val url = buildString { val url = buildString {
append("https://") append("https://")
append(domain) append(domain)
append("/search") append("/search")
append("?") append("?")
when (order) { when (query.order) {
SortOrder.POPULARITY -> append("sort_by=views") SortOrder.POPULARITY -> append("sort_by=views")
SortOrder.UPDATED -> append("sort_by=recent") SortOrder.UPDATED -> append("sort_by=recent")
else -> append("sort_by=recent") else -> append("sort_by=recent")
@ -45,10 +53,19 @@ internal abstract class MTLParser(
append("&page=") append("&page=")
append(page) append(page)
} }
append("&q=") query.criteria.find { it.field == SearchableField.TITLE_NAME }?.let { criteria ->
if (!filter.query.isNullOrEmpty()) { when (criteria) {
append(filter.query.urlEncoded()) is Match -> {
} append("&q=")
append(criteria.value.toString())
}
is Include,
is Exclude,
is Range,
-> Unit // Not supported for this field
}
}
} }
val doc = webClient.httpGet(url).parseHtml() val doc = webClient.httpGet(url).parseHtml()
@ -173,7 +190,7 @@ internal abstract class MTLParser(
val sdf = SimpleDateFormat("dd MMMM yyyy", Locale.ENGLISH) val sdf = SimpleDateFormat("dd MMMM yyyy", Locale.ENGLISH)
sdf.timeZone = TimeZone.getTimeZone("UTC") sdf.timeZone = TimeZone.getTimeZone("UTC")
sdf.parse(dateString)?.time ?: 0L sdf.parse(dateString)?.time ?: 0L
} catch (_: Exception) { } catch (e: Exception) {
0L 0L
} }
} }

@ -1,12 +1,11 @@
package org.koitharu.kotatsu.parsers.site.en.mtl package org.koitharu.kotatsu.parsers.site.en.MTL
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.site.en.MTL.MTLParser
@Broken
@MangaSourceParser("SNOWMTL", "SnowMTL", "en", type = ContentType.OTHER) @MangaSourceParser("SNOWMTL", "SnowMTL", "en", type = ContentType.OTHER)
internal class SnowMTL(context: MangaLoaderContext): internal class SnowMTL(context: MangaLoaderContext):
MTLParser(context, source = MangaParserSource.SNOWMTL, "snowmtl.ru") MTLParser(context, source = MangaParserSource.SNOWMTL, "snowmtl.ru")

@ -1,12 +1,11 @@
package org.koitharu.kotatsu.parsers.site.en.mtl package org.koitharu.kotatsu.parsers.site.en.MTL
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.site.en.MTL.MTLParser
@Broken
@MangaSourceParser("SOLARMTL", "SolarMTL", "en", type = ContentType.MANGA) @MangaSourceParser("SOLARMTL", "SolarMTL", "en", type = ContentType.MANGA)
internal class SolarMTL(context: MangaLoaderContext): internal class SolarMTL(context: MangaLoaderContext):
MTLParser(context, source = MangaParserSource.SOLARMTL, "solarmtl.com") MTLParser(context, source = MangaParserSource.SOLARMTL, "solarmtl.com")

@ -156,21 +156,18 @@ internal class MangaGeko(context: MangaLoaderContext) :
} }
} }
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain) val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
return doc.select("center img")
.mapNotNull { it.attr("src").takeIf { src -> src.isNotBlank() } } return doc.requireElementById("chapter-reader").select("img").map { img ->
// remove all invaild images + credits val url = img.requireSrc().toRelativeUrl(domain)
.filterNot { it.startsWith("data:image") || it.contains("credits-mgeko.png") } MangaPage(
.distinct().map { url -> id = generateUid(url),
val finalUrl = url.toRelativeUrl(domain) url = url,
MangaPage( preview = null,
id = generateUid(finalUrl), source = source,
url = finalUrl, )
preview = null, }
source = source, }
)
}
}
} }

@ -4,8 +4,15 @@ import org.json.JSONObject
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.PagedMangaParser import org.koitharu.kotatsu.parsers.core.FlexiblePagedMangaParser
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQuery
import org.koitharu.kotatsu.parsers.model.search.MangaSearchQueryCapabilities
import org.koitharu.kotatsu.parsers.model.search.SearchCapability
import org.koitharu.kotatsu.parsers.model.search.QueryCriteria
import org.koitharu.kotatsu.parsers.model.search.QueryCriteria.*
import org.koitharu.kotatsu.parsers.model.search.SearchableField
import org.koitharu.kotatsu.parsers.model.search.SearchableField.*
import org.koitharu.kotatsu.parsers.util.generateUid import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.parseHtml import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow import org.koitharu.kotatsu.parsers.util.selectFirstOrThrow
@ -16,12 +23,7 @@ import java.util.Locale
@MangaSourceParser("VIOLETSCANS", "VioletScans", "en") @MangaSourceParser("VIOLETSCANS", "VioletScans", "en")
internal class VioletScans(context: MangaLoaderContext) : internal class VioletScans(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.VIOLETSCANS, 12) { FlexiblePagedMangaParser(context, MangaParserSource.VIOLETSCANS, 12) {
override val filterCapabilities: MangaListFilterCapabilities
get() = MangaListFilterCapabilities(
isSearchSupported = true,
)
override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("violetscans.com") override val configKeyDomain: ConfigKey.Domain = ConfigKey.Domain("violetscans.com")
@ -32,14 +34,37 @@ internal class VioletScans(context: MangaLoaderContext) :
override val availableSortOrders: Set<SortOrder> = setOf(SortOrder.NEWEST) override val availableSortOrders: Set<SortOrder> = setOf(SortOrder.NEWEST)
override val searchQueryCapabilities: MangaSearchQueryCapabilities
get() = MangaSearchQueryCapabilities(
SearchCapability(
field = TITLE_NAME,
criteriaTypes = setOf(Match::class),
isMultiple = false,
),
)
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions() override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions()
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(query: MangaSearchQuery, page: Int): List<Manga> {
return if (!filter.query.isNullOrEmpty()) { var searchParameter = ""
scrapeSearchList(filter.query, page) query.criteria.forEach { criterion ->
} else { when (criterion) {
scrapeNonSearchList(page) is QueryCriteria.Match<*> -> {
} if (criterion.field == SearchableField.TITLE_NAME) {
searchParameter = criterion.value.toString()
}
}
is QueryCriteria.Exclude<*> -> null
is QueryCriteria.Range<*> -> null
is QueryCriteria.Include<*> -> null
}
}
// scrapeNonSearchList has considerable less payload as response so this is a optimization
return when {
!searchParameter.isNullOrEmpty() -> scrapeSearchList(searchParameter, page)
else -> scrapeNonSearchList(page)
}
} }
private suspend fun scrapeNonSearchList(page: Int): List<Manga> { private suspend fun scrapeNonSearchList(page: Int): List<Manga> {
@ -174,7 +199,7 @@ internal class VioletScans(context: MangaLoaderContext) :
} }
val dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH) val dateFormat = SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH)
val date = dateFormat.parseSafe(dateString) val date = dateFormat.parseSafe(dateString) ?: 0L
val chaptersList = root.selectFirstOrThrow("#chapterlist ul") val chaptersList = root.selectFirstOrThrow("#chapterlist ul")
val chapters = chaptersList.select("li") val chapters = chaptersList.select("li")

@ -9,7 +9,7 @@ import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.util.* import java.util.*
@Broken("Redirect to DragonTranslation.org source") @Broken // Website closed
@MangaSourceParser("DRAGONTRANSLATION", "Dragon Translation", "es") @MangaSourceParser("DRAGONTRANSLATION", "Dragon Translation", "es")
internal class DragonTranslationParser(context: MangaLoaderContext) : internal class DragonTranslationParser(context: MangaLoaderContext) :
PagedMangaParser(context, MangaParserSource.DRAGONTRANSLATION, 30) { PagedMangaParser(context, MangaParserSource.DRAGONTRANSLATION, 30) {

@ -1,230 +0,0 @@
package org.koitharu.kotatsu.parsers.site.fr
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities
import org.koitharu.kotatsu.parsers.model.MangaListFilterOptions
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
import org.koitharu.kotatsu.parsers.model.SortOrder
import org.koitharu.kotatsu.parsers.util.generateUid
import org.koitharu.kotatsu.parsers.util.parseHtml
import org.koitharu.kotatsu.parsers.util.toAbsoluteUrl
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.SinglePageMangaParser
import java.util.EnumSet
import java.util.Locale
@Broken
@MangaSourceParser("MANGAMOINS", "MangaMoins", "fr")
internal class MangaMoins(context: MangaLoaderContext) :
SinglePageMangaParser(context, MangaParserSource.MANGAMOINS) {
override val configKeyDomain = ConfigKey.Domain("mangamoins.com")
override val availableSortOrders: Set<SortOrder> = EnumSet.of(SortOrder.UPDATED)
override val filterCapabilities = MangaListFilterCapabilities()
override suspend fun getFilterOptions(): MangaListFilterOptions = MangaListFilterOptions()
override suspend fun getList(order: SortOrder, filter: MangaListFilter): List<Manga> {
return listOf(
Manga(
id = generateUid("OP"),
title = "One Piece",
altTitles = emptySet(),
url = "OP",
publicUrl = "https://mangamoins.com/",
rating = RATING_UNKNOWN,
contentRating = null,
coverUrl = "https://mangamoins.com/files/scans/OP1161/thumbnail.png",
tags = emptySet(),
state = MangaState.ONGOING,
authors = setOf("Eiichiro Oda"),
source = source,
),
Manga(
id = generateUid("LCDL"),
title = "Les Carnets de l'Apothicaire",
altTitles = emptySet(),
url = "LCDL",
publicUrl = "https://mangamoins.com/",
rating = RATING_UNKNOWN,
contentRating = null,
coverUrl = "https://mangamoins.com/files/scans/LCDL76.2/thumbnail.png",
tags = emptySet(),
state = MangaState.ONGOING,
authors = setOf("Itsuki Nanao", "Nekokurage"),
source = source,
),
Manga(
id = generateUid("JKM"),
title = "Jujutsu Kaisen Modulo",
altTitles = emptySet(),
url = "JKM",
publicUrl = "https://mangamoins.com/",
rating = RATING_UNKNOWN,
contentRating = null,
coverUrl = "https://mangamoins.com/files/scans/JKM1/thumbnail.png",
tags = emptySet(),
state = MangaState.ONGOING,
authors = setOf("Gege Akutami", "Yuji Iwasaki"),
source = source,
),
Manga(
id = generateUid("OPC"),
title = "One Piece Colo",
altTitles = emptySet(),
url = "OPC",
publicUrl = "https://mangamoins.com/",
rating = RATING_UNKNOWN,
contentRating = null,
coverUrl = "https://mangamoins.com/files/scans/OPC1160/thumbnail.png",
tags = emptySet(),
state = MangaState.ONGOING,
authors = setOf("Eiichiro Oda"),
source = source,
),
Manga(
id = generateUid("LDS"),
title = "L'Atelier des Sorciers",
altTitles = emptySet(),
url = "LDS",
publicUrl = "https://mangamoins.com/",
rating = RATING_UNKNOWN,
contentRating = null,
coverUrl = "https://sceneario.com/wp-content/uploads/2023/05/9782811641344-1.jpg",
tags = emptySet(),
state = MangaState.ONGOING,
authors = setOf("Kamome Shirahama"),
source = source,
)
)
}
override suspend fun getDetails(manga: Manga): Manga {
val doc = webClient.httpGet("https://$domain").parseHtml()
val prefix = manga.url
val latestSiteChapterNumber = doc.select("div.sortie a").mapNotNull { a ->
val href = a.attr("href")
if (href.startsWith("?scan=$prefix")) {
href.substringAfter(prefix).toFloatOrNull()
} else {
null
}
}.maxOrNull()
if (latestSiteChapterNumber == null) return manga
val cachedChapters = manga.chapters
val lastKnownChapterNumber = cachedChapters?.maxByOrNull { it.number }?.number
if (lastKnownChapterNumber != null && cachedChapters.isNotEmpty()) {
// INCREMENTAL SCAN (CACHE EXISTS)
if (latestSiteChapterNumber <= lastKnownChapterNumber) {
return manga.copy(chapters = cachedChapters.sortedBy { it.number })
}
val newChapters = mutableListOf<MangaChapter>()
var currentChapterInt = latestSiteChapterNumber.toInt()
while (currentChapterInt > lastKnownChapterNumber.toInt()) {
val chaptersForGroup = doChecks(prefix, currentChapterInt)
newChapters.addAll(chaptersForGroup)
currentChapterInt--
}
val combinedChapters = (cachedChapters + newChapters).distinctBy { it.number }
return manga.copy(chapters = combinedChapters.sortedBy { it.number })
} else {
// FULL SCAN (NO CACHE)
val allChapters = mutableListOf<MangaChapter>()
var currentChapterInt = latestSiteChapterNumber.toInt()
var misses = 0
while (currentChapterInt >= 1 && misses < 4) {
val chaptersForGroup = doChecks(prefix, currentChapterInt)
if (chaptersForGroup.isNotEmpty()) {
misses = 0
allChapters.addAll(chaptersForGroup)
} else {
misses++
}
currentChapterInt--
}
return manga.copy(chapters = allChapters.sortedBy { it.number })
}
}
private suspend fun doChecks(prefix: String, chapterInt: Int): List<MangaChapter> {
val foundChapters = mutableListOf<MangaChapter>()
// Check for integer chapter
val integerChapter = checkChapter(prefix, chapterInt.toString(), chapterInt.toFloat())
if (integerChapter != null) {
foundChapters.add(integerChapter)
}
// Conditionally and sequentially check for decimal chapters
if (prefix == "LCDL") {
// Start from 2 because .1 never exists
for (i in 2..9) {
val decimalNum = chapterInt + (i / 10.0f)
val decimalNumStr = String.format(Locale.US, "%.1f", decimalNum)
val decimalChapter = checkChapter(prefix, decimalNumStr, decimalNum)
if (decimalChapter != null) {
foundChapters.add(decimalChapter)
} else {
break // Stop if there's a gap
}
}
}
return foundChapters
}
private suspend fun checkChapter(prefix: String, chapterNumStr: String, chapterNumFloat: Float): MangaChapter? {
val thumbUrl = "https://mangamoins.com/files/scans/$prefix$chapterNumStr/thumbnail.png"
return try {
val response = webClient.httpHead(thumbUrl)
if (response.isSuccessful) {
response.close()
val chapterUrl = "https://mangamoins.com/?scan=$prefix$chapterNumStr"
MangaChapter(
id = generateUid(chapterUrl),
title = null,
number = chapterNumFloat,
volume = 0,
url = chapterUrl,
scanlator = null,
uploadDate = 0,
branch = null,
source = source
)
} else {
response.close()
null
}
} catch (_: Exception) {
null
}
}
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val doc = webClient.httpGet(chapter.url).parseHtml()
return doc.select("link[rel=preload][as=image]").map { element ->
val url = element.attr("href").toAbsoluteUrl(domain)
MangaPage(
id = generateUid(url),
url = url,
preview = null,
source = source
)
}
}
}

@ -6,12 +6,12 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.heancms.HeanCms import org.koitharu.kotatsu.parsers.site.heancms.HeanCms
@Broken("Not dead, changed template") @Broken // Not dead, changed template
@MangaSourceParser("TEMPLESCAN", "TempleScan", "en") @MangaSourceParser("TEMPLESCAN", "TempleScan", "en")
internal class TempleScan(context: MangaLoaderContext) : internal class TempleScan(context: MangaLoaderContext) :
HeanCms(context, MangaParserSource.TEMPLESCAN, "templetoons.com") { HeanCms(context, MangaParserSource.TEMPLESCAN, "templetoons.com") {
override val pathManga = "comic" override val pathManga = "comic"
override val apiPath: String override val apiPath: String
get() = "$domain/api" get() = "$domain/apiv1"
} }

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.heancms.HeanCms import org.koitharu.kotatsu.parsers.site.heancms.HeanCms
@Broken("Not dead, changed template") @Broken // Not dead but changed template
@MangaSourceParser("YUGEN_MANGAS_ES", "YugenMangas.lat", "es", ContentType.HENTAI) @MangaSourceParser("YUGEN_MANGAS_ES", "YugenMangas.lat", "es", ContentType.HENTAI)
internal class YugenMangasEs(context: MangaLoaderContext) : internal class YugenMangasEs(context: MangaLoaderContext) :
HeanCms(context, MangaParserSource.YUGEN_MANGAS_ES, "lectorikigai.acamu.net") HeanCms(context, MangaParserSource.YUGEN_MANGAS_ES, "lectorikigai.acamu.net")

@ -8,9 +8,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.heancmsalt.HeanCmsAlt import org.koitharu.kotatsu.parsers.site.heancmsalt.HeanCmsAlt
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("BRAKEOUT", "Brakeout", "es") @MangaSourceParser("BRAKEOUT", "Brakeout", "es")
internal class Brakeout(context: MangaLoaderContext) : internal class Brakeout(context: MangaLoaderContext) :
HeanCmsAlt(context, MangaParserSource.BRAKEOUT, "brakeout.xyz", 10) { HeanCmsAlt(context, MangaParserSource.BRAKEOUT, "brakeout.xyz", 10) {

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.heancmsalt.HeanCmsAlt import org.koitharu.kotatsu.parsers.site.heancmsalt.HeanCmsAlt
import org.koitharu.kotatsu.parsers.Broken
@Broken("Not dead, changed template")
@MangaSourceParser("LEGIONSCANS", "CerberusSeries", "es") @MangaSourceParser("LEGIONSCANS", "CerberusSeries", "es")
internal class CerberuSeries(context: MangaLoaderContext) : internal class CerberuSeries(context: MangaLoaderContext) :
HeanCmsAlt(context, MangaParserSource.LEGIONSCANS, "legionscans.com") HeanCmsAlt(context, MangaParserSource.LEGIONSCANS, "cerberuseries.xyz")

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.heancmsalt.HeanCmsAlt import org.koitharu.kotatsu.parsers.site.heancmsalt.HeanCmsAlt
@Broken("Not dead, changed template") @Broken // Not dead but changed template
@MangaSourceParser("MANGAESP", "MangaEsp", "es") @MangaSourceParser("MANGAESP", "MangaEsp", "es")
internal class MangaEsp(context: MangaLoaderContext) : internal class MangaEsp(context: MangaLoaderContext) :
HeanCmsAlt(context, MangaParserSource.MANGAESP, "mangaesp.topmanhuas.org", 15) { HeanCmsAlt(context, MangaParserSource.MANGAESP, "mangaesp.topmanhuas.org", 15) {

@ -30,7 +30,6 @@ internal class DoujinDesuParser(context: MangaLoaderContext) :
isMultipleTagsSupported = true, isMultipleTagsSupported = true,
isSearchSupported = true, isSearchSupported = true,
isSearchWithFiltersSupported = true, isSearchWithFiltersSupported = true,
isAuthorSearchSupported = true,
) )
override suspend fun getFilterOptions() = MangaListFilterOptions( override suspend fun getFilterOptions() = MangaListFilterOptions(
@ -50,25 +49,17 @@ internal class DoujinDesuParser(context: MangaLoaderContext) :
override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { override suspend fun getListPage(page: Int, order: SortOrder, filter: MangaListFilter): List<Manga> {
val url = urlBuilder().apply { val url = urlBuilder().apply {
when { addPathSegment("manga")
page > 1 -> addPathSegments("manga/page/$page/") addPathSegment("page")
else -> addPathSegment("manga/") addPathSegment("$page/")
}
addQueryParameter( addQueryParameter(
"title", "title",
filter.query?.let { filter.query?.let {
filter.query filter.query
}, },
) )
addQueryParameter(
name = "author",
value = filter.author?.let { it
space2plus(it).lowercase()
}
)
addQueryParameter( addQueryParameter(
"order", "order",
when (order) { when (order) {
@ -106,6 +97,14 @@ internal class DoujinDesuParser(context: MangaLoaderContext) :
}, },
) )
} }
// Author
// addQueryParameter("author",
// filter.author?.let {
// filter.author
// }
// )
}.build() }.build()
return webClient.httpGet(url).parseHtml() return webClient.httpGet(url).parseHtml()
@ -205,6 +204,4 @@ internal class DoujinDesuParser(context: MangaLoaderContext) :
) )
} }
} }
private fun space2plus(input: String): String = input.replace(' ', '+')
} }

@ -192,8 +192,8 @@ internal abstract class IkenParser(
val key = it.attrOrNull("value") ?: return@mapNotNullToSet null val key = it.attrOrNull("value") ?: return@mapNotNullToSet null
MangaTag( MangaTag(
key = key, key = key,
title = it.text().ifBlank { key }.toTitleCase(sourceLocale), title = (it.text() ?: key).toTitleCase(sourceLocale),
source = source, source = source,
) )
} }
} }
@ -201,7 +201,7 @@ internal abstract class IkenParser(
protected fun Document.getNextJson(key: String): String { protected fun Document.getNextJson(key: String): String {
val scripts = select("script") val scripts = select("script")
val scriptData = scripts.find { script -> val scriptData = scripts.find { script ->
script.data().contains(key) script.data()?.contains(key) == true
}?.data() ?: throw Exception("Unable to retrieve NEXT data") }?.data() ?: throw Exception("Unable to retrieve NEXT data")
val keyIndex = scriptData.indexOf(key) val keyIndex = scriptData.indexOf(key)

@ -4,7 +4,9 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.iken.IkenParser import org.koitharu.kotatsu.parsers.site.iken.IkenParser
import org.koitharu.kotatsu.parsers.Broken
@Broken("Need to fix getPages")
@MangaSourceParser("HIVECOMIC", "HiveComic", "en") @MangaSourceParser("HIVECOMIC", "HiveComic", "en")
internal class HiveComic(context: MangaLoaderContext) : internal class HiveComic(context: MangaLoaderContext) :
IkenParser(context, MangaParserSource.HIVECOMIC, "hivetoons.org", 18, true) IkenParser(context, MangaParserSource.HIVECOMIC, "hivecomic.com", 18)

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.iken.IkenParser import org.koitharu.kotatsu.parsers.site.iken.IkenParser
@Broken("Redirect to VortexScans") @Broken // Redirect to @VORTEXSCANS
@MangaSourceParser("MANGAGALAXY", "MangaGalaxy", "en") @MangaSourceParser("MANGAGALAXY", "MangaGalaxy", "en")
internal class MangaGalaxyParser(context: MangaLoaderContext) : internal class MangaGalaxyParser(context: MangaLoaderContext) :
IkenParser(context, MangaParserSource.MANGAGALAXY, "vortexscans.org", 18) IkenParser(context, MangaParserSource.MANGAGALAXY, "vortexscans.org", 18)

@ -134,7 +134,6 @@ internal abstract class MadaraParser(
"en curso", "en curso",
"ongoing", "ongoing",
"on going", "on going",
"OnGoing",
"ativo", "ativo",
"en cours", "en cours",
"en cours \uD83D\uDFE2", "en cours \uD83D\uDFE2",
@ -479,7 +478,7 @@ internal abstract class MadaraParser(
return elements.map { div -> return elements.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary") val summary = div.selectFirst(".tab-summary") ?: div.selectFirst(".item-summary")
val author = summary?.selectFirst(".mg_author, .mg_artists")?.selectFirst("a")?.ownText() val author = summary?.selectFirst(".mg_author")?.selectFirst("a")?.ownText()
Manga( Manga(
id = generateUid(href), id = generateUid(href),
url = href, url = href,
@ -550,7 +549,7 @@ internal abstract class MadaraParser(
"div.post-content_item:contains(Status), div.post-content_item:contains(Statut), " + "div.post-content_item:contains(Status), div.post-content_item:contains(Statut), " +
"div.post-content_item:contains(État), div.post-content_item:contains(حالة العمل), div.post-content_item:contains(Estado), div.post-content_item:contains(สถานะ)," + "div.post-content_item:contains(État), div.post-content_item:contains(حالة العمل), div.post-content_item:contains(Estado), div.post-content_item:contains(สถานะ)," +
"div.post-content_item:contains(Stato), div.post-content_item:contains(Durum), div.post-content_item:contains(Statüsü), div.post-content_item:contains(Статус)," + "div.post-content_item:contains(Stato), div.post-content_item:contains(Durum), div.post-content_item:contains(Statüsü), div.post-content_item:contains(Статус)," +
"div.post-content_item:contains(状态), div.post-content_item:contains(الحالة), div.post-content_item:contains(Tình trạng)" "div.post-content_item:contains(状态), div.post-content_item:contains(الحالة)"
protected open val selectAlt = protected open val selectAlt =
".post-content_item:contains(Alt) .summary-content, .post-content_item:contains(Nomes alternativos: ) .summary-content" ".post-content_item:contains(Alt) .summary-content, .post-content_item:contains(Nomes alternativos: ) .summary-content"
@ -568,7 +567,7 @@ internal abstract class MadaraParser(
val href = doc.selectFirst("head meta[property='og:url']")?.attr("content")?.toRelativeUrl(domain) ?: manga.url val href = doc.selectFirst("head meta[property='og:url']")?.attr("content")?.toRelativeUrl(domain) ?: manga.url
val testCheckAsync = doc.select(selectTestAsync) val testCheckAsync = doc.select(selectTestAsync)
val chaptersDeferred = if (testCheckAsync.isEmpty()) { val chaptersDeferred = if (testCheckAsync.isNullOrEmpty()) {
async { loadChapters(href, doc) } async { loadChapters(href, doc) }
} else { } else {
async { getChapters(manga, doc) } async { getChapters(manga, doc) }

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("COMICARAB", "ComicArab", "ar") @MangaSourceParser("COMICARAB", "ComicArab", "ar")
internal class ComicArab(context: MangaLoaderContext) : internal class ComicArab(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.COMICARAB, "comicarab.com", pageSize = 24) { MadaraParser(context, MangaParserSource.COMICARAB, "comicarab.com", pageSize = 24) {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("GATEMANGA", "GateManga", "ar") @MangaSourceParser("GATEMANGA", "GateManga", "ar")
internal class GateManga(context: MangaLoaderContext) : internal class GateManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.GATEMANGA, "gatemanga.com") { MadaraParser(context, MangaParserSource.GATEMANGA, "gatemanga.com") {

@ -9,8 +9,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
internal class FreeMangaTop(context: MangaLoaderContext) : internal class FreeMangaTop(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.FREEMANGATOP, "freemangatop.com") { MadaraParser(context, MangaParserSource.FREEMANGATOP, "freemangatop.com") {
override val datePattern = "MM/dd/yyyy" override val datePattern = "MM/dd/yyyy"
override fun getRequestHeaders() = super.getRequestHeaders().newBuilder()
.add("Referer", "https://$domain/")
.build()
} }

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @hentai20") @Broken // Redirect to @hentai20
@MangaSourceParser("HENTAI3Z", "Hentai3z", "en", ContentType.HENTAI) @MangaSourceParser("HENTAI3Z", "Hentai3z", "en", ContentType.HENTAI)
internal class Hentai3z(context: MangaLoaderContext) : internal class Hentai3z(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.HENTAI3Z, "manga18h.xyz", pageSize = 20) { MadaraParser(context, MangaParserSource.HENTAI3Z, "manga18h.xyz", pageSize = 20) {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @MortalsGroove") @Broken // Redirect to @MortalsGroove
@MangaSourceParser("IMMORTALUPDATES", "ImmortalUpdates", "en") @MangaSourceParser("IMMORTALUPDATES", "ImmortalUpdates", "en")
internal class ImmortalUpdates(context: MangaLoaderContext) : internal class ImmortalUpdates(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.IMMORTALUPDATES, "immortalupdates.com") { MadaraParser(context, MangaParserSource.IMMORTALUPDATES, "immortalupdates.com") {

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import java.util.* import java.util.*
@Broken("Redirect to @XMANHWA") @Broken // Redirect to @XMANHWA
@MangaSourceParser("INSTAMANHWA", "InstaManhwa", "en", ContentType.HENTAI) @MangaSourceParser("INSTAMANHWA", "InstaManhwa", "en", ContentType.HENTAI)
internal class InstaManhwa(context: MangaLoaderContext) : internal class InstaManhwa(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.INSTAMANHWA, "www.manhwaden.com", 15) { MadaraParser(context, MangaParserSource.INSTAMANHWA, "www.manhwaden.com", 15) {

@ -1,90 +1,15 @@
package org.koitharu.kotatsu.parsers.site.madara.en package org.koitharu.kotatsu.parsers.site.madara.en
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.Interceptor
import okhttp3.Response
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.exception.ParseException
import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.network.UserAgents
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.*
private const val F_URL = "fullUrl="
@MangaSourceParser("MADARADEX", "MadaraDex", "en", ContentType.HENTAI) @MangaSourceParser("MADARADEX", "MadaraDex", "en", ContentType.HENTAI)
internal class MadaraDex(context: MangaLoaderContext) : internal class MadaraDex(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MADARADEX, "madaradex.org") { MadaraParser(context, MangaParserSource.MADARADEX, "madaradex.org") {
override val listUrl = "title/"
init { override val tagPrefix = "genre/"
context.cookieJar.insertCookies(domain, "wpmanga-adault=1") override val postReq = true
}
override fun onCreateConfig(keys: MutableCollection<ConfigKey<*>>) {
super.onCreateConfig(keys)
keys.remove(userAgentKey)
}
override fun getRequestHeaders() = super.getRequestHeaders().newBuilder()
.set("User-Agent", UserAgents.CHROME_DESKTOP)
.build()
override val authUrl: String
get() = "https://${domain}"
override suspend fun isAuthorized(): Boolean {
return context.cookieJar.getCookies(domain).any {
it.name.contains("cm_uaid")
}
}
override val listUrl = "title/"
override val tagPrefix = "genre/"
override val postReq = true
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml()
val root = doc.body().selectFirst(selectBodyPage)
?: throw ParseException("No image found, try to log in", fullUrl)
return root.select(selectPage).flatMap { div ->
div.selectOrThrow("img").map { img ->
val fragUrl = img.requireSrc().toRelativeUrl(domain).toHttpUrl().newBuilder()
.fragment(F_URL + fullUrl)
.build()
val cleanUrl = fragUrl.newBuilder().fragment(null).build()
MangaPage(
id = generateUid(cleanUrl.toString()),
url = fragUrl.toString(),
preview = null,
source = source,
)
}
}
}
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val url = request.url
val fullUrl = url.fragment?.substringAfter(F_URL, "")
return if (!fullUrl.isNullOrEmpty()) {
copyCookies()
val cleanUrl = url.newBuilder().fragment(null).toString()
val newReq = request.newBuilder()
.header("Referer", fullUrl)
.url(cleanUrl)
.build()
chain.proceed(newReq)
} else {
super.intercept(chain)
}
}
private fun copyCookies() = context.cookieJar.copyCookies(domain, "cdn.$domain")
} }

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @hentai20") @Broken // Redirect to @hentai20
@MangaSourceParser("MANGA18H", "Manga18h", "en", ContentType.HENTAI) @MangaSourceParser("MANGA18H", "Manga18h", "en", ContentType.HENTAI)
internal class Manga18h(context: MangaLoaderContext) : internal class Manga18h(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MANGA18H, "manga18h.xyz", 20) MadaraParser(context, MangaParserSource.MANGA18H, "manga18h.xyz", 20)

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @MANGAREAD") @Broken // Redirect to @MANGAREAD
@MangaSourceParser("MANGAEFFECT", "MangaEffect", "en") @MangaSourceParser("MANGAEFFECT", "MangaEffect", "en")
internal class MangaEffect(context: MangaLoaderContext) : internal class MangaEffect(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MANGAEFFECT, "www.mangaread.org") { MadaraParser(context, MangaParserSource.MANGAEFFECT, "www.mangaread.org") {

@ -5,7 +5,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("RESETSCANS", "Resetscans", "en") @MangaSourceParser("RESETSCANS", "ReadManhua", "en")
internal class ResetScans(context: MangaLoaderContext) : internal class ResetScans(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.RESETSCANS, "reset-scans.org", 20) MadaraParser(context, MangaParserSource.RESETSCANS, "reset-scans.co", 20)

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("SCANSRAW", "AquaScans.com", "en") @MangaSourceParser("SCANSRAW", "AquaScans.com", "en")
internal class Scansraw(context: MangaLoaderContext) : internal class Scansraw(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.SCANSRAW, "aquascans.com", 10) MadaraParser(context, MangaParserSource.SCANSRAW, "aquascans.com", 10)

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("STKISSMANGABLOG", "1StKissManga.net", "en") @MangaSourceParser("STKISSMANGABLOG", "1StKissManga.net", "en")
internal class StkissMangaBlog(context: MangaLoaderContext) : internal class StkissMangaBlog(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.STKISSMANGABLOG, "1stkissmanga.org", 20) { MadaraParser(context, MangaParserSource.STKISSMANGABLOG, "1stkissmanga.org", 20) {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("STKISSMANGA_COM", "1stKissManga.com", "en") @MangaSourceParser("STKISSMANGA_COM", "1stKissManga.com", "en")
internal class StkissMangaCom(context: MangaLoaderContext) : internal class StkissMangaCom(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.STKISSMANGA_COM, "1stkissmanga.mom", 10) { MadaraParser(context, MangaParserSource.STKISSMANGA_COM, "1stkissmanga.mom", 10) {

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("COCORIP", "Cocorip", "es") @MangaSourceParser("COCORIP", "Cocorip", "es")
internal class Cocorip(context: MangaLoaderContext) : internal class Cocorip(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.COCORIP, "cocorip.net", 16) { MadaraParser(context, MangaParserSource.COCORIP, "cocorip.net", 16) {

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("DAPROB", "Daprob", "es") @MangaSourceParser("DAPROB", "Daprob", "es")
internal class Daprob(context: MangaLoaderContext) : internal class Daprob(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.DAPROB, "daprob.com") { MadaraParser(context, MangaParserSource.DAPROB, "daprob.com") {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("DARKNEBULUS", "Darknebulus", "es") @MangaSourceParser("DARKNEBULUS", "Darknebulus", "es")
internal class Darknebulus(context: MangaLoaderContext) : internal class Darknebulus(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.DARKNEBULUS, "www.darknebulus.com") MadaraParser(context, MangaParserSource.DARKNEBULUS, "www.darknebulus.com")

@ -1,12 +0,0 @@
package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("DRAGONTRANSLATIONORG", "DragonTranslation.org", "es")
internal class DragonTranslationOrg(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.DRAGONTRANSLATIONORG, "dragontranslation.org", 16) {
override val datePattern = "dd/MM/yyyy"
}

@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("EMPERORSCAN", "EmperorScan", "es") @MangaSourceParser("EMPERORSCAN", "EmperorScan", "es")
internal class EmperorScan(context: MangaLoaderContext) : internal class EmperorScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.EMPERORSCAN, "emperorscan.mundoalterno.org") MadaraParser(context, MangaParserSource.EMPERORSCAN, "zonaemperor.com")

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Not dead, changed template") @Broken
@MangaSourceParser("HAREMSCANN", "HaremScann", "es") @MangaSourceParser("HAREMSCANN", "HaremScann", "es")
internal class HaremScann(context: MangaLoaderContext) : internal class HaremScann(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.HAREMSCANN, "haremscann.es") { MadaraParser(context, MangaParserSource.HAREMSCANN, "haremscann.es") {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Not dead, changed template") @Broken
@MangaSourceParser("HOUSEMANGAS", "HouseMangas", "es") @MangaSourceParser("HOUSEMANGAS", "HouseMangas", "es")
internal class HouseMangas(context: MangaLoaderContext) : internal class HouseMangas(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.HOUSEMANGAS, "visormanga.com") MadaraParser(context, MangaParserSource.HOUSEMANGAS, "housemangas.com")

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("INMORALNOFANSUB", "InmoralNoFansub", "es") @MangaSourceParser("INMORALNOFANSUB", "InmoralNoFansub", "es")
internal class InmoralNoFansub(context: MangaLoaderContext) : internal class InmoralNoFansub(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.INMORALNOFANSUB, "inmoralnofansub.xyz") { MadaraParser(context, MangaParserSource.INMORALNOFANSUB, "inmoralnofansub.xyz") {

@ -5,8 +5,8 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("JEAZTWOBLUESCANS", "Lector HUB", "es") @MangaSourceParser("JEAZTWOBLUESCANS", "Marcialhub", "es")
internal class JeazTwoBlueScans(context: MangaLoaderContext) : internal class JeazTwoBlueScans(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.JEAZTWOBLUESCANS, "lectorhub.j5z.xyz") { MadaraParser(context, MangaParserSource.JEAZTWOBLUESCANS, "marcialhub.xyz") {
override val datePattern = "d MMMM, yyyy" override val datePattern = "d MMMM, yyyy"
} }

@ -6,9 +6,9 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Not dead, changed template") @Broken
@MangaSourceParser("LMTOS", "Lmtos", "es") @MangaSourceParser("JOBSIBE", "Jobsibe", "es")
internal class Lmtos(context: MangaLoaderContext) : internal class Jobsibe(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.LMTOS, "lmtos.com") { MadaraParser(context, MangaParserSource.JOBSIBE, "jobsibe.com") {
override val datePattern = "dd/MM" override val datePattern = "dd/MM"
} }

@ -5,9 +5,9 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("KNIGHTNOSCANLATION", "Lector KNS", "es") @MangaSourceParser("KNIGHTNOSCANLATION", "TwoBlueScans", "es")
internal class KnightnoScanlation(context: MangaLoaderContext) : internal class KnightnoScanlation(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.KNIGHTNOSCANLATION, "lectorknight.com") { MadaraParser(context, MangaParserSource.KNIGHTNOSCANLATION, "kns.cookni.net") {
override val listUrl = "sr/" override val listUrl = "sr/"
override val tagPrefix = "generos/" override val tagPrefix = "generos/"
} }

@ -6,9 +6,9 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Not dead, changed template") @Broken // Not dead but changed template
@MangaSourceParser("KOINOBORISCAN", "KoinoboriScan", "es") @MangaSourceParser("KOINOBORISCAN", "KoinoboriScan", "es")
internal class KoinoboriScan(context: MangaLoaderContext) : internal class KoinoboriScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.KOINOBORISCAN, "visorkoi.com") { MadaraParser(context, MangaParserSource.KOINOBORISCAN, "koinoboriscan.com") {
override val postReq = true override val postReq = true
} }

@ -1,12 +1,14 @@
package org.koitharu.kotatsu.parsers.site.madara.es package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken
@MangaSourceParser("LEGENDSCANLATIONS", "LegendScanlations", "es") @MangaSourceParser("LEGENDSCANLATIONS", "LegendScanlations", "es")
internal class LegendScanlations(context: MangaLoaderContext) : internal class LegendScanlations(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.LEGENDSCANLATIONS, "escaneodeleyendas.com", 10) { MadaraParser(context, MangaParserSource.LEGENDSCANLATIONS, "legendscanlations.com", 10) {
override val datePattern = "dd/MM/yyyy" override val datePattern = "dd/MM/yyyy"
} }

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("MANGA_CRAB", "MangaCrab", "es") @MangaSourceParser("MANGA_CRAB", "MangaCrab", "es")
internal class MangaCrab(context: MangaLoaderContext) : internal class MangaCrab(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MANGA_CRAB, "mangacrab.org") { MadaraParser(context, MangaParserSource.MANGA_CRAB, "mangacrab.topmanhuas.org") {
override val datePattern = "dd/MM/yyyy" override val datePattern = "dd/MM/yyyy"
override val tagPrefix = "manga-genero/" override val tagPrefix = "manga-genero/"
override val listUrl = "series/" override val listUrl = "series/"

@ -1,13 +1,15 @@
package org.koitharu.kotatsu.parsers.site.madara.es package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken
@MangaSourceParser("MANTRAZSCAN", "MantrazScan", "es") @MangaSourceParser("MANTRAZSCAN", "MantrazScan", "es")
internal class MantrazScan(context: MangaLoaderContext) : internal class MantrazScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MANTRAZSCAN, "mantrazscan.org") { MadaraParser(context, MangaParserSource.MANTRAZSCAN, "artessupremas.com") {
override val datePattern = "dd/MM/yyyy" override val datePattern = "dd/MM/yyyy"
override val tagPrefix = "generos-de-manga/" override val tagPrefix = "generos-de-manga/"
} }

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("MHSCANS", "MhScans", "es") @MangaSourceParser("MHSCANS", "MhScans", "es")
internal class MhScans(context: MangaLoaderContext) : internal class MhScans(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MHSCANS, "mhscans.mundoalterno.org") { MadaraParser(context, MangaParserSource.MHSCANS, "twobluescans.com") {
override val datePattern = "d 'de' MMMMM 'de' yyyy" override val datePattern = "d 'de' MMMMM 'de' yyyy"
override val listUrl = "series/" override val listUrl = "series/"
} }

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("MMDAOS", "Mmdaos", "es") @MangaSourceParser("MMDAOS", "Mmdaos", "es")
internal class Mmdaos(context: MangaLoaderContext) : internal class Mmdaos(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MMDAOS, "mmdaos.com") MadaraParser(context, MangaParserSource.MMDAOS, "mmdaos.com")

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("MONARCAMANGA", "MonarcaManga", "es") @MangaSourceParser("MONARCAMANGA", "MonarcaManga", "es")
internal class MonarcaManga(context: MangaLoaderContext) : internal class MonarcaManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.MONARCAMANGA, "visormonarca.com") { MadaraParser(context, MangaParserSource.MONARCAMANGA, "visormonarca.com") {

@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("NOBLESSETRANSLATIONS", "NoblesseTranslations", "es") @MangaSourceParser("NOBLESSETRANSLATIONS", "NoblesseTranslations", "es")
internal class NoblesseTranslations(context: MangaLoaderContext) : internal class NoblesseTranslations(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.NOBLESSETRANSLATIONS, "nobledicion.yoveo.xyz") MadaraParser(context, MangaParserSource.NOBLESSETRANSLATIONS, "swordalada.org")

@ -9,8 +9,8 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken @Broken
@MangaSourceParser("RAGNAROKSCAN", "RagnarokScan", "es") @MangaSourceParser("RAGNAROKSCAN", "RagnarokScan", "es")
internal class RagnarokScan(context: MangaLoaderContext) : internal class RagnarokScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.RAGNAROKSCAN, "ragnarokscan.com") { MadaraParser(context, MangaParserSource.RAGNAROKSCAN, "ragnarokscan.com") {
override val stylePage = "" override val stylePage = ""
override val listUrl = "series/" override val listUrl = "series/"
override val tagPrefix = "genero/" override val tagPrefix = "genero/"
} }

@ -7,4 +7,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("RAGNAROKSCANLATION", "RagnarokScanlation", "es") @MangaSourceParser("RAGNAROKSCANLATION", "RagnarokScanlation", "es")
internal class RagnarokScanlation(context: MangaLoaderContext) : internal class RagnarokScanlation(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.RAGNAROKSCANLATION, "ragnarokscanlation.org") MadaraParser(context, MangaParserSource.RAGNAROKSCANLATION, "ragnarokscanlation.net")

@ -8,7 +8,7 @@ import java.util.*
@MangaSourceParser("RICHTOSCAN", "RichtoScan", "es") @MangaSourceParser("RICHTOSCAN", "RichtoScan", "es")
internal class RichtoScan(context: MangaLoaderContext) : internal class RichtoScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.RICHTOSCAN, "r1.richtoon.top") { MadaraParser(context, MangaParserSource.RICHTOSCAN, "richtoscan.com") {
override val tagPrefix = "manga-generos/" override val tagPrefix = "manga-generos/"
override val sourceLocale: Locale = Locale.ENGLISH override val sourceLocale: Locale = Locale.ENGLISH
} }

@ -7,6 +7,6 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("SAMURAISCAN", "SamuraiScan", "es") @MangaSourceParser("SAMURAISCAN", "SamuraiScan", "es")
internal class SamuraiScan(context: MangaLoaderContext) : internal class SamuraiScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.SAMURAISCAN, "samuraiscan.com", 10) { MadaraParser(context, MangaParserSource.SAMURAISCAN, "latan.visorsmr.com", 10) {
override val listUrl = "read/" override val listUrl = "read/"
} }

@ -7,6 +7,6 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("TAURUSMANGA", "TaurusManga", "es") @MangaSourceParser("TAURUSMANGA", "TaurusManga", "es")
internal class TaurusManga(context: MangaLoaderContext) : internal class TaurusManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.TAURUSMANGA, "taurus.topmanhuas.org") { MadaraParser(context, MangaParserSource.TAURUSMANGA, "taurusmanga.com") {
override val datePattern = "dd/MM/yyyy" override val datePattern = "dd/MM/yyyy"
} }

@ -4,11 +4,9 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("TECNOPROJECTS", "TecnoProjects", "es") @MangaSourceParser("TECNOPROJECTS", "TecnoProjects", "es")
internal class TecnoProjects(context: MangaLoaderContext) : internal class TecnoProjects(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.TECNOPROJECTS, "tecnoprojects.xyz") { MadaraParser(context, MangaParserSource.TECNOPROJECTS, "tecnoprojects.com") {
override val datePattern = "dd 'de' MMMM 'de' yyyy" override val datePattern = "dd 'de' MMMM 'de' yyyy"
} }

@ -1,6 +1,5 @@
package org.koitharu.kotatsu.parsers.site.madara.es package org.koitharu.kotatsu.parsers.site.madara.es
import org.koitharu.kotatsu.parsers.Broken
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
@ -8,7 +7,6 @@ import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
@Broken
@MangaSourceParser("TMOMANGA", "TmoManga", "es") @MangaSourceParser("TMOMANGA", "TmoManga", "es")
internal class TmoManga(context: MangaLoaderContext) : internal class TmoManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.TMOMANGA, "tmomanga.com") { MadaraParser(context, MangaParserSource.TMOMANGA, "tmomanga.com") {

@ -4,9 +4,7 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken
@Broken
@MangaSourceParser("ZEVEP", "Zevep", "es") @MangaSourceParser("ZEVEP", "Zevep", "es")
internal class Zevep(context: MangaLoaderContext) : internal class Zevep(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.ZEVEP, "zevep.com", 16) MadaraParser(context, MangaParserSource.ZEVEP, "zevep.com", 16)

@ -8,4 +8,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("SCANHENTAIMENU", "ScanHentai.Menu", "fr", ContentType.HENTAI) @MangaSourceParser("SCANHENTAIMENU", "ScanHentai.Menu", "fr", ContentType.HENTAI)
internal class ScanHentaiMenu(context: MangaLoaderContext) : internal class ScanHentaiMenu(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.SCANHENTAIMENU, "x-manga.org") MadaraParser(context, MangaParserSource.SCANHENTAIMENU, "x-manga.net")

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Not dead, changed template") @Broken // Not dead, changed template
@MangaSourceParser("CERISE_SCANS", "CeriseScans", "pt") @MangaSourceParser("CERISE_SCANS", "CeriseScans", "pt")
internal class CeriseScans(context: MangaLoaderContext) : internal class CeriseScans(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.CERISE_SCANS, "cerise.leitorweb.com") { MadaraParser(context, MangaParserSource.CERISE_SCANS, "cerise.leitorweb.com") {

@ -8,4 +8,4 @@ import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("CVNSCAN", "CvnScan", "pt", ContentType.HENTAI) @MangaSourceParser("CVNSCAN", "CvnScan", "pt", ContentType.HENTAI)
internal class CvnScan(context: MangaLoaderContext) : internal class CvnScan(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.CVNSCAN, "covendasbruxonas.com") MadaraParser(context, MangaParserSource.CVNSCAN, "cvnscan.com")

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed
@MangaSourceParser("GALINHASAMURAI", "GalinhaSamurai", "pt") @MangaSourceParser("GALINHASAMURAI", "GalinhaSamurai", "pt")
internal class GalinhaSamurai(context: MangaLoaderContext) : internal class GalinhaSamurai(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.GALINHASAMURAI, "galinhasamurai.com") { MadaraParser(context, MangaParserSource.GALINHASAMURAI, "galinhasamurai.com") {

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken("Original site closed") @Broken // Website closed and the domain has been sold
@MangaSourceParser("GOOFFANSUB", "GoofFansub", "pt", ContentType.HENTAI) @MangaSourceParser("GOOFFANSUB", "GoofFansub", "pt", ContentType.HENTAI)
internal class GoofFansub(context: MangaLoaderContext) : internal class GoofFansub(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.GOOFFANSUB, "gooffansub.com") { MadaraParser(context, MangaParserSource.GOOFFANSUB, "gooffansub.com") {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Not dead, changed template") @Broken // Not dead, changed template
@MangaSourceParser("SINENSISSCANS", "SinensisScans", "pt") @MangaSourceParser("SINENSISSCANS", "SinensisScans", "pt")
internal class SinensisScans(context: MangaLoaderContext) : internal class SinensisScans(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.SINENSISSCANS, "sinensis.leitorweb.com") { MadaraParser(context, MangaParserSource.SINENSISSCANS, "sinensis.leitorweb.com") {

@ -7,7 +7,7 @@ import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @KINGS_MANGA") @Broken // Redirect to @KINGS_MANGA
@MangaSourceParser("NEKOPOST", "NekoPost", "th", ContentType.HENTAI) @MangaSourceParser("NEKOPOST", "NekoPost", "th", ContentType.HENTAI)
internal class NekoPost(context: MangaLoaderContext) : internal class NekoPost(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.NEKOPOST, "www.superdoujin.org") { MadaraParser(context, MangaParserSource.NEKOPOST, "www.superdoujin.org") {

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @WEBTOONHATTI") @Broken // Redirect to @WEBTOONHATTI
@MangaSourceParser("CLOVERMANGA", "CloverManga", "tr") @MangaSourceParser("CLOVERMANGA", "CloverManga", "tr")
internal class CloverManga(context: MangaLoaderContext) : internal class CloverManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.CLOVERMANGA, "webtoonhatti.me", 20) MadaraParser(context, MangaParserSource.CLOVERMANGA, "webtoonhatti.me", 20)

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @GRIMELEK") @Broken // Redirect to @GRIMELEK
@MangaSourceParser("GHOSTFANSUB", "GhostFansub", "tr") @MangaSourceParser("GHOSTFANSUB", "GhostFansub", "tr")
internal class GhostFansub(context: MangaLoaderContext) : internal class GhostFansub(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.GHOSTFANSUB, "ghostfansub.co", 18) MadaraParser(context, MangaParserSource.GHOSTFANSUB, "ghostfansub.co", 18)

@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@Broken("Redirect to @MANGAGEZGINI") @Broken // Redirect to @MANGAGEZGINI
@MangaSourceParser("GLORYMANGA", "GloryManga", "tr") @MangaSourceParser("GLORYMANGA", "GloryManga", "tr")
internal class GloryManga(context: MangaLoaderContext) : internal class GloryManga(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.GLORYMANGA, "mangagezgini.site", 18) { MadaraParser(context, MangaParserSource.GLORYMANGA, "mangagezgini.site", 18) {

@ -16,7 +16,7 @@ import org.koitharu.kotatsu.parsers.util.urlEncoded
@MangaSourceParser("HENTAIVNPLUS", "HentaiVN.plus", "vi", ContentType.HENTAI) @MangaSourceParser("HENTAIVNPLUS", "HentaiVN.plus", "vi", ContentType.HENTAI)
internal class HentaiVnPlus(context: MangaLoaderContext) : internal class HentaiVnPlus(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.HENTAIVNPLUS, "hentaivn.party", 24) { MadaraParser(context, MangaParserSource.HENTAIVNPLUS, "hentaivn.cx", 24) {
override val listUrl = "truyen-hentai/" override val listUrl = "truyen-hentai/"
override val tagPrefix = "the-loai/" override val tagPrefix = "the-loai/"
override val datePattern = "dd/MM/yyyy" override val datePattern = "dd/MM/yyyy"

@ -9,6 +9,7 @@ import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.util.*
import org.koitharu.kotatsu.parsers.Broken import org.koitharu.kotatsu.parsers.Broken
@Broken @Broken
@ -25,7 +26,7 @@ internal class HentaiZ(context: MangaLoaderContext) :
val href = doc.selectFirst("head meta[property='og:url']")?.attr("content")?.toRelativeUrl(domain) ?: manga.url val href = doc.selectFirst("head meta[property='og:url']")?.attr("content")?.toRelativeUrl(domain) ?: manga.url
val testCheckAsync = doc.select(selectTestAsync) val testCheckAsync = doc.select(selectTestAsync)
val chaptersDeferred = if (testCheckAsync.isEmpty()) { val chaptersDeferred = if (testCheckAsync.isNullOrEmpty()) {
async { loadChapters(href, doc) } async { loadChapters(href, doc) }
} else { } else {
async { getChapters(manga, doc) } async { getChapters(manga, doc) }

@ -11,7 +11,7 @@ import org.koitharu.kotatsu.parsers.util.*
@MangaSourceParser("QUAANHDAOCUTEO", "Quả Anh Đào Cuteo", "vi", ContentType.HENTAI) @MangaSourceParser("QUAANHDAOCUTEO", "Quả Anh Đào Cuteo", "vi", ContentType.HENTAI)
internal class Quaanhdaocuteo(context: MangaLoaderContext) : internal class Quaanhdaocuteo(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.QUAANHDAOCUTEO, "qadcuteo.cc") { MadaraParser(context, MangaParserSource.QUAANHDAOCUTEO, "qadcuteo.org") {
override val datePattern = "dd/MM/yyyy" override val datePattern = "dd/MM/yyyy"
override val selectPage = "p img" override val selectPage = "p img"

@ -1,14 +0,0 @@
package org.koitharu.kotatsu.parsers.site.madara.vi
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.site.madara.MadaraParser
@MangaSourceParser("TRUYENTRANHFULL", "Truyện Tranh Full", "vi")
internal class TruyenTranhFull(context: MangaLoaderContext) :
MadaraParser(context, MangaParserSource.TRUYENTRANHFULL, "truyentranhfull.net", 20) {
override val listUrl = "truyen-tranh/"
override val tagPrefix = "the-loai/"
override val datePattern = "dd/MM/yyyy"
}

@ -7,7 +7,6 @@ import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.core.PagedMangaParser import org.koitharu.kotatsu.parsers.core.PagedMangaParser
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.DateFormat import java.text.DateFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -142,7 +141,7 @@ internal abstract class MadthemeParser(
private suspend fun fetchAvailableTags(): Set<MangaTag> { private suspend fun fetchAvailableTags(): Set<MangaTag> {
val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml() val doc = webClient.httpGet("https://$domain/$listUrl").parseHtml()
return doc.select("div.genres .checkbox").mapNotNullToSet { checkbox -> return doc.select("div.genres .checkbox").mapNotNullToSet { checkbox ->
val key = checkbox.selectFirstOrThrow("input").attr("value") val key = checkbox.selectFirstOrThrow("input").attr("value") ?: return@mapNotNullToSet null
val name = checkbox.selectFirst("span.radio__label")?.text() ?: key val name = checkbox.selectFirst("span.radio__label")?.text() ?: key
MangaTag( MangaTag(
key = key, key = key,
@ -191,7 +190,7 @@ internal abstract class MadthemeParser(
altTitles = setOfNotNull(alt.nullIfEmpty()), altTitles = setOfNotNull(alt.nullIfEmpty()),
state = state, state = state,
chapters = chaptersDeferred.await(), chapters = chaptersDeferred.await(),
contentRating = if (nsfw || manga.contentRating == ContentRating.ADULT) { contentRating = if (nsfw || manga.isNsfw) {
ContentRating.ADULT ContentRating.ADULT
} else { } else {
ContentRating.SAFE ContentRating.SAFE

@ -271,7 +271,7 @@ internal abstract class MangaboxParser(
val fullUrl = chapter.url.toAbsoluteUrl(domain) val fullUrl = chapter.url.toAbsoluteUrl(domain)
val doc = webClient.httpGet(fullUrl).parseHtml() val doc = webClient.httpGet(fullUrl).parseHtml()
if (doc.select(selectPage).isEmpty()) { if (doc.select(selectPage).isNullOrEmpty()) {
val fullUrl2 = chapter.url.toAbsoluteUrl(domain).replace(domain, otherDomain) val fullUrl2 = chapter.url.toAbsoluteUrl(domain).replace(domain, otherDomain)
val doc2 = webClient.httpGet(fullUrl2).parseHtml() val doc2 = webClient.httpGet(fullUrl2).parseHtml()

@ -132,7 +132,7 @@ internal class Mangakakalot(context: MangaLoaderContext) : MangaboxParser(contex
val doc = webClient.httpGet(titleSearchUrl ?: url).parseHtml() val doc = webClient.httpGet(titleSearchUrl ?: url).parseHtml()
return doc.select("div.list-comic-item-wrap").ifEmpty { return doc.select("div.list-truyen-item-wrap").ifEmpty {
doc.select("div.story_item") doc.select("div.story_item")
}.map { div -> }.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")

@ -2,7 +2,6 @@ package org.koitharu.kotatsu.parsers.site.mangabox.en
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import org.koitharu.kotatsu.parsers.Broken
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.config.ConfigKey import org.koitharu.kotatsu.parsers.config.ConfigKey
@ -18,7 +17,6 @@ import org.koitharu.kotatsu.parsers.site.mangabox.MangaboxParser
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.util.* import java.util.*
@Broken("Connection refused")
@MangaSourceParser("MANGAKAKALOTTV", "Mangakakalot.tv", "en") @MangaSourceParser("MANGAKAKALOTTV", "Mangakakalot.tv", "en")
internal class MangakakalotTv(context: MangaLoaderContext) : internal class MangakakalotTv(context: MangaLoaderContext) :
MangaboxParser(context, MangaParserSource.MANGAKAKALOTTV) { MangaboxParser(context, MangaParserSource.MANGAKAKALOTTV) {
@ -121,7 +119,7 @@ internal class MangakakalotTv(context: MangaLoaderContext) :
val doc = webClient.httpGet(titleSearchUrl ?: url).parseHtml() val doc = webClient.httpGet(titleSearchUrl ?: url).parseHtml()
return doc.select("div.list-comic-item-wrap").ifEmpty { return doc.select("div.list-truyen-item-wrap").ifEmpty {
doc.select("div.story_item") doc.select("div.story_item")
}.map { div -> }.map { div ->
val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href") val href = div.selectFirstOrThrow("a").attrAsRelativeUrl("href")
@ -130,7 +128,7 @@ internal class MangakakalotTv(context: MangaLoaderContext) :
url = href, url = href,
publicUrl = href.toAbsoluteUrl(div.host ?: domain), publicUrl = href.toAbsoluteUrl(div.host ?: domain),
coverUrl = div.selectFirst("img")?.src(), coverUrl = div.selectFirst("img")?.src(),
title = div.selectFirstOrThrow("h3").text(), title = div.selectFirstOrThrow("h3").text().orEmpty(),
altTitles = emptySet(), altTitles = emptySet(),
rating = RATING_UNKNOWN, rating = RATING_UNKNOWN,
tags = emptySet(), tags = emptySet(),

@ -4,7 +4,6 @@ import org.jsoup.nodes.Document
import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaSourceParser import org.koitharu.kotatsu.parsers.MangaSourceParser
import org.koitharu.kotatsu.parsers.model.* import org.koitharu.kotatsu.parsers.model.*
import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser import org.koitharu.kotatsu.parsers.site.mangareader.MangaReaderParser
import org.koitharu.kotatsu.parsers.util.* import org.koitharu.kotatsu.parsers.util.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -112,7 +111,7 @@ internal class Normoyun(context: MangaLoaderContext) :
description = docs.selectFirst("span.desc")?.html(), description = docs.selectFirst("span.desc")?.html(),
state = mangaState, state = mangaState,
authors = setOfNotNull(author), authors = setOfNotNull(author),
contentRating = if (manga.contentRating == ContentRating.ADULT || nsfw) { contentRating = if (manga.isNsfw || nsfw) {
ContentRating.ADULT ContentRating.ADULT
} else { } else {
ContentRating.SAFE ContentRating.SAFE

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save